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