blob: 4496fd049a9452a0e63ab53a37477ffd36b45b1a [file] [log] [blame]
Brian Swetland9c4c0752009-01-25 16:23:50 -08001/*
2 * Copyright (c) 2009, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <debug.h>
30#include <string.h>
31#include <stdlib.h>
Deepa Dinamani0bf2f442012-10-19 11:41:06 -070032#include <platform.h>
Brian Swetland9c4c0752009-01-25 16:23:50 -080033#include <kernel/thread.h>
34#include <kernel/event.h>
35#include <dev/udc.h>
36
Shashank Mittal6a5609f2011-08-04 15:51:59 -070037#define MAX_RSP_SIZE 64
Hanumant Singh108cdc62012-12-11 16:48:49 -080038#define MAX_USBFS_BULK_SIZE (32 * 1024)
Shashank Mittal6a5609f2011-08-04 15:51:59 -070039
Brian Swetland9c4c0752009-01-25 16:23:50 -080040void boot_linux(void *bootimg, unsigned sz);
41
42/* todo: give lk strtoul and nuke this */
43static unsigned hex2unsigned(const char *x)
44{
45 unsigned n = 0;
46
47 while(*x) {
48 switch(*x) {
49 case '0': case '1': case '2': case '3': case '4':
50 case '5': case '6': case '7': case '8': case '9':
51 n = (n << 4) | (*x - '0');
52 break;
53 case 'a': case 'b': case 'c':
54 case 'd': case 'e': case 'f':
55 n = (n << 4) | (*x - 'a' + 10);
56 break;
57 case 'A': case 'B': case 'C':
58 case 'D': case 'E': case 'F':
59 n = (n << 4) | (*x - 'A' + 10);
60 break;
61 default:
62 return n;
63 }
64 x++;
65 }
66
67 return n;
68}
69
70struct fastboot_cmd {
71 struct fastboot_cmd *next;
72 const char *prefix;
73 unsigned prefix_len;
74 void (*handle)(const char *arg, void *data, unsigned sz);
75};
76
77struct fastboot_var {
78 struct fastboot_var *next;
79 const char *name;
80 const char *value;
81};
82
83static struct fastboot_cmd *cmdlist;
84
85void fastboot_register(const char *prefix,
86 void (*handle)(const char *arg, void *data, unsigned sz))
87{
88 struct fastboot_cmd *cmd;
89 cmd = malloc(sizeof(*cmd));
90 if (cmd) {
91 cmd->prefix = prefix;
92 cmd->prefix_len = strlen(prefix);
93 cmd->handle = handle;
94 cmd->next = cmdlist;
95 cmdlist = cmd;
96 }
97}
98
99static struct fastboot_var *varlist;
100
101void fastboot_publish(const char *name, const char *value)
102{
103 struct fastboot_var *var;
104 var = malloc(sizeof(*var));
105 if (var) {
106 var->name = name;
107 var->value = value;
108 var->next = varlist;
109 varlist = var;
110 }
111}
112
113
114static event_t usb_online;
115static event_t txn_done;
116static unsigned char buffer[4096];
117static struct udc_endpoint *in, *out;
118static struct udc_request *req;
119int txn_status;
120
121static void *download_base;
122static unsigned download_max;
123static unsigned download_size;
124
125#define STATE_OFFLINE 0
126#define STATE_COMMAND 1
127#define STATE_COMPLETE 2
128#define STATE_ERROR 3
129
130static unsigned fastboot_state = STATE_OFFLINE;
131
132static void req_complete(struct udc_request *req, unsigned actual, int status)
133{
134 txn_status = status;
135 req->length = actual;
Hanumant Singh108cdc62012-12-11 16:48:49 -0800136
Brian Swetland9c4c0752009-01-25 16:23:50 -0800137 event_signal(&txn_done, 0);
138}
139
140static int usb_read(void *_buf, unsigned len)
141{
142 int r;
143 unsigned xfer;
144 unsigned char *buf = _buf;
145 int count = 0;
146
147 if (fastboot_state == STATE_ERROR)
148 goto oops;
149
150 while (len > 0) {
Shashank Mittal1cc65b02011-12-20 15:30:17 -0800151 xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700152 req->buf = PA((addr_t)buf);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800153 req->length = xfer;
154 req->complete = req_complete;
155 r = udc_request_queue(out, req);
156 if (r < 0) {
157 dprintf(INFO, "usb_read() queue failed\n");
158 goto oops;
159 }
160 event_wait(&txn_done);
161
162 if (txn_status < 0) {
163 dprintf(INFO, "usb_read() transaction failed\n");
164 goto oops;
165 }
166
167 count += req->length;
168 buf += req->length;
169 len -= req->length;
170
171 /* short transfer? */
172 if (req->length != xfer) break;
173 }
Hanumant Singh108cdc62012-12-11 16:48:49 -0800174 /*
175 * Force reload of buffer from memory
176 * since transaction is complete now.
177 */
178 arch_invalidate_cache_range(_buf, count);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800179 return count;
180
181oops:
182 fastboot_state = STATE_ERROR;
183 return -1;
184}
185
186static int usb_write(void *buf, unsigned len)
187{
188 int r;
189
190 if (fastboot_state == STATE_ERROR)
191 goto oops;
192
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700193 req->buf = PA((addr_t)buf);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800194 req->length = len;
195 req->complete = req_complete;
196 r = udc_request_queue(in, req);
197 if (r < 0) {
198 dprintf(INFO, "usb_write() queue failed\n");
199 goto oops;
200 }
201 event_wait(&txn_done);
202 if (txn_status < 0) {
203 dprintf(INFO, "usb_write() transaction failed\n");
204 goto oops;
205 }
206 return req->length;
207
208oops:
209 fastboot_state = STATE_ERROR;
210 return -1;
211}
212
213void fastboot_ack(const char *code, const char *reason)
214{
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700215 char response[MAX_RSP_SIZE];
Brian Swetland9c4c0752009-01-25 16:23:50 -0800216
217 if (fastboot_state != STATE_COMMAND)
218 return;
219
220 if (reason == 0)
221 reason = "";
222
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700223 snprintf(response, MAX_RSP_SIZE, "%s%s", code, reason);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800224 fastboot_state = STATE_COMPLETE;
225
226 usb_write(response, strlen(response));
227
228}
229
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700230void fastboot_info(const char *reason)
231{
232 char response[MAX_RSP_SIZE];
233
234 if (fastboot_state != STATE_COMMAND)
235 return;
236
237 if (reason == 0)
238 return;
239
240 snprintf(response, MAX_RSP_SIZE, "INFO%s", reason);
241
242 usb_write(response, strlen(response));
243}
244
Brian Swetland9c4c0752009-01-25 16:23:50 -0800245void fastboot_fail(const char *reason)
246{
247 fastboot_ack("FAIL", reason);
248}
249
250void fastboot_okay(const char *info)
251{
252 fastboot_ack("OKAY", info);
253}
254
255static void cmd_getvar(const char *arg, void *data, unsigned sz)
256{
257 struct fastboot_var *var;
258
259 for (var = varlist; var; var = var->next) {
260 if (!strcmp(var->name, arg)) {
261 fastboot_okay(var->value);
262 return;
263 }
264 }
265 fastboot_okay("");
266}
267
268static void cmd_download(const char *arg, void *data, unsigned sz)
269{
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700270 char response[MAX_RSP_SIZE];
Brian Swetland9c4c0752009-01-25 16:23:50 -0800271 unsigned len = hex2unsigned(arg);
272 int r;
273
274 download_size = 0;
275 if (len > download_max) {
276 fastboot_fail("data too large");
277 return;
278 }
279
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700280 snprintf(response, MAX_RSP_SIZE, "DATA%08x", len);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800281 if (usb_write(response, strlen(response)) < 0)
282 return;
283
284 r = usb_read(download_base, len);
Greg Griscod6250552011-06-29 14:40:23 -0700285 if ((r < 0) || ((unsigned) r != len)) {
Brian Swetland9c4c0752009-01-25 16:23:50 -0800286 fastboot_state = STATE_ERROR;
287 return;
288 }
289 download_size = len;
290 fastboot_okay("");
291}
292
293static void fastboot_command_loop(void)
294{
295 struct fastboot_cmd *cmd;
296 int r;
297 dprintf(INFO,"fastboot: processing commands\n");
298
299again:
300 while (fastboot_state != STATE_ERROR) {
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700301 r = usb_read(buffer, MAX_RSP_SIZE);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800302 if (r < 0) break;
303 buffer[r] = 0;
304 dprintf(INFO,"fastboot: %s\n", buffer);
305
306 for (cmd = cmdlist; cmd; cmd = cmd->next) {
307 if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
308 continue;
309 fastboot_state = STATE_COMMAND;
310 cmd->handle((const char*) buffer + cmd->prefix_len,
311 (void*) download_base, download_size);
312 if (fastboot_state == STATE_COMMAND)
313 fastboot_fail("unknown reason");
314 goto again;
315 }
316
317 fastboot_fail("unknown command");
318
319 }
320 fastboot_state = STATE_OFFLINE;
321 dprintf(INFO,"fastboot: oops!\n");
322}
323
324static int fastboot_handler(void *arg)
325{
326 for (;;) {
327 event_wait(&usb_online);
328 fastboot_command_loop();
329 }
330 return 0;
331}
332
333static void fastboot_notify(struct udc_gadget *gadget, unsigned event)
334{
335 if (event == UDC_EVENT_ONLINE) {
336 event_signal(&usb_online, 0);
337 }
338}
339
340static struct udc_endpoint *fastboot_endpoints[2];
341
342static struct udc_gadget fastboot_gadget = {
343 .notify = fastboot_notify,
344 .ifc_class = 0xff,
345 .ifc_subclass = 0x42,
346 .ifc_protocol = 0x03,
347 .ifc_endpoints = 2,
348 .ifc_string = "fastboot",
349 .ept = fastboot_endpoints,
350};
351
352int fastboot_init(void *base, unsigned size)
353{
354 thread_t *thr;
355 dprintf(INFO, "fastboot_init()\n");
356
357 download_base = base;
358 download_max = size;
359
360 event_init(&usb_online, 0, EVENT_FLAG_AUTOUNSIGNAL);
361 event_init(&txn_done, 0, EVENT_FLAG_AUTOUNSIGNAL);
362
363 in = udc_endpoint_alloc(UDC_TYPE_BULK_IN, 512);
364 if (!in)
365 goto fail_alloc_in;
366 out = udc_endpoint_alloc(UDC_TYPE_BULK_OUT, 512);
367 if (!out)
368 goto fail_alloc_out;
369
370 fastboot_endpoints[0] = in;
371 fastboot_endpoints[1] = out;
372
373 req = udc_request_alloc();
374 if (!req)
375 goto fail_alloc_req;
376
377 if (udc_register_gadget(&fastboot_gadget))
378 goto fail_udc_register;
379
380 fastboot_register("getvar:", cmd_getvar);
381 fastboot_register("download:", cmd_download);
382 fastboot_publish("version", "0.5");
383
384 thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);
neetid32ba8472011-12-07 16:34:06 -0800385 if (!thr)
386 {
387 goto fail_alloc_in;
388 }
Brian Swetland9c4c0752009-01-25 16:23:50 -0800389 thread_resume(thr);
390 return 0;
391
392fail_udc_register:
393 udc_request_free(req);
394fail_alloc_req:
395 udc_endpoint_free(out);
396fail_alloc_out:
397 udc_endpoint_free(in);
398fail_alloc_in:
399 return -1;
400}