blob: 0b3d16611db57da8e9510719ca027b18b8ec569b [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>
32#include <kernel/thread.h>
33#include <kernel/event.h>
34#include <dev/udc.h>
35
Shashank Mittal6a5609f2011-08-04 15:51:59 -070036#define MAX_RSP_SIZE 64
Shashank Mittal1cc65b02011-12-20 15:30:17 -080037#define MAX_USBFS_BULK_SIZE (16 * 1024)
Shashank Mittal6a5609f2011-08-04 15:51:59 -070038
Brian Swetland9c4c0752009-01-25 16:23:50 -080039void boot_linux(void *bootimg, unsigned sz);
40
41/* todo: give lk strtoul and nuke this */
42static unsigned hex2unsigned(const char *x)
43{
44 unsigned n = 0;
45
46 while(*x) {
47 switch(*x) {
48 case '0': case '1': case '2': case '3': case '4':
49 case '5': case '6': case '7': case '8': case '9':
50 n = (n << 4) | (*x - '0');
51 break;
52 case 'a': case 'b': case 'c':
53 case 'd': case 'e': case 'f':
54 n = (n << 4) | (*x - 'a' + 10);
55 break;
56 case 'A': case 'B': case 'C':
57 case 'D': case 'E': case 'F':
58 n = (n << 4) | (*x - 'A' + 10);
59 break;
60 default:
61 return n;
62 }
63 x++;
64 }
65
66 return n;
67}
68
69struct fastboot_cmd {
70 struct fastboot_cmd *next;
71 const char *prefix;
72 unsigned prefix_len;
73 void (*handle)(const char *arg, void *data, unsigned sz);
74};
75
76struct fastboot_var {
77 struct fastboot_var *next;
78 const char *name;
79 const char *value;
80};
81
82static struct fastboot_cmd *cmdlist;
83
84void fastboot_register(const char *prefix,
85 void (*handle)(const char *arg, void *data, unsigned sz))
86{
87 struct fastboot_cmd *cmd;
88 cmd = malloc(sizeof(*cmd));
89 if (cmd) {
90 cmd->prefix = prefix;
91 cmd->prefix_len = strlen(prefix);
92 cmd->handle = handle;
93 cmd->next = cmdlist;
94 cmdlist = cmd;
95 }
96}
97
98static struct fastboot_var *varlist;
99
100void fastboot_publish(const char *name, const char *value)
101{
102 struct fastboot_var *var;
103 var = malloc(sizeof(*var));
104 if (var) {
105 var->name = name;
106 var->value = value;
107 var->next = varlist;
108 varlist = var;
109 }
110}
111
112
113static event_t usb_online;
114static event_t txn_done;
115static unsigned char buffer[4096];
116static struct udc_endpoint *in, *out;
117static struct udc_request *req;
118int txn_status;
119
120static void *download_base;
121static unsigned download_max;
122static unsigned download_size;
123
124#define STATE_OFFLINE 0
125#define STATE_COMMAND 1
126#define STATE_COMPLETE 2
127#define STATE_ERROR 3
128
129static unsigned fastboot_state = STATE_OFFLINE;
130
131static void req_complete(struct udc_request *req, unsigned actual, int status)
132{
133 txn_status = status;
134 req->length = actual;
135 event_signal(&txn_done, 0);
136}
137
138static int usb_read(void *_buf, unsigned len)
139{
140 int r;
141 unsigned xfer;
142 unsigned char *buf = _buf;
143 int count = 0;
144
145 if (fastboot_state == STATE_ERROR)
146 goto oops;
147
148 while (len > 0) {
Shashank Mittal1cc65b02011-12-20 15:30:17 -0800149 xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
Brian Swetland9c4c0752009-01-25 16:23:50 -0800150 req->buf = buf;
151 req->length = xfer;
152 req->complete = req_complete;
153 r = udc_request_queue(out, req);
154 if (r < 0) {
155 dprintf(INFO, "usb_read() queue failed\n");
156 goto oops;
157 }
158 event_wait(&txn_done);
159
160 if (txn_status < 0) {
161 dprintf(INFO, "usb_read() transaction failed\n");
162 goto oops;
163 }
164
165 count += req->length;
166 buf += req->length;
167 len -= req->length;
168
169 /* short transfer? */
170 if (req->length != xfer) break;
171 }
172
173 return count;
174
175oops:
176 fastboot_state = STATE_ERROR;
177 return -1;
178}
179
180static int usb_write(void *buf, unsigned len)
181{
182 int r;
183
184 if (fastboot_state == STATE_ERROR)
185 goto oops;
186
187 req->buf = buf;
188 req->length = len;
189 req->complete = req_complete;
190 r = udc_request_queue(in, req);
191 if (r < 0) {
192 dprintf(INFO, "usb_write() queue failed\n");
193 goto oops;
194 }
195 event_wait(&txn_done);
196 if (txn_status < 0) {
197 dprintf(INFO, "usb_write() transaction failed\n");
198 goto oops;
199 }
200 return req->length;
201
202oops:
203 fastboot_state = STATE_ERROR;
204 return -1;
205}
206
207void fastboot_ack(const char *code, const char *reason)
208{
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700209 char response[MAX_RSP_SIZE];
Brian Swetland9c4c0752009-01-25 16:23:50 -0800210
211 if (fastboot_state != STATE_COMMAND)
212 return;
213
214 if (reason == 0)
215 reason = "";
216
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700217 snprintf(response, MAX_RSP_SIZE, "%s%s", code, reason);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800218 fastboot_state = STATE_COMPLETE;
219
220 usb_write(response, strlen(response));
221
222}
223
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700224void fastboot_info(const char *reason)
225{
226 char response[MAX_RSP_SIZE];
227
228 if (fastboot_state != STATE_COMMAND)
229 return;
230
231 if (reason == 0)
232 return;
233
234 snprintf(response, MAX_RSP_SIZE, "INFO%s", reason);
235
236 usb_write(response, strlen(response));
237}
238
Brian Swetland9c4c0752009-01-25 16:23:50 -0800239void fastboot_fail(const char *reason)
240{
241 fastboot_ack("FAIL", reason);
242}
243
244void fastboot_okay(const char *info)
245{
246 fastboot_ack("OKAY", info);
247}
248
249static void cmd_getvar(const char *arg, void *data, unsigned sz)
250{
251 struct fastboot_var *var;
252
253 for (var = varlist; var; var = var->next) {
254 if (!strcmp(var->name, arg)) {
255 fastboot_okay(var->value);
256 return;
257 }
258 }
259 fastboot_okay("");
260}
261
262static void cmd_download(const char *arg, void *data, unsigned sz)
263{
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700264 char response[MAX_RSP_SIZE];
Brian Swetland9c4c0752009-01-25 16:23:50 -0800265 unsigned len = hex2unsigned(arg);
266 int r;
267
268 download_size = 0;
269 if (len > download_max) {
270 fastboot_fail("data too large");
271 return;
272 }
273
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700274 snprintf(response, MAX_RSP_SIZE, "DATA%08x", len);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800275 if (usb_write(response, strlen(response)) < 0)
276 return;
277
278 r = usb_read(download_base, len);
Greg Griscod6250552011-06-29 14:40:23 -0700279 if ((r < 0) || ((unsigned) r != len)) {
Brian Swetland9c4c0752009-01-25 16:23:50 -0800280 fastboot_state = STATE_ERROR;
281 return;
282 }
283 download_size = len;
284 fastboot_okay("");
285}
286
287static void fastboot_command_loop(void)
288{
289 struct fastboot_cmd *cmd;
290 int r;
291 dprintf(INFO,"fastboot: processing commands\n");
292
293again:
294 while (fastboot_state != STATE_ERROR) {
Shashank Mittal6a5609f2011-08-04 15:51:59 -0700295 r = usb_read(buffer, MAX_RSP_SIZE);
Brian Swetland9c4c0752009-01-25 16:23:50 -0800296 if (r < 0) break;
297 buffer[r] = 0;
298 dprintf(INFO,"fastboot: %s\n", buffer);
299
300 for (cmd = cmdlist; cmd; cmd = cmd->next) {
301 if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
302 continue;
303 fastboot_state = STATE_COMMAND;
304 cmd->handle((const char*) buffer + cmd->prefix_len,
305 (void*) download_base, download_size);
306 if (fastboot_state == STATE_COMMAND)
307 fastboot_fail("unknown reason");
308 goto again;
309 }
310
311 fastboot_fail("unknown command");
312
313 }
314 fastboot_state = STATE_OFFLINE;
315 dprintf(INFO,"fastboot: oops!\n");
316}
317
318static int fastboot_handler(void *arg)
319{
320 for (;;) {
321 event_wait(&usb_online);
322 fastboot_command_loop();
323 }
324 return 0;
325}
326
327static void fastboot_notify(struct udc_gadget *gadget, unsigned event)
328{
329 if (event == UDC_EVENT_ONLINE) {
330 event_signal(&usb_online, 0);
331 }
332}
333
334static struct udc_endpoint *fastboot_endpoints[2];
335
336static struct udc_gadget fastboot_gadget = {
337 .notify = fastboot_notify,
338 .ifc_class = 0xff,
339 .ifc_subclass = 0x42,
340 .ifc_protocol = 0x03,
341 .ifc_endpoints = 2,
342 .ifc_string = "fastboot",
343 .ept = fastboot_endpoints,
344};
345
346int fastboot_init(void *base, unsigned size)
347{
348 thread_t *thr;
349 dprintf(INFO, "fastboot_init()\n");
350
351 download_base = base;
352 download_max = size;
353
354 event_init(&usb_online, 0, EVENT_FLAG_AUTOUNSIGNAL);
355 event_init(&txn_done, 0, EVENT_FLAG_AUTOUNSIGNAL);
356
357 in = udc_endpoint_alloc(UDC_TYPE_BULK_IN, 512);
358 if (!in)
359 goto fail_alloc_in;
360 out = udc_endpoint_alloc(UDC_TYPE_BULK_OUT, 512);
361 if (!out)
362 goto fail_alloc_out;
363
364 fastboot_endpoints[0] = in;
365 fastboot_endpoints[1] = out;
366
367 req = udc_request_alloc();
368 if (!req)
369 goto fail_alloc_req;
370
371 if (udc_register_gadget(&fastboot_gadget))
372 goto fail_udc_register;
373
374 fastboot_register("getvar:", cmd_getvar);
375 fastboot_register("download:", cmd_download);
376 fastboot_publish("version", "0.5");
377
378 thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);
379 thread_resume(thr);
380 return 0;
381
382fail_udc_register:
383 udc_request_free(req);
384fail_alloc_req:
385 udc_endpoint_free(out);
386fail_alloc_out:
387 udc_endpoint_free(in);
388fail_alloc_in:
389 return -1;
390}