]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
bf2b72be EC |
2 | /* |
3 | * (C) Copyright 2017 | |
4 | * | |
5 | * Eddie Cai <[email protected]> | |
bf2b72be | 6 | */ |
09140113 | 7 | #include <command.h> |
bf2b72be | 8 | #include <config.h> |
7b51b576 | 9 | #include <env.h> |
bf2b72be | 10 | #include <errno.h> |
f7ae49fc | 11 | #include <log.h> |
bf2b72be EC |
12 | #include <malloc.h> |
13 | #include <memalign.h> | |
e6f6f9e6 | 14 | #include <part.h> |
bf2b72be EC |
15 | #include <linux/usb/ch9.h> |
16 | #include <linux/usb/gadget.h> | |
17 | #include <linux/usb/composite.h> | |
18 | #include <linux/compiler.h> | |
bf2b72be | 19 | #include <g_dnl.h> |
15f09a1a | 20 | #include <asm/arch-rockchip/f_rockusb.h> |
bf2b72be EC |
21 | |
22 | static inline struct f_rockusb *func_to_rockusb(struct usb_function *f) | |
23 | { | |
24 | return container_of(f, struct f_rockusb, usb_function); | |
25 | } | |
26 | ||
27 | static struct usb_endpoint_descriptor fs_ep_in = { | |
28 | .bLength = USB_DT_ENDPOINT_SIZE, | |
29 | .bDescriptorType = USB_DT_ENDPOINT, | |
30 | .bEndpointAddress = USB_DIR_IN, | |
31 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
32 | .wMaxPacketSize = cpu_to_le16(64), | |
33 | }; | |
34 | ||
35 | static struct usb_endpoint_descriptor fs_ep_out = { | |
36 | .bLength = USB_DT_ENDPOINT_SIZE, | |
37 | .bDescriptorType = USB_DT_ENDPOINT, | |
38 | .bEndpointAddress = USB_DIR_OUT, | |
39 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
40 | .wMaxPacketSize = cpu_to_le16(64), | |
41 | }; | |
42 | ||
43 | static struct usb_endpoint_descriptor hs_ep_in = { | |
44 | .bLength = USB_DT_ENDPOINT_SIZE, | |
45 | .bDescriptorType = USB_DT_ENDPOINT, | |
46 | .bEndpointAddress = USB_DIR_IN, | |
47 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
48 | .wMaxPacketSize = cpu_to_le16(512), | |
49 | }; | |
50 | ||
51 | static struct usb_endpoint_descriptor hs_ep_out = { | |
52 | .bLength = USB_DT_ENDPOINT_SIZE, | |
53 | .bDescriptorType = USB_DT_ENDPOINT, | |
54 | .bEndpointAddress = USB_DIR_OUT, | |
55 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
56 | .wMaxPacketSize = cpu_to_le16(512), | |
57 | }; | |
58 | ||
59 | static struct usb_interface_descriptor interface_desc = { | |
60 | .bLength = USB_DT_INTERFACE_SIZE, | |
61 | .bDescriptorType = USB_DT_INTERFACE, | |
62 | .bInterfaceNumber = 0x00, | |
63 | .bAlternateSetting = 0x00, | |
64 | .bNumEndpoints = 0x02, | |
65 | .bInterfaceClass = ROCKUSB_INTERFACE_CLASS, | |
66 | .bInterfaceSubClass = ROCKUSB_INTERFACE_SUB_CLASS, | |
67 | .bInterfaceProtocol = ROCKUSB_INTERFACE_PROTOCOL, | |
68 | }; | |
69 | ||
70 | static struct usb_descriptor_header *rkusb_fs_function[] = { | |
71 | (struct usb_descriptor_header *)&interface_desc, | |
72 | (struct usb_descriptor_header *)&fs_ep_in, | |
73 | (struct usb_descriptor_header *)&fs_ep_out, | |
74 | }; | |
75 | ||
76 | static struct usb_descriptor_header *rkusb_hs_function[] = { | |
77 | (struct usb_descriptor_header *)&interface_desc, | |
78 | (struct usb_descriptor_header *)&hs_ep_in, | |
79 | (struct usb_descriptor_header *)&hs_ep_out, | |
80 | NULL, | |
81 | }; | |
82 | ||
83 | static const char rkusb_name[] = "Rockchip Rockusb"; | |
84 | ||
85 | static struct usb_string rkusb_string_defs[] = { | |
86 | [0].s = rkusb_name, | |
87 | { } /* end of list */ | |
88 | }; | |
89 | ||
90 | static struct usb_gadget_strings stringtab_rkusb = { | |
91 | .language = 0x0409, /* en-us */ | |
92 | .strings = rkusb_string_defs, | |
93 | }; | |
94 | ||
95 | static struct usb_gadget_strings *rkusb_strings[] = { | |
96 | &stringtab_rkusb, | |
97 | NULL, | |
98 | }; | |
99 | ||
100 | static struct f_rockusb *rockusb_func; | |
101 | static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); | |
102 | static int rockusb_tx_write_csw(u32 tag, int residue, u8 status, int size); | |
103 | ||
104 | struct f_rockusb *get_rkusb(void) | |
105 | { | |
106 | struct f_rockusb *f_rkusb = rockusb_func; | |
107 | ||
108 | if (!f_rkusb) { | |
109 | f_rkusb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_rkusb)); | |
110 | if (!f_rkusb) | |
0234609d | 111 | return NULL; |
bf2b72be EC |
112 | |
113 | rockusb_func = f_rkusb; | |
114 | memset(f_rkusb, 0, sizeof(*f_rkusb)); | |
115 | } | |
116 | ||
117 | if (!f_rkusb->buf_head) { | |
118 | f_rkusb->buf_head = memalign(CONFIG_SYS_CACHELINE_SIZE, | |
119 | RKUSB_BUF_SIZE); | |
120 | if (!f_rkusb->buf_head) | |
0234609d | 121 | return NULL; |
bf2b72be EC |
122 | |
123 | f_rkusb->buf = f_rkusb->buf_head; | |
124 | memset(f_rkusb->buf_head, 0, RKUSB_BUF_SIZE); | |
125 | } | |
126 | return f_rkusb; | |
127 | } | |
128 | ||
129 | static struct usb_endpoint_descriptor *rkusb_ep_desc( | |
130 | struct usb_gadget *g, | |
131 | struct usb_endpoint_descriptor *fs, | |
132 | struct usb_endpoint_descriptor *hs) | |
133 | { | |
134 | if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) | |
135 | return hs; | |
136 | return fs; | |
137 | } | |
138 | ||
139 | static void rockusb_complete(struct usb_ep *ep, struct usb_request *req) | |
140 | { | |
141 | int status = req->status; | |
142 | ||
143 | if (!status) | |
144 | return; | |
145 | debug("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); | |
146 | } | |
147 | ||
148 | /* config the rockusb device*/ | |
149 | static int rockusb_bind(struct usb_configuration *c, struct usb_function *f) | |
150 | { | |
151 | int id; | |
152 | struct usb_gadget *gadget = c->cdev->gadget; | |
153 | struct f_rockusb *f_rkusb = func_to_rockusb(f); | |
154 | const char *s; | |
155 | ||
156 | id = usb_interface_id(c, f); | |
157 | if (id < 0) | |
158 | return id; | |
159 | interface_desc.bInterfaceNumber = id; | |
160 | ||
161 | id = usb_string_id(c->cdev); | |
162 | if (id < 0) | |
163 | return id; | |
164 | ||
165 | rkusb_string_defs[0].id = id; | |
166 | interface_desc.iInterface = id; | |
167 | ||
168 | f_rkusb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); | |
169 | if (!f_rkusb->in_ep) | |
170 | return -ENODEV; | |
171 | f_rkusb->in_ep->driver_data = c->cdev; | |
172 | ||
173 | f_rkusb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); | |
174 | if (!f_rkusb->out_ep) | |
175 | return -ENODEV; | |
176 | f_rkusb->out_ep->driver_data = c->cdev; | |
177 | ||
178 | f->descriptors = rkusb_fs_function; | |
179 | ||
180 | if (gadget_is_dualspeed(gadget)) { | |
181 | hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; | |
182 | hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; | |
183 | f->hs_descriptors = rkusb_hs_function; | |
184 | } | |
185 | ||
186 | s = env_get("serial#"); | |
187 | if (s) | |
188 | g_dnl_set_serialnumber((char *)s); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static void rockusb_unbind(struct usb_configuration *c, struct usb_function *f) | |
194 | { | |
195 | /* clear the configuration*/ | |
196 | memset(rockusb_func, 0, sizeof(*rockusb_func)); | |
197 | } | |
198 | ||
199 | static void rockusb_disable(struct usb_function *f) | |
200 | { | |
201 | struct f_rockusb *f_rkusb = func_to_rockusb(f); | |
202 | ||
203 | usb_ep_disable(f_rkusb->out_ep); | |
204 | usb_ep_disable(f_rkusb->in_ep); | |
205 | ||
206 | if (f_rkusb->out_req) { | |
207 | free(f_rkusb->out_req->buf); | |
208 | usb_ep_free_request(f_rkusb->out_ep, f_rkusb->out_req); | |
209 | f_rkusb->out_req = NULL; | |
210 | } | |
211 | if (f_rkusb->in_req) { | |
212 | free(f_rkusb->in_req->buf); | |
213 | usb_ep_free_request(f_rkusb->in_ep, f_rkusb->in_req); | |
214 | f_rkusb->in_req = NULL; | |
215 | } | |
216 | if (f_rkusb->buf_head) { | |
217 | free(f_rkusb->buf_head); | |
218 | f_rkusb->buf_head = NULL; | |
219 | f_rkusb->buf = NULL; | |
220 | } | |
221 | } | |
222 | ||
223 | static struct usb_request *rockusb_start_ep(struct usb_ep *ep) | |
224 | { | |
225 | struct usb_request *req; | |
226 | ||
227 | req = usb_ep_alloc_request(ep, 0); | |
228 | if (!req) | |
229 | return NULL; | |
230 | ||
231 | req->length = EP_BUFFER_SIZE; | |
232 | req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); | |
233 | if (!req->buf) { | |
234 | usb_ep_free_request(ep, req); | |
235 | return NULL; | |
236 | } | |
237 | memset(req->buf, 0, req->length); | |
238 | ||
239 | return req; | |
240 | } | |
241 | ||
242 | static int rockusb_set_alt(struct usb_function *f, unsigned int interface, | |
243 | unsigned int alt) | |
244 | { | |
245 | int ret; | |
246 | struct usb_composite_dev *cdev = f->config->cdev; | |
247 | struct usb_gadget *gadget = cdev->gadget; | |
248 | struct f_rockusb *f_rkusb = func_to_rockusb(f); | |
249 | const struct usb_endpoint_descriptor *d; | |
250 | ||
251 | debug("%s: func: %s intf: %d alt: %d\n", | |
252 | __func__, f->name, interface, alt); | |
253 | ||
254 | d = rkusb_ep_desc(gadget, &fs_ep_out, &hs_ep_out); | |
255 | ret = usb_ep_enable(f_rkusb->out_ep, d); | |
256 | if (ret) { | |
257 | printf("failed to enable out ep\n"); | |
258 | return ret; | |
259 | } | |
260 | ||
261 | f_rkusb->out_req = rockusb_start_ep(f_rkusb->out_ep); | |
262 | if (!f_rkusb->out_req) { | |
263 | printf("failed to alloc out req\n"); | |
264 | ret = -EINVAL; | |
265 | goto err; | |
266 | } | |
267 | f_rkusb->out_req->complete = rx_handler_command; | |
268 | ||
269 | d = rkusb_ep_desc(gadget, &fs_ep_in, &hs_ep_in); | |
270 | ret = usb_ep_enable(f_rkusb->in_ep, d); | |
271 | if (ret) { | |
272 | printf("failed to enable in ep\n"); | |
273 | goto err; | |
274 | } | |
275 | ||
276 | f_rkusb->in_req = rockusb_start_ep(f_rkusb->in_ep); | |
277 | if (!f_rkusb->in_req) { | |
278 | printf("failed alloc req in\n"); | |
279 | ret = -EINVAL; | |
280 | goto err; | |
281 | } | |
282 | f_rkusb->in_req->complete = rockusb_complete; | |
283 | ||
284 | ret = usb_ep_queue(f_rkusb->out_ep, f_rkusb->out_req, 0); | |
285 | if (ret) | |
286 | goto err; | |
287 | ||
288 | return 0; | |
289 | err: | |
290 | rockusb_disable(f); | |
291 | return ret; | |
292 | } | |
293 | ||
294 | static int rockusb_add(struct usb_configuration *c) | |
295 | { | |
296 | struct f_rockusb *f_rkusb = get_rkusb(); | |
297 | int status; | |
298 | ||
299 | debug("%s: cdev: 0x%p\n", __func__, c->cdev); | |
300 | ||
301 | f_rkusb->usb_function.name = "f_rockusb"; | |
302 | f_rkusb->usb_function.bind = rockusb_bind; | |
303 | f_rkusb->usb_function.unbind = rockusb_unbind; | |
304 | f_rkusb->usb_function.set_alt = rockusb_set_alt; | |
305 | f_rkusb->usb_function.disable = rockusb_disable; | |
306 | f_rkusb->usb_function.strings = rkusb_strings; | |
307 | ||
308 | status = usb_add_function(c, &f_rkusb->usb_function); | |
309 | if (status) { | |
ff52577c | 310 | free(f_rkusb->buf_head); |
bf2b72be | 311 | free(f_rkusb); |
ff52577c | 312 | rockusb_func = NULL; |
bf2b72be EC |
313 | } |
314 | return status; | |
315 | } | |
316 | ||
317 | void rockusb_dev_init(char *dev_type, int dev_index) | |
318 | { | |
319 | struct f_rockusb *f_rkusb = get_rkusb(); | |
320 | ||
321 | f_rkusb->dev_type = dev_type; | |
322 | f_rkusb->dev_index = dev_index; | |
323 | } | |
324 | ||
325 | DECLARE_GADGET_BIND_CALLBACK(usb_dnl_rockusb, rockusb_add); | |
326 | ||
327 | static int rockusb_tx_write(const char *buffer, unsigned int buffer_size) | |
328 | { | |
329 | struct usb_request *in_req = rockusb_func->in_req; | |
330 | int ret; | |
331 | ||
332 | memcpy(in_req->buf, buffer, buffer_size); | |
333 | in_req->length = buffer_size; | |
e11f9166 | 334 | debug("Transferring 0x%x bytes\n", buffer_size); |
bf2b72be EC |
335 | usb_ep_dequeue(rockusb_func->in_ep, in_req); |
336 | ret = usb_ep_queue(rockusb_func->in_ep, in_req, 0); | |
337 | if (ret) | |
338 | printf("Error %d on queue\n", ret); | |
339 | return 0; | |
340 | } | |
341 | ||
342 | static int rockusb_tx_write_str(const char *buffer) | |
343 | { | |
344 | return rockusb_tx_write(buffer, strlen(buffer)); | |
345 | } | |
346 | ||
347 | #ifdef DEBUG | |
348 | static void printcbw(char *buf) | |
349 | { | |
350 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
351 | sizeof(struct fsg_bulk_cb_wrap)); | |
352 | ||
353 | memcpy((char *)cbw, buf, USB_BULK_CB_WRAP_LEN); | |
354 | ||
355 | debug("cbw: signature:%x\n", cbw->signature); | |
356 | debug("cbw: tag=%x\n", cbw->tag); | |
357 | debug("cbw: data_transfer_length=%d\n", cbw->data_transfer_length); | |
358 | debug("cbw: flags=%x\n", cbw->flags); | |
359 | debug("cbw: lun=%d\n", cbw->lun); | |
360 | debug("cbw: length=%d\n", cbw->length); | |
361 | debug("cbw: ucOperCode=%x\n", cbw->CDB[0]); | |
362 | debug("cbw: ucReserved=%x\n", cbw->CDB[1]); | |
363 | debug("cbw: dwAddress:%x %x %x %x\n", cbw->CDB[5], cbw->CDB[4], | |
364 | cbw->CDB[3], cbw->CDB[2]); | |
365 | debug("cbw: ucReserved2=%x\n", cbw->CDB[6]); | |
366 | debug("cbw: uslength:%x %x\n", cbw->CDB[8], cbw->CDB[7]); | |
367 | } | |
368 | ||
369 | static void printcsw(char *buf) | |
370 | { | |
371 | ALLOC_CACHE_ALIGN_BUFFER(struct bulk_cs_wrap, csw, | |
372 | sizeof(struct bulk_cs_wrap)); | |
373 | memcpy((char *)csw, buf, USB_BULK_CS_WRAP_LEN); | |
374 | debug("csw: signature:%x\n", csw->signature); | |
375 | debug("csw: tag:%x\n", csw->tag); | |
376 | debug("csw: residue:%x\n", csw->residue); | |
377 | debug("csw: status:%x\n", csw->status); | |
378 | } | |
379 | #endif | |
380 | ||
381 | static int rockusb_tx_write_csw(u32 tag, int residue, u8 status, int size) | |
382 | { | |
383 | ALLOC_CACHE_ALIGN_BUFFER(struct bulk_cs_wrap, csw, | |
384 | sizeof(struct bulk_cs_wrap)); | |
385 | csw->signature = cpu_to_le32(USB_BULK_CS_SIG); | |
386 | csw->tag = tag; | |
387 | csw->residue = cpu_to_be32(residue); | |
388 | csw->status = status; | |
389 | #ifdef DEBUG | |
4f6dc4c8 | 390 | printcsw((char *)csw); |
bf2b72be EC |
391 | #endif |
392 | return rockusb_tx_write((char *)csw, size); | |
393 | } | |
394 | ||
cad66e32 AP |
395 | static void tx_handler_send_csw(struct usb_ep *ep, struct usb_request *req) |
396 | { | |
397 | struct f_rockusb *f_rkusb = get_rkusb(); | |
398 | int status = req->status; | |
399 | ||
400 | if (status) | |
401 | debug("status: %d ep '%s' trans: %d\n", | |
402 | status, ep->name, req->actual); | |
403 | ||
404 | /* Return back to default in_req complete function after sending CSW */ | |
405 | req->complete = rockusb_complete; | |
406 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_GOOD, USB_BULK_CS_WRAP_LEN); | |
407 | } | |
408 | ||
bf2b72be EC |
409 | static unsigned int rx_bytes_expected(struct usb_ep *ep) |
410 | { | |
411 | struct f_rockusb *f_rkusb = get_rkusb(); | |
412 | int rx_remain = f_rkusb->dl_size - f_rkusb->dl_bytes; | |
413 | unsigned int rem; | |
414 | unsigned int maxpacket = ep->maxpacket; | |
415 | ||
416 | if (rx_remain <= 0) | |
417 | return 0; | |
418 | else if (rx_remain > EP_BUFFER_SIZE) | |
419 | return EP_BUFFER_SIZE; | |
420 | ||
421 | rem = rx_remain % maxpacket; | |
422 | if (rem > 0) | |
423 | rx_remain = rx_remain + (maxpacket - rem); | |
424 | ||
425 | return rx_remain; | |
426 | } | |
427 | ||
e11f9166 AP |
428 | /* usb_request complete call back to handle upload image */ |
429 | static void tx_handler_ul_image(struct usb_ep *ep, struct usb_request *req) | |
430 | { | |
431 | ALLOC_CACHE_ALIGN_BUFFER(char, rbuffer, RKBLOCK_BUF_SIZE); | |
432 | struct f_rockusb *f_rkusb = get_rkusb(); | |
433 | struct usb_request *in_req = rockusb_func->in_req; | |
434 | int ret; | |
435 | ||
436 | /* Print error status of previous transfer */ | |
437 | if (req->status) | |
438 | debug("status: %d ep '%s' trans: %d len %d\n", req->status, | |
439 | ep->name, req->actual, req->length); | |
440 | ||
441 | /* On transfer complete reset in_req and feedback host with CSW_GOOD */ | |
442 | if (f_rkusb->ul_bytes >= f_rkusb->ul_size) { | |
443 | in_req->length = 0; | |
444 | in_req->complete = rockusb_complete; | |
445 | ||
446 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_GOOD, | |
447 | USB_BULK_CS_WRAP_LEN); | |
448 | return; | |
449 | } | |
450 | ||
451 | /* Proceed with current chunk */ | |
452 | unsigned int transfer_size = f_rkusb->ul_size - f_rkusb->ul_bytes; | |
453 | ||
454 | if (transfer_size > RKBLOCK_BUF_SIZE) | |
455 | transfer_size = RKBLOCK_BUF_SIZE; | |
456 | /* Read at least one block */ | |
457 | unsigned int blkcount = (transfer_size + f_rkusb->desc->blksz - 1) / | |
458 | f_rkusb->desc->blksz; | |
459 | ||
460 | debug("ul %x bytes, %x blks, read lba %x, ul_size:%x, ul_bytes:%x, ", | |
461 | transfer_size, blkcount, f_rkusb->lba, | |
462 | f_rkusb->ul_size, f_rkusb->ul_bytes); | |
463 | ||
464 | int blks = blk_dread(f_rkusb->desc, f_rkusb->lba, blkcount, rbuffer); | |
465 | ||
466 | if (blks != blkcount) { | |
467 | printf("failed reading from device %s: %d\n", | |
468 | f_rkusb->dev_type, f_rkusb->dev_index); | |
469 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL, | |
470 | USB_BULK_CS_WRAP_LEN); | |
471 | return; | |
472 | } | |
473 | f_rkusb->lba += blkcount; | |
474 | f_rkusb->ul_bytes += transfer_size; | |
475 | ||
476 | /* Proceed with USB request */ | |
477 | memcpy(in_req->buf, rbuffer, transfer_size); | |
478 | in_req->length = transfer_size; | |
479 | in_req->complete = tx_handler_ul_image; | |
11758a56 | 480 | debug("Uploading 0x%x bytes\n", transfer_size); |
e11f9166 AP |
481 | usb_ep_dequeue(rockusb_func->in_ep, in_req); |
482 | ret = usb_ep_queue(rockusb_func->in_ep, in_req, 0); | |
483 | if (ret) | |
484 | printf("Error %d on queue\n", ret); | |
485 | } | |
486 | ||
bf2b72be EC |
487 | /* usb_request complete call back to handle down load image */ |
488 | static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) | |
489 | { | |
490 | struct f_rockusb *f_rkusb = get_rkusb(); | |
491 | unsigned int transfer_size = 0; | |
492 | const unsigned char *buffer = req->buf; | |
493 | unsigned int buffer_size = req->actual; | |
494 | ||
495 | transfer_size = f_rkusb->dl_size - f_rkusb->dl_bytes; | |
bf2b72be EC |
496 | |
497 | if (req->status != 0) { | |
498 | printf("Bad status: %d\n", req->status); | |
499 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL, | |
500 | USB_BULK_CS_WRAP_LEN); | |
501 | return; | |
502 | } | |
503 | ||
504 | if (buffer_size < transfer_size) | |
505 | transfer_size = buffer_size; | |
506 | ||
507 | memcpy((void *)f_rkusb->buf, buffer, transfer_size); | |
508 | f_rkusb->dl_bytes += transfer_size; | |
92c7edae | 509 | int blks = 0, blkcnt = transfer_size / f_rkusb->desc->blksz; |
bf2b72be EC |
510 | |
511 | debug("dl %x bytes, %x blks, write lba %x, dl_size:%x, dl_bytes:%x, ", | |
512 | transfer_size, blkcnt, f_rkusb->lba, f_rkusb->dl_size, | |
513 | f_rkusb->dl_bytes); | |
514 | blks = blk_dwrite(f_rkusb->desc, f_rkusb->lba, blkcnt, f_rkusb->buf); | |
515 | if (blks != blkcnt) { | |
516 | printf("failed writing to device %s: %d\n", f_rkusb->dev_type, | |
517 | f_rkusb->dev_index); | |
518 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL, | |
519 | USB_BULK_CS_WRAP_LEN); | |
520 | return; | |
521 | } | |
522 | f_rkusb->lba += blkcnt; | |
523 | ||
524 | /* Check if transfer is done */ | |
525 | if (f_rkusb->dl_bytes >= f_rkusb->dl_size) { | |
526 | req->complete = rx_handler_command; | |
527 | req->length = EP_BUFFER_SIZE; | |
528 | f_rkusb->buf = f_rkusb->buf_head; | |
11758a56 | 529 | debug("transfer 0x%x bytes done\n", f_rkusb->dl_size); |
bf2b72be EC |
530 | f_rkusb->dl_size = 0; |
531 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_GOOD, | |
532 | USB_BULK_CS_WRAP_LEN); | |
533 | } else { | |
534 | req->length = rx_bytes_expected(ep); | |
535 | if (f_rkusb->buf == f_rkusb->buf_head) | |
536 | f_rkusb->buf = f_rkusb->buf_head + EP_BUFFER_SIZE; | |
537 | else | |
538 | f_rkusb->buf = f_rkusb->buf_head; | |
539 | ||
92c7edae AP |
540 | debug("remain %x bytes, %lx sectors\n", req->length, |
541 | req->length / f_rkusb->desc->blksz); | |
bf2b72be EC |
542 | } |
543 | ||
544 | req->actual = 0; | |
545 | usb_ep_queue(ep, req, 0); | |
546 | } | |
547 | ||
548 | static void cb_test_unit_ready(struct usb_ep *ep, struct usb_request *req) | |
549 | { | |
550 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
551 | sizeof(struct fsg_bulk_cb_wrap)); | |
552 | ||
553 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
554 | ||
555 | rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, | |
556 | CSW_GOOD, USB_BULK_CS_WRAP_LEN); | |
557 | } | |
558 | ||
559 | static void cb_read_storage_id(struct usb_ep *ep, struct usb_request *req) | |
560 | { | |
561 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
562 | sizeof(struct fsg_bulk_cb_wrap)); | |
cad66e32 | 563 | struct f_rockusb *f_rkusb = get_rkusb(); |
bf2b72be EC |
564 | char emmc_id[] = "EMMC "; |
565 | ||
566 | printf("read storage id\n"); | |
567 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
cad66e32 AP |
568 | |
569 | /* Prepare for sending subsequent CSW_GOOD */ | |
570 | f_rkusb->tag = cbw->tag; | |
571 | f_rkusb->in_req->complete = tx_handler_send_csw; | |
572 | ||
bf2b72be | 573 | rockusb_tx_write_str(emmc_id); |
bf2b72be EC |
574 | } |
575 | ||
e4b34a76 AP |
576 | int __weak rk_get_bootrom_chip_version(unsigned int *chip_info, int size) |
577 | { | |
578 | return 0; | |
579 | } | |
580 | ||
581 | static void cb_get_chip_version(struct usb_ep *ep, struct usb_request *req) | |
582 | { | |
583 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
584 | sizeof(struct fsg_bulk_cb_wrap)); | |
585 | struct f_rockusb *f_rkusb = get_rkusb(); | |
586 | unsigned int chip_info[4], i; | |
587 | ||
588 | memset(chip_info, 0, sizeof(chip_info)); | |
589 | rk_get_bootrom_chip_version(chip_info, 4); | |
590 | ||
591 | /* | |
592 | * Chip Version is a string saved in BOOTROM address space Little Endian | |
593 | * | |
594 | * Ex for rk3288: 0x33323041 0x32303134 0x30383133 0x56323030 | |
595 | * which brings: 320A20140813V200 | |
596 | * | |
597 | * Note that memory version do invert MSB/LSB so printing the char | |
598 | * buffer will show: A02341023180002V | |
599 | */ | |
600 | printf("read chip version: "); | |
601 | for (i = 0; i < 4; i++) { | |
602 | printf("%c%c%c%c", | |
603 | (chip_info[i] >> 24) & 0xFF, | |
604 | (chip_info[i] >> 16) & 0xFF, | |
605 | (chip_info[i] >> 8) & 0xFF, | |
606 | (chip_info[i] >> 0) & 0xFF); | |
607 | } | |
608 | printf("\n"); | |
609 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
610 | ||
611 | /* Prepare for sending subsequent CSW_GOOD */ | |
612 | f_rkusb->tag = cbw->tag; | |
613 | f_rkusb->in_req->complete = tx_handler_send_csw; | |
614 | ||
615 | rockusb_tx_write((char *)chip_info, sizeof(chip_info)); | |
616 | } | |
617 | ||
e11f9166 AP |
618 | static void cb_read_lba(struct usb_ep *ep, struct usb_request *req) |
619 | { | |
620 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
621 | sizeof(struct fsg_bulk_cb_wrap)); | |
622 | struct f_rockusb *f_rkusb = get_rkusb(); | |
623 | int sector_count; | |
624 | ||
625 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
626 | sector_count = (int)get_unaligned_be16(&cbw->CDB[7]); | |
627 | f_rkusb->tag = cbw->tag; | |
628 | ||
629 | if (!f_rkusb->desc) { | |
630 | char *type = f_rkusb->dev_type; | |
631 | int index = f_rkusb->dev_index; | |
632 | ||
633 | f_rkusb->desc = blk_get_dev(type, index); | |
634 | if (!f_rkusb->desc || | |
635 | f_rkusb->desc->type == DEV_TYPE_UNKNOWN) { | |
636 | printf("invalid device \"%s\", %d\n", type, index); | |
637 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL, | |
638 | USB_BULK_CS_WRAP_LEN); | |
639 | return; | |
640 | } | |
641 | } | |
642 | ||
643 | f_rkusb->lba = get_unaligned_be32(&cbw->CDB[2]); | |
644 | f_rkusb->ul_size = sector_count * f_rkusb->desc->blksz; | |
645 | f_rkusb->ul_bytes = 0; | |
646 | ||
647 | debug("require read %x bytes, %x sectors from lba %x\n", | |
648 | f_rkusb->ul_size, sector_count, f_rkusb->lba); | |
649 | ||
650 | if (f_rkusb->ul_size == 0) { | |
651 | rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, | |
652 | CSW_FAIL, USB_BULK_CS_WRAP_LEN); | |
653 | return; | |
654 | } | |
655 | ||
656 | /* Start right now sending first chunk */ | |
657 | tx_handler_ul_image(ep, req); | |
658 | } | |
659 | ||
bf2b72be EC |
660 | static void cb_write_lba(struct usb_ep *ep, struct usb_request *req) |
661 | { | |
662 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
663 | sizeof(struct fsg_bulk_cb_wrap)); | |
664 | struct f_rockusb *f_rkusb = get_rkusb(); | |
665 | int sector_count; | |
666 | ||
667 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
668 | sector_count = (int)get_unaligned_be16(&cbw->CDB[7]); | |
92c7edae AP |
669 | f_rkusb->tag = cbw->tag; |
670 | ||
671 | if (!f_rkusb->desc) { | |
672 | char *type = f_rkusb->dev_type; | |
673 | int index = f_rkusb->dev_index; | |
674 | ||
675 | f_rkusb->desc = blk_get_dev(type, index); | |
676 | if (!f_rkusb->desc || | |
677 | f_rkusb->desc->type == DEV_TYPE_UNKNOWN) { | |
678 | printf("invalid device \"%s\", %d\n", type, index); | |
679 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL, | |
680 | USB_BULK_CS_WRAP_LEN); | |
681 | return; | |
682 | } | |
683 | } | |
684 | ||
bf2b72be | 685 | f_rkusb->lba = get_unaligned_be32(&cbw->CDB[2]); |
92c7edae | 686 | f_rkusb->dl_size = sector_count * f_rkusb->desc->blksz; |
bf2b72be | 687 | f_rkusb->dl_bytes = 0; |
92c7edae | 688 | |
bf2b72be EC |
689 | debug("require write %x bytes, %x sectors to lba %x\n", |
690 | f_rkusb->dl_size, sector_count, f_rkusb->lba); | |
691 | ||
692 | if (f_rkusb->dl_size == 0) { | |
693 | rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, | |
694 | CSW_FAIL, USB_BULK_CS_WRAP_LEN); | |
695 | } else { | |
696 | req->complete = rx_handler_dl_image; | |
697 | req->length = rx_bytes_expected(ep); | |
698 | } | |
699 | } | |
700 | ||
f68c8e82 AP |
701 | static void cb_erase_lba(struct usb_ep *ep, struct usb_request *req) |
702 | { | |
703 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
704 | sizeof(struct fsg_bulk_cb_wrap)); | |
705 | struct f_rockusb *f_rkusb = get_rkusb(); | |
706 | int sector_count, lba, blks; | |
707 | ||
708 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
709 | sector_count = (int)get_unaligned_be16(&cbw->CDB[7]); | |
710 | f_rkusb->tag = cbw->tag; | |
711 | ||
712 | if (!f_rkusb->desc) { | |
713 | char *type = f_rkusb->dev_type; | |
714 | int index = f_rkusb->dev_index; | |
715 | ||
716 | f_rkusb->desc = blk_get_dev(type, index); | |
717 | if (!f_rkusb->desc || | |
718 | f_rkusb->desc->type == DEV_TYPE_UNKNOWN) { | |
719 | printf("invalid device \"%s\", %d\n", type, index); | |
720 | rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL, | |
721 | USB_BULK_CS_WRAP_LEN); | |
722 | return; | |
723 | } | |
724 | } | |
725 | ||
726 | lba = get_unaligned_be32(&cbw->CDB[2]); | |
727 | ||
728 | debug("require erase %x sectors from lba %x\n", | |
729 | sector_count, lba); | |
730 | ||
731 | blks = blk_derase(f_rkusb->desc, lba, sector_count); | |
732 | if (blks != sector_count) { | |
733 | printf("failed erasing device %s: %d\n", f_rkusb->dev_type, | |
734 | f_rkusb->dev_index); | |
735 | rockusb_tx_write_csw(f_rkusb->tag, | |
736 | cbw->data_transfer_length, CSW_FAIL, | |
737 | USB_BULK_CS_WRAP_LEN); | |
738 | return; | |
739 | } | |
740 | ||
741 | rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, CSW_GOOD, | |
742 | USB_BULK_CS_WRAP_LEN); | |
743 | } | |
744 | ||
bf2b72be EC |
745 | void __weak rkusb_set_reboot_flag(int flag) |
746 | { | |
747 | struct f_rockusb *f_rkusb = get_rkusb(); | |
748 | ||
749 | printf("rockkusb set reboot flag: %d\n", f_rkusb->reboot_flag); | |
750 | } | |
751 | ||
752 | static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) | |
753 | { | |
754 | struct f_rockusb *f_rkusb = get_rkusb(); | |
755 | ||
756 | rkusb_set_reboot_flag(f_rkusb->reboot_flag); | |
757 | do_reset(NULL, 0, 0, NULL); | |
758 | } | |
759 | ||
760 | static void cb_reboot(struct usb_ep *ep, struct usb_request *req) | |
761 | { | |
762 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
763 | sizeof(struct fsg_bulk_cb_wrap)); | |
764 | struct f_rockusb *f_rkusb = get_rkusb(); | |
765 | ||
bf2b72be EC |
766 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); |
767 | f_rkusb->reboot_flag = cbw->CDB[1]; | |
768 | rockusb_func->in_req->complete = compl_do_reset; | |
769 | rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, CSW_GOOD, | |
770 | USB_BULK_CS_WRAP_LEN); | |
771 | } | |
772 | ||
773 | static void cb_not_support(struct usb_ep *ep, struct usb_request *req) | |
774 | { | |
775 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
776 | sizeof(struct fsg_bulk_cb_wrap)); | |
777 | ||
778 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
779 | printf("Rockusb command %x not support yet\n", cbw->CDB[0]); | |
780 | rockusb_tx_write_csw(cbw->tag, 0, CSW_FAIL, USB_BULK_CS_WRAP_LEN); | |
781 | } | |
782 | ||
783 | static const struct cmd_dispatch_info cmd_dispatch_info[] = { | |
784 | { | |
785 | .cmd = K_FW_TEST_UNIT_READY, | |
786 | .cb = cb_test_unit_ready, | |
787 | }, | |
788 | { | |
789 | .cmd = K_FW_READ_FLASH_ID, | |
790 | .cb = cb_read_storage_id, | |
791 | }, | |
792 | { | |
793 | .cmd = K_FW_SET_DEVICE_ID, | |
794 | .cb = cb_not_support, | |
795 | }, | |
796 | { | |
797 | .cmd = K_FW_TEST_BAD_BLOCK, | |
798 | .cb = cb_not_support, | |
799 | }, | |
800 | { | |
801 | .cmd = K_FW_READ_10, | |
802 | .cb = cb_not_support, | |
803 | }, | |
804 | { | |
805 | .cmd = K_FW_WRITE_10, | |
806 | .cb = cb_not_support, | |
807 | }, | |
808 | { | |
809 | .cmd = K_FW_ERASE_10, | |
810 | .cb = cb_not_support, | |
811 | }, | |
812 | { | |
813 | .cmd = K_FW_WRITE_SPARE, | |
814 | .cb = cb_not_support, | |
815 | }, | |
816 | { | |
817 | .cmd = K_FW_READ_SPARE, | |
818 | .cb = cb_not_support, | |
819 | }, | |
820 | { | |
821 | .cmd = K_FW_ERASE_10_FORCE, | |
822 | .cb = cb_not_support, | |
823 | }, | |
824 | { | |
825 | .cmd = K_FW_GET_VERSION, | |
826 | .cb = cb_not_support, | |
827 | }, | |
828 | { | |
829 | .cmd = K_FW_LBA_READ_10, | |
e11f9166 | 830 | .cb = cb_read_lba, |
bf2b72be EC |
831 | }, |
832 | { | |
833 | .cmd = K_FW_LBA_WRITE_10, | |
834 | .cb = cb_write_lba, | |
835 | }, | |
836 | { | |
837 | .cmd = K_FW_ERASE_SYS_DISK, | |
838 | .cb = cb_not_support, | |
839 | }, | |
840 | { | |
841 | .cmd = K_FW_SDRAM_READ_10, | |
842 | .cb = cb_not_support, | |
843 | }, | |
844 | { | |
845 | .cmd = K_FW_SDRAM_WRITE_10, | |
846 | .cb = cb_not_support, | |
847 | }, | |
848 | { | |
849 | .cmd = K_FW_SDRAM_EXECUTE, | |
850 | .cb = cb_not_support, | |
851 | }, | |
852 | { | |
853 | .cmd = K_FW_READ_FLASH_INFO, | |
854 | .cb = cb_not_support, | |
855 | }, | |
856 | { | |
857 | .cmd = K_FW_GET_CHIP_VER, | |
e4b34a76 | 858 | .cb = cb_get_chip_version, |
bf2b72be EC |
859 | }, |
860 | { | |
861 | .cmd = K_FW_LOW_FORMAT, | |
862 | .cb = cb_not_support, | |
863 | }, | |
864 | { | |
865 | .cmd = K_FW_SET_RESET_FLAG, | |
866 | .cb = cb_not_support, | |
867 | }, | |
868 | { | |
869 | .cmd = K_FW_SPI_READ_10, | |
870 | .cb = cb_not_support, | |
871 | }, | |
872 | { | |
873 | .cmd = K_FW_SPI_WRITE_10, | |
874 | .cb = cb_not_support, | |
875 | }, | |
f68c8e82 AP |
876 | { |
877 | .cmd = K_FW_LBA_ERASE_10, | |
878 | .cb = cb_erase_lba, | |
879 | }, | |
bf2b72be EC |
880 | { |
881 | .cmd = K_FW_SESSION, | |
882 | .cb = cb_not_support, | |
883 | }, | |
884 | { | |
885 | .cmd = K_FW_RESET, | |
886 | .cb = cb_reboot, | |
887 | }, | |
888 | }; | |
889 | ||
890 | static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) | |
891 | { | |
892 | void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; | |
893 | ||
894 | ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw, | |
895 | sizeof(struct fsg_bulk_cb_wrap)); | |
896 | char *cmdbuf = req->buf; | |
897 | int i; | |
898 | ||
899 | if (req->status || req->length == 0) | |
900 | return; | |
901 | ||
902 | memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); | |
903 | #ifdef DEBUG | |
904 | printcbw(req->buf); | |
905 | #endif | |
906 | ||
907 | for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { | |
908 | if (cmd_dispatch_info[i].cmd == cbw->CDB[0]) { | |
909 | func_cb = cmd_dispatch_info[i].cb; | |
910 | break; | |
911 | } | |
912 | } | |
913 | ||
914 | if (!func_cb) { | |
915 | printf("unknown command: %s\n", (char *)req->buf); | |
916 | rockusb_tx_write_str("FAILunknown command"); | |
917 | } else { | |
918 | if (req->actual < req->length) { | |
919 | u8 *buf = (u8 *)req->buf; | |
920 | ||
921 | buf[req->actual] = 0; | |
922 | func_cb(ep, req); | |
923 | } else { | |
924 | puts("buffer overflow\n"); | |
925 | rockusb_tx_write_str("FAILbuffer overflow"); | |
926 | } | |
927 | } | |
928 | ||
929 | *cmdbuf = '\0'; | |
930 | req->actual = 0; | |
931 | usb_ep_queue(ep, req, 0); | |
932 | } |