diff options
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/ci_udc.c | 182 | ||||
-rw-r--r-- | drivers/usb/gadget/ci_udc.h | 17 | ||||
-rw-r--r-- | drivers/usb/gadget/f_dfu.c | 10 | ||||
-rw-r--r-- | drivers/usb/gadget/f_fastboot.c | 513 | ||||
-rw-r--r-- | drivers/usb/gadget/f_thor.c | 12 | ||||
-rw-r--r-- | drivers/usb/gadget/storage_common.c | 4 |
7 files changed, 659 insertions, 80 deletions
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 896c8d4..66becdc 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o +obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o endif ifdef CONFIG_USB_ETHER obj-y += ether.o diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 02d3fda..9cd0036 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -205,13 +205,26 @@ static void ci_invalidate_qtd(int ep_num) static struct usb_request * ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) { - struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); - return &ci_ep->req; + struct ci_req *ci_req; + + ci_req = memalign(ARCH_DMA_MINALIGN, sizeof(*ci_req)); + if (!ci_req) + return NULL; + + INIT_LIST_HEAD(&ci_req->queue); + ci_req->b_buf = 0; + + return &ci_req->req; } -static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req) +static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req) { - return; + struct ci_req *ci_req; + + ci_req = container_of(req, struct ci_req, req); + if (ci_req->b_buf) + free(ci_req->b_buf); + free(ci_req); } static void ep_enable(int num, int in, int maxpacket) @@ -267,99 +280,102 @@ static int ci_ep_disable(struct usb_ep *ep) return 0; } -static int ci_bounce(struct ci_ep *ep, int in) +static int ci_bounce(struct ci_req *ci_req, int in) { - uint32_t addr = (uint32_t)ep->req.buf; - uint32_t ba; + struct usb_request *req = &ci_req->req; + uint32_t addr = (uint32_t)req->buf; + uint32_t hwaddr; + uint32_t aligned_used_len; /* Input buffer address is not aligned. */ if (addr & (ARCH_DMA_MINALIGN - 1)) goto align; /* Input buffer length is not aligned. */ - if (ep->req.length & (ARCH_DMA_MINALIGN - 1)) + if (req->length & (ARCH_DMA_MINALIGN - 1)) goto align; /* The buffer is well aligned, only flush cache. */ - ep->b_len = ep->req.length; - ep->b_buf = ep->req.buf; + ci_req->hw_len = req->length; + ci_req->hw_buf = req->buf; goto flush; align: - /* Use internal buffer for small payloads. */ - if (ep->req.length <= 64) { - ep->b_len = 64; - ep->b_buf = ep->b_fast; - } else { - ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN); - ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len); - if (!ep->b_buf) + if (ci_req->b_buf && req->length > ci_req->b_len) { + free(ci_req->b_buf); + ci_req->b_buf = 0; + } + if (!ci_req->b_buf) { + ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN); + ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len); + if (!ci_req->b_buf) return -ENOMEM; } + ci_req->hw_len = ci_req->b_len; + ci_req->hw_buf = ci_req->b_buf; + if (in) - memcpy(ep->b_buf, ep->req.buf, ep->req.length); + memcpy(ci_req->hw_buf, req->buf, req->length); flush: - ba = (uint32_t)ep->b_buf; - flush_dcache_range(ba, ba + ep->b_len); + hwaddr = (uint32_t)ci_req->hw_buf; + aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN); + flush_dcache_range(hwaddr, hwaddr + aligned_used_len); return 0; } -static void ci_debounce(struct ci_ep *ep, int in) +static void ci_debounce(struct ci_req *ci_req, int in) { - uint32_t addr = (uint32_t)ep->req.buf; - uint32_t ba = (uint32_t)ep->b_buf; + struct usb_request *req = &ci_req->req; + uint32_t addr = (uint32_t)req->buf; + uint32_t hwaddr = (uint32_t)ci_req->hw_buf; + uint32_t aligned_used_len; - if (in) { - if (addr == ba) - return; /* not a bounce */ - goto free; - } - invalidate_dcache_range(ba, ba + ep->b_len); + if (in) + return; + + aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN); + invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len); - if (addr == ba) - return; /* not a bounce */ + if (addr == hwaddr) + return; /* not a bounce */ - memcpy(ep->req.buf, ep->b_buf, ep->req.actual); -free: - /* Large payloads use allocated buffer, free it. */ - if (ep->b_buf != ep->b_fast) - free(ep->b_buf); + memcpy(req->buf, ci_req->hw_buf, req->actual); } -static int ci_ep_queue(struct usb_ep *ep, - struct usb_request *req, gfp_t gfp_flags) +static void ci_ep_submit_next_request(struct ci_ep *ci_ep) { - struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; struct ept_queue_item *item; struct ept_queue_head *head; - int bit, num, len, in, ret; + int bit, num, len, in; + struct ci_req *ci_req; + + ci_ep->req_primed = true; + num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; item = ci_get_qtd(num, in); head = ci_get_qh(num, in); - len = req->length; - ret = ci_bounce(ci_ep, in); - if (ret) - return ret; + ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue); + len = ci_req->req.length; item->next = TERMINATE; item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; - item->page0 = (uint32_t)ci_ep->b_buf; - item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000; - item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000; - item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000; - item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000; + item->page0 = (uint32_t)ci_req->hw_buf; + item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000; + item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000; + item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000; + item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000; ci_flush_qtd(num); head->next = (unsigned) item; head->info = 0; - DBG("ept%d %s queue len %x, buffer %p\n", - num, in ? "in" : "out", len, ci_ep->b_buf); + DBG("ept%d %s queue len %x, req %p, buffer %p\n", + num, in ? "in" : "out", len, ci_req, ci_req->hw_buf); ci_flush_qh(num); if (in) @@ -368,6 +384,29 @@ static int ci_ep_queue(struct usb_ep *ep, bit = EPT_RX(num); writel(bit, &udc->epprime); +} + +static int ci_ep_queue(struct usb_ep *ep, + struct usb_request *req, gfp_t gfp_flags) +{ + struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + struct ci_req *ci_req = container_of(req, struct ci_req, req); + int in, ret; + int __maybe_unused num; + + num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; + + ret = ci_bounce(ci_req, in); + if (ret) + return ret; + + DBG("ept%d %s pre-queue req %p, buffer %p\n", + num, in ? "in" : "out", ci_req, ci_req->hw_buf); + list_add_tail(&ci_req->queue, &ci_ep->queue); + + if (!ci_ep->req_primed) + ci_ep_submit_next_request(ci_ep); return 0; } @@ -376,6 +415,8 @@ static void handle_ep_complete(struct ci_ep *ep) { struct ept_queue_item *item; int num, in, len; + struct ci_req *ci_req; + num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; if (num == 0) @@ -383,20 +424,27 @@ static void handle_ep_complete(struct ci_ep *ep) item = ci_get_qtd(num, in); ci_invalidate_qtd(num); + len = (item->info >> 16) & 0x7fff; if (item->info & 0xff) printf("EP%d/%s FAIL info=%x pg0=%x\n", num, in ? "in" : "out", item->info, item->page0); - len = (item->info >> 16) & 0x7fff; - ep->req.actual = ep->req.length - len; - ci_debounce(ep, in); + ci_req = list_first_entry(&ep->queue, struct ci_req, queue); + list_del_init(&ci_req->queue); + ep->req_primed = false; + + if (!list_empty(&ep->queue)) + ci_ep_submit_next_request(ep); + + ci_req->req.actual = ci_req->req.length - len; + ci_debounce(ci_req, in); - DBG("ept%d %s complete %x\n", - num, in ? "in" : "out", len); - ep->req.complete(&ep->ep, &ep->req); + DBG("ept%d %s req %p, complete %x\n", + num, in ? "in" : "out", ci_req, len); + ci_req->req.complete(&ep->ep, &ci_req->req); if (num == 0) { - ep->req.length = 0; - usb_ep_queue(&ep->ep, &ep->req, 0); + ci_req->req.length = 0; + usb_ep_queue(&ep->ep, &ci_req->req, 0); ep->desc = &ep0_in_desc; } } @@ -405,13 +453,18 @@ static void handle_ep_complete(struct ci_ep *ep) static void handle_setup(void) { - struct usb_request *req = &controller.ep[0].req; + struct ci_ep *ci_ep = &controller.ep[0]; + struct ci_req *ci_req; + struct usb_request *req; struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; struct ept_queue_head *head; struct usb_ctrlrequest r; int status = 0; int num, in, _num, _in, i; char *buf; + + ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue); + req = &ci_req->req; head = ci_get_qh(0, 0); /* EP0 OUT */ ci_invalidate_qh(0); @@ -424,6 +477,9 @@ static void handle_setup(void) DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest), r.bRequestType, r.bRequest, r.wIndex, r.wValue); + list_del_init(&ci_req->queue); + ci_ep->req_primed = false; + switch (SETUP(r.bRequestType, r.bRequest)) { case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE): _num = r.wIndex & 15; @@ -701,6 +757,8 @@ static int ci_udc_probe(void) /* Init EP 0 */ memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init)); controller.ep[0].desc = &ep0_in_desc; + INIT_LIST_HEAD(&controller.ep[0].queue); + controller.ep[0].req_primed = false; controller.gadget.ep0 = &controller.ep[0].ep; INIT_LIST_HEAD(&controller.gadget.ep0->ep_list); @@ -708,6 +766,8 @@ static int ci_udc_probe(void) for (i = 1; i < NUM_ENDPOINTS; i++) { memcpy(&controller.ep[i].ep, &ci_ep_init[1], sizeof(*ci_ep_init)); + INIT_LIST_HEAD(&controller.ep[i].queue); + controller.ep[i].req_primed = false; list_add_tail(&controller.ep[i].ep.ep_list, &controller.gadget.ep_list); } diff --git a/drivers/usb/gadget/ci_udc.h b/drivers/usb/gadget/ci_udc.h index 4425fd9..23cff56 100644 --- a/drivers/usb/gadget/ci_udc.h +++ b/drivers/usb/gadget/ci_udc.h @@ -77,15 +77,22 @@ struct ci_udc { #define CTRL_TXT_BULK (2 << 18) #define CTRL_RXT_BULK (2 << 2) +struct ci_req { + struct usb_request req; + struct list_head queue; + /* Bounce buffer allocated if needed to align the transfer */ + uint8_t *b_buf; + uint32_t b_len; + /* Buffer for the current transfer. Either req.buf/len or b_buf/len */ + uint8_t *hw_buf; + uint32_t hw_len; +}; + struct ci_ep { struct usb_ep ep; struct list_head queue; + bool req_primed; const struct usb_endpoint_descriptor *desc; - - struct usb_request req; - uint8_t *b_buf; - uint32_t b_len; - uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN); }; struct ci_drv { diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 1b1e179..859fe82 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -175,10 +175,17 @@ static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req) req->length, f_dfu->blk_seq_num); } +static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu) +{ + return dfu->poll_timeout ? dfu->poll_timeout(dfu) : + DFU_MANIFEST_POLL_TIMEOUT; +} + static void handle_getstatus(struct usb_request *req) { struct dfu_status *dstat = (struct dfu_status *)req->buf; struct f_dfu *f_dfu = req->context; + struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting); dfu_set_poll_timeout(dstat, 0); @@ -191,7 +198,8 @@ static void handle_getstatus(struct usb_request *req) f_dfu->dfu_state = DFU_STATE_dfuMANIFEST; break; case DFU_STATE_dfuMANIFEST: - dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT); + dfu_set_poll_timeout(dstat, dfu_get_manifest_timeout(dfu)); + break; default: break; } diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 0000000..9dd85b6 --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,513 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix@windriver.com> + * + * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/compiler.h> +#include <version.h> +#include <g_dnl.h> + +#define FASTBOOT_VERSION "0.4" + +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +/* The 64 defined bytes plus \0 */ +#define RESPONSE_LEN (64 + 1) + +#define EP_BUFFER_SIZE 4096 + +struct f_fastboot { + struct usb_function usb_function; + + /* IN/OUT EP's and correspoinding requests */ + struct usb_ep *in_ep, *out_ep; + struct usb_request *in_req, *out_req; +}; + +static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) +{ + return container_of(f, struct f_fastboot, usb_function); +} + +static struct f_fastboot *fastboot_func; +static unsigned int download_size; +static unsigned int download_bytes; + +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1, + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, + .bInterval = 0x00, +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, +}; + +static struct usb_descriptor_header *fb_runtime_descs[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&fs_ep_in, + (struct usb_descriptor_header *)&hs_ep_out, + NULL, +}; + +/* + * static strings, in UTF-8 + */ +static const char fastboot_name[] = "Android Fastboot"; + +static struct usb_string fastboot_string_defs[] = { + [0].s = fastboot_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_fastboot = { + .language = 0x0409, /* en-us */ + .strings = fastboot_string_defs, +}; + +static struct usb_gadget_strings *fastboot_strings[] = { + &stringtab_fastboot, + NULL, +}; + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); + +static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + if (!status) + return; + printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); +} + +static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) +{ + int id; + struct usb_gadget *gadget = c->cdev->gadget; + struct f_fastboot *f_fb = func_to_fastboot(f); + + /* DYNAMIC interface numbers assignments */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + interface_desc.bInterfaceNumber = id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + fastboot_string_defs[0].id = id; + interface_desc.iInterface = id; + + f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!f_fb->in_ep) + return -ENODEV; + f_fb->in_ep->driver_data = c->cdev; + + f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!f_fb->out_ep) + return -ENODEV; + f_fb->out_ep->driver_data = c->cdev; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + + return 0; +} + +static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) +{ + memset(fastboot_func, 0, sizeof(*fastboot_func)); +} + +static void fastboot_disable(struct usb_function *f) +{ + struct f_fastboot *f_fb = func_to_fastboot(f); + + usb_ep_disable(f_fb->out_ep); + usb_ep_disable(f_fb->in_ep); + + if (f_fb->out_req) { + free(f_fb->out_req->buf); + usb_ep_free_request(f_fb->out_ep, f_fb->out_req); + f_fb->out_req = NULL; + } + if (f_fb->in_req) { + free(f_fb->in_req->buf); + usb_ep_free_request(f_fb->in_ep, f_fb->in_req); + f_fb->in_req = NULL; + } +} + +static struct usb_request *fastboot_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return NULL; + + req->length = EP_BUFFER_SIZE; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + memset(req->buf, 0, req->length); + return req; +} + +static int fastboot_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) +{ + int ret; + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct f_fastboot *f_fb = func_to_fastboot(f); + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, interface, alt); + + /* make sure we don't enable the ep twice */ + if (gadget->speed == USB_SPEED_HIGH) + ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out); + else + ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out); + if (ret) { + puts("failed to enable out ep\n"); + return ret; + } + + f_fb->out_req = fastboot_start_ep(f_fb->out_ep); + if (!f_fb->out_req) { + puts("failed to alloc out req\n"); + ret = -EINVAL; + goto err; + } + f_fb->out_req->complete = rx_handler_command; + + ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in); + if (ret) { + puts("failed to enable in ep\n"); + goto err; + } + + f_fb->in_req = fastboot_start_ep(f_fb->in_ep); + if (!f_fb->in_req) { + puts("failed alloc req in\n"); + ret = -EINVAL; + goto err; + } + f_fb->in_req->complete = fastboot_complete; + + ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0); + if (ret) + goto err; + + return 0; +err: + fastboot_disable(f); + return ret; +} + +static int fastboot_add(struct usb_configuration *c) +{ + struct f_fastboot *f_fb = fastboot_func; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + if (!f_fb) { + f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb)); + if (!f_fb) + return -ENOMEM; + + fastboot_func = f_fb; + memset(f_fb, 0, sizeof(*f_fb)); + } + + f_fb->usb_function.name = "f_fastboot"; + f_fb->usb_function.hs_descriptors = fb_runtime_descs; + f_fb->usb_function.bind = fastboot_bind; + f_fb->usb_function.unbind = fastboot_unbind; + f_fb->usb_function.set_alt = fastboot_set_alt; + f_fb->usb_function.disable = fastboot_disable; + f_fb->usb_function.strings = fastboot_strings; + + status = usb_add_function(c, &f_fb->usb_function); + if (status) { + free(f_fb); + fastboot_func = f_fb; + } + + return status; +} +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add); + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + struct usb_request *in_req = fastboot_func->in_req; + int ret; + + memcpy(in_req->buf, buffer, buffer_size); + in_req->length = buffer_size; + ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0); + if (ret) + printf("Error %d on queue\n", ret); + return 0; +} + +static int fastboot_tx_write_str(const char *buffer) +{ + return fastboot_tx_write(buffer, strlen(buffer)); +} + +static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_func->in_req->complete = compl_do_reset; + fastboot_tx_write_str("OKAY"); +} + +static int strcmp_l1(const char *s1, const char *s2) +{ + if (!s1 || !s2) + return -1; + return strncmp(s1, s2, strlen(s1)); +} + +static void cb_getvar(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + const char *s; + + strcpy(response, "OKAY"); + strsep(&cmd, ":"); + if (!cmd) { + fastboot_tx_write_str("FAILmissing var"); + return; + } + + if (!strcmp_l1("version", cmd)) { + strncat(response, FASTBOOT_VERSION, sizeof(response)); + } else if (!strcmp_l1("bootloader-version", cmd)) { + strncat(response, U_BOOT_VERSION, sizeof(response)); + } else if (!strcmp_l1("downloadsize", cmd)) { + char str_num[12]; + + sprintf(str_num, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE); + strncat(response, str_num, sizeof(response)); + } else if (!strcmp_l1("serialno", cmd)) { + s = getenv("serial#"); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else { + strcpy(response, "FAILVariable not implemented"); + } + fastboot_tx_write_str(response); +} + +static unsigned int rx_bytes_expected(void) +{ + int rx_remain = download_size - download_bytes; + if (rx_remain < 0) + return 0; + if (rx_remain > EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + return rx_remain; +} + +#define BYTES_PER_DOT 0x20000 +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + char response[RESPONSE_LEN]; + unsigned int transfer_size = download_size - download_bytes; + const unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + + if (req->status != 0) { + printf("Bad status: %d\n", req->status); + return; + } + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes, + buffer, transfer_size); + + download_bytes += transfer_size; + + /* Check if transfer is done */ + if (download_bytes >= download_size) { + /* + * Reset global transfer variable, keep download_bytes because + * it will be used in the next possible flashing command + */ + download_size = 0; + req->complete = rx_handler_command; + req->length = EP_BUFFER_SIZE; + + sprintf(response, "OKAY"); + fastboot_tx_write_str(response); + + printf("\ndownloading of %d bytes finished\n", download_bytes); + } else { + req->length = rx_bytes_expected(); + if (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + + if (download_bytes && !(download_bytes % BYTES_PER_DOT)) { + putc('.'); + if (!(download_bytes % (74 * BYTES_PER_DOT))) + putc('\n'); + } + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void cb_download(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + + strsep(&cmd, ":"); + download_size = simple_strtoul(cmd, NULL, 16); + download_bytes = 0; + + printf("Starting download of %d bytes\n", download_size); + + if (0 == download_size) { + sprintf(response, "FAILdata invalid size"); + } else if (download_size > CONFIG_USB_FASTBOOT_BUF_SIZE) { + download_size = 0; + sprintf(response, "FAILdata too large"); + } else { + sprintf(response, "DATA%08x", download_size); + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(); + if (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + fastboot_tx_write_str(response); +} + +static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + char boot_addr_start[12]; + char *bootm_args[] = { "bootm", boot_addr_start, NULL }; + + puts("Booting kernel..\n"); + + sprintf(boot_addr_start, "0x%lx", load_addr); + do_bootm(NULL, 0, 2, bootm_args); + + /* This only happens if image is somehow faulty so we start over */ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_boot(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_func->in_req->complete = do_bootm_on_complete; + fastboot_tx_write_str("OKAY"); +} + +struct cmd_dispatch_info { + char *cmd; + void (*cb)(struct usb_ep *ep, struct usb_request *req); +}; + +static const struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = "reboot", + .cb = cb_reboot, + }, { + .cmd = "getvar:", + .cb = cb_getvar, + }, { + .cmd = "download:", + .cb = cb_download, + }, { + .cmd = "boot", + .cb = cb_boot, + }, +}; + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + char *cmdbuf = req->buf; + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) { + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) + fastboot_tx_write_str("FAILunknown command"); + else + func_cb(ep, req); + + if (req->status == 0) { + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); + } +} diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index feef9e4..28f215e 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -219,21 +219,15 @@ static int download_tail(long long int left, int cnt) } /* - * To store last "packet" DFU storage backend requires dfu_write with - * size parameter equal to 0 + * To store last "packet" or write file from buffer to filesystem + * DFU storage backend requires dfu_flush * * This also frees memory malloc'ed by dfu_get_buf(), so no explicit * need fo call dfu_free_buf() is needed. */ - ret = dfu_write(dfu_entity, transfer_buffer, 0, cnt); - if (ret) - error("DFU write failed [%d] cnt: %d", ret, cnt); - ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt); - if (ret) { + if (ret) error("DFU flush failed!"); - return ret; - } return ret; } diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 7430074..02803df 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -311,11 +311,7 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ /* Number of buffers we will use. 2 is enough for double-buffering */ -#ifndef CONFIG_CI_UDC #define FSG_NUM_BUFFERS 2 -#else -#define FSG_NUM_BUFFERS 1 /* ci_udc only allows 1 req per ep at present */ -#endif /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384) |