blob: 43ff0a5e737c2bf2d26d207dd5d0a747d9aba9bf [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2016 MediaTek Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <debug.h>
25#include <dev/udc.h>
26#include <err.h>
27#include <kernel/event.h>
28#include <kernel/thread.h>
29#include <kernel/timer.h>
30#include <lib/fastboot.h>
31#include <platform/udc-common.h>
32#include <stdlib.h>
33#include <string.h>
34#include <trace.h>
35
36#define LOCAL_TRACE 0
37
38/* MAX_USBFS_BULK_SIZE: if use USB3 QMU GPD mode: cannot exceed 63 * 1024 */
39#define MAX_USBFS_BULK_SIZE (16 * 1024)
40
41static event_t usb_online;
42static event_t txn_done;
43static struct udc_endpoint *in, *out;
44static struct udc_request *req;
45int txn_status;
46
47void *download_base;
48unsigned download_max;
49unsigned download_size;
50unsigned fastboot_state = STATE_OFFLINE;
51
52struct fastboot_cmd *cmdlist;
53
54static void req_complete(struct udc_request *req, unsigned actual, int status)
55{
56 txn_status = status;
57 req->length = actual;
58 event_signal(&txn_done, 0);
59}
60
61void fastboot_register(const char *prefix,
62 void (*handle)(const char *arg, void *data, unsigned sz))
63{
64 struct fastboot_cmd *cmd;
65
66 cmd = malloc(sizeof(*cmd));
67 if (cmd) {
68 cmd->prefix = prefix;
69 cmd->prefix_len = strlen(prefix);
70 cmd->handle = handle;
71 cmd->next = cmdlist;
72 cmdlist = cmd;
73 }
74}
75
76struct fastboot_var *varlist;
77
78void fastboot_publish(const char *name, const char *value)
79{
80 struct fastboot_var *var;
81 var = malloc(sizeof(*var));
82 if (var) {
83 var->name = name;
84 var->value = value;
85 var->next = varlist;
86 varlist = var;
87 }
88}
89
90/*
91 * Read data from USB.
92 * _buf: the buffer for reading data. Star address must be cache line aligned!
93 * len: the length for reading data. Must be cache line size aligned!
94 */
95int usb_read(void *_buf, unsigned len)
96{
97 int r;
98 unsigned xfer;
99 unsigned char *buf = _buf;
100 int count = 0;
101
102 if (fastboot_state == STATE_ERROR)
103 goto oops;
104
105 while (len > 0) {
106 xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
107 req->buffer = buf;
108 req->length = xfer;
109 req->complete = req_complete;
110 r = udc_request_queue(out, req);
111 if (r < 0) {
112 LTRACEF("usb_read() queue failed\n");
113 goto oops;
114 }
115 event_wait(&txn_done);
116
117 if (txn_status < 0) {
118 LTRACEF("usb_read() transaction failed\n");
119 goto oops;
120 }
121
122 count += req->length;
123 buf += req->length;
124 len -= req->length;
125
126 /* short transfer? */
127 if (req->length != xfer) break;
128 }
129
130 return count;
131
132oops:
133 fastboot_state = STATE_ERROR;
134 return -1;
135}
136
137/*
138 * Write data to USB.
139 * buf: the buffer for writing data. Star address must be cache line aligned!
140 * len: the length for writing data. Must be cache line size aligned!
141 */
142int usb_write(void *buf, unsigned len)
143{
144 int r;
145
146 if (fastboot_state == STATE_ERROR)
147 goto oops;
148
149 req->buffer = buf;
150 req->length = len;
151 req->complete = req_complete;
152 r = udc_request_queue(in, req);
153 if (r < 0) {
154 LTRACEF("usb_write() queue failed\n");
155 goto oops;
156 }
157 event_wait(&txn_done);
158 if (txn_status < 0) {
159 LTRACEF("usb_write() transaction failed\n");
160 goto oops;
161 }
162 return req->length;
163
164oops:
165 fastboot_state = STATE_ERROR;
166 return -1;
167}
168
169void fastboot_ack(const char *code, const char *reason)
170{
171 char *response;
172
173 if (fastboot_state != STATE_COMMAND)
174 return;
175
176 if (reason == 0)
177 reason = "";
178
179 response = (char *)memalign(CACHE_LINE, MAX_RSP_SIZE);
180 if (!response) {
181 dprintf(CRITICAL, "fastboot: can't allocate memory\n");
182 return;
183 }
184 snprintf(response, MAX_RSP_SIZE, "%s%s", code, reason);
185 fastboot_state = STATE_COMPLETE;
186
187 usb_write(response, strlen(response));
188 free(response);
189
190}
191
192void fastboot_info(const char *reason)
193{
194 char *response;
195
196 if (fastboot_state != STATE_COMMAND)
197 return;
198
199 if (reason == 0)
200 return;
201
202 response = (char *)memalign(CACHE_LINE, MAX_RSP_SIZE);
203 if (!response) {
204 dprintf(CRITICAL, "fastboot: can't allocate memory\n");
205 return;
206 }
207 snprintf(response, MAX_RSP_SIZE, "INFO%s", reason);
208
209 usb_write(response, strlen(response));
210 free(response);
211}
212
213void fastboot_fail(const char *reason)
214{
215 fastboot_ack("FAIL", reason);
216 /* add debug log */
217 dprintf(ALWAYS, "%s\n", reason);
218}
219
220void fastboot_okay(const char *info)
221{
222 fastboot_ack("OKAY", info);
223}
224
225static int fastboot_command_loop(void)
226{
227 struct fastboot_cmd *cmd;
228 int r;
229 char *buffer = (char *)memalign(CACHE_LINE, MAX_RSP_SIZE);
230
231 if (!buffer) {
232 dprintf(CRITICAL, "fastboot: can't allocate memory\n");
233 return ERR_NO_MEMORY;
234 }
235 dprintf(ALWAYS,"fastboot: processing commands\n");
236
237again:
238 while ((fastboot_state != STATE_ERROR) && (fastboot_state != STATE_RETURN)) {
239 r = usb_read(buffer, MAX_RSP_SIZE);
240 if (r < 0) break; /* no input command */
241 buffer[r] = 0;
242 dprintf(ALWAYS, "fastboot: %s[len:%d]\n", buffer, r);
243 dprintf(ALWAYS, "fastboot:[download_base:%p][download_size:0x%x]\n",download_base,(unsigned int)download_size);
244
245 /* Pick up matched command and handle it */
246 for (cmd = cmdlist; cmd; cmd = cmd->next) {
247 if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
248 continue;
249 fastboot_state = STATE_COMMAND;
250 dprintf(ALWAYS,"fastboot:[cmd:%s]-[arg:%s]\n", cmd->prefix, buffer + cmd->prefix_len);
251 cmd->handle((const char *) buffer + cmd->prefix_len, (void *) download_base, download_size);
252 if (fastboot_state == STATE_COMMAND)
253 fastboot_fail("unknown reason");
254 goto again;
255 }
256 dprintf(ALWAYS,"[unknown command]*[%s]*\n", buffer);
257 fastboot_fail("unknown command");
258
259 }
260 if (fastboot_state != STATE_RETURN)
261 fastboot_state = STATE_OFFLINE;
262 dprintf(ALWAYS,"fastboot: oops!\n");
263 free(buffer);
264 return fastboot_state;
265}
266
267static int fastboot_handler(void *arg)
268{
269 int status = 0;
270 while (status != STATE_RETURN) {
271 event_wait(&usb_online);
272 status = fastboot_command_loop();
273 }
274 return 0;
275}
276
277static void fastboot_notify(struct udc_gadget *gadget, unsigned event)
278{
279 if (event == UDC_EVENT_ONLINE) {
280 event_signal(&usb_online, 0);
281 } else if (event == UDC_EVENT_OFFLINE) {
282 event_unsignal(&usb_online);
283 }
284}
285
286static struct udc_endpoint *fastboot_endpoints[2];
287
288static struct udc_gadget fastboot_gadget = {
289 .notify = fastboot_notify,
290 .ifc_class = 0xff,
291 .ifc_subclass = 0x42,
292 .ifc_protocol = 0x03,
293 .ifc_endpoints = 2,
294 .ifc_string = "fastboot",
295 .ept = fastboot_endpoints,
296};
297
298int fastboot_init(void *base, unsigned size)
299{
300 thread_t *thr;
301
302 dprintf(ALWAYS, "fastboot_init()\n");
303 download_base = base;
304 download_max = size;
305
306 event_init(&usb_online, 0, EVENT_FLAG_AUTOUNSIGNAL);
307 event_init(&txn_done, 0, EVENT_FLAG_AUTOUNSIGNAL);
308
309 in = udc_endpoint_alloc(UDC_BULK_IN, 512);
310 if (!in)
311 goto fail_alloc_in;
312 out = udc_endpoint_alloc(UDC_BULK_OUT, 512);
313 if (!out)
314 goto fail_alloc_out;
315
316 fastboot_endpoints[0] = in;
317 fastboot_endpoints[1] = out;
318
319 req = udc_request_alloc();
320 if (!req)
321 goto fail_alloc_req;
322
323 if (udc_register_gadget(&fastboot_gadget))
324 goto fail_udc_register;
325
326 thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
327 if (!thr) {
328 goto fail_alloc_in;
329 }
330 thread_resume(thr);
331
332 udc_start();
333 thread_join(thr, NULL, INFINITE_TIME);
334 udc_stop();
335
336 return 0;
337
338fail_udc_register:
339 udc_request_free(req);
340fail_alloc_req:
341 udc_endpoint_free(out);
342fail_alloc_out:
343 udc_endpoint_free(in);
344fail_alloc_in:
345 return -1;
346}
347