X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fusb%2Fgadget%2Ff_fastboot.c;h=2160b1ccdc3d93a658e0f28161322b4292a49aaf;hb=9bc34799c8e6d8907b18e02c405576aa6bf9ce15;hp=b9a909986b733edc27eed753059d5ab3e2d3248b;hpb=94b385fa236c85b00ee0bceced05aad2550c2208;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index b9a9099..2160b1c 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,9 @@ #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV #include #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV +#include +#endif #define FASTBOOT_VERSION "0.4" @@ -34,10 +38,12 @@ #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 +/* + * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size + * (64 or 512 or 1024), else we break on certain controllers like DWC3 + * that expect bulk OUT requests to be divisible by maxpacket size. + */ struct f_fastboot { struct usb_function usb_function; @@ -55,15 +61,13 @@ static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) static struct f_fastboot *fastboot_func; static unsigned int download_size; static unsigned int download_bytes; -static bool is_high_speed; 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, + .wMaxPacketSize = cpu_to_le16(64), }; static struct usb_endpoint_descriptor fs_ep_out = { @@ -71,8 +75,15 @@ static struct usb_endpoint_descriptor fs_ep_out = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1, - .bInterval = 0x00, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor hs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_endpoint_descriptor hs_ep_out = { @@ -80,8 +91,7 @@ static struct usb_endpoint_descriptor hs_ep_out = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, - .bInterval = 0x00, + .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_interface_descriptor interface_desc = { @@ -95,13 +105,28 @@ static struct usb_interface_descriptor interface_desc = { .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, }; -static struct usb_descriptor_header *fb_runtime_descs[] = { +static struct usb_descriptor_header *fb_fs_function[] = { (struct usb_descriptor_header *)&interface_desc, (struct usb_descriptor_header *)&fs_ep_in, + (struct usb_descriptor_header *)&fs_ep_out, +}; + +static struct usb_descriptor_header *fb_hs_function[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&hs_ep_in, (struct usb_descriptor_header *)&hs_ep_out, NULL, }; +static struct usb_endpoint_descriptor * +fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + /* * static strings, in UTF-8 */ @@ -125,6 +150,21 @@ static struct usb_gadget_strings *fastboot_strings[] = { static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); static int strcmp_l1(const char *s1, const char *s2); + +static char *fb_response_str; + +void fastboot_fail(const char *reason) +{ + strncpy(fb_response_str, "FAIL\0", 5); + strncat(fb_response_str, reason, FASTBOOT_RESPONSE_LEN - 4 - 1); +} + +void fastboot_okay(const char *reason) +{ + strncpy(fb_response_str, "OKAY\0", 5); + strncat(fb_response_str, reason, FASTBOOT_RESPONSE_LEN - 4 - 1); +} + static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) { int status = req->status; @@ -162,7 +202,15 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) return -ENODEV; f_fb->out_ep->driver_data = c->cdev; - hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + f->descriptors = fb_fs_function; + + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + /* copy HS descriptors */ + f->hs_descriptors = fb_hs_function; + } s = getenv("serial#"); if (s) @@ -221,18 +269,13 @@ static int fastboot_set_alt(struct usb_function *f, struct usb_composite_dev *cdev = f->config->cdev; struct usb_gadget *gadget = cdev->gadget; struct f_fastboot *f_fb = func_to_fastboot(f); + const struct usb_endpoint_descriptor *d; 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); - is_high_speed = true; - } else { - ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out); - is_high_speed = false; - } + d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out); + ret = usb_ep_enable(f_fb->out_ep, d); if (ret) { puts("failed to enable out ep\n"); return ret; @@ -246,7 +289,8 @@ static int fastboot_set_alt(struct usb_function *f, } f_fb->out_req->complete = rx_handler_command; - ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in); + d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in); + ret = usb_ep_enable(f_fb->in_ep, d); if (ret) { puts("failed to enable in ep\n"); goto err; @@ -287,7 +331,6 @@ static int fastboot_add(struct usb_configuration *c) } 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; @@ -311,6 +354,9 @@ static int fastboot_tx_write(const char *buffer, unsigned int buffer_size) memcpy(in_req->buf, buffer, buffer_size); in_req->length = buffer_size; + + usb_ep_dequeue(fastboot_func->in_ep, in_req); + ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0); if (ret) printf("Error %d on queue\n", ret); @@ -355,7 +401,7 @@ static int strcmp_l1(const char *s1, const char *s2) static void cb_getvar(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN]; const char *s; size_t chars_left; @@ -364,7 +410,7 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) strsep(&cmd, ":"); if (!cmd) { - error("missing variable\n"); + error("missing variable"); fastboot_tx_write_str("FAILmissing var"); return; } @@ -377,7 +423,7 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) !strcmp_l1("max-download-size", cmd)) { char str_num[12]; - sprintf(str_num, "0x%08x", CONFIG_USB_FASTBOOT_BUF_SIZE); + sprintf(str_num, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE); strncat(response, str_num, chars_left); } else if (!strcmp_l1("serialno", cmd)) { s = getenv("serial#"); @@ -386,38 +432,52 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) else strcpy(response, "FAILValue not set"); } else { - error("unknown variable: %s\n", cmd); - strcpy(response, "FAILVariable not implemented"); + char envstr[32]; + + snprintf(envstr, sizeof(envstr) - 1, "fastboot.%s", cmd); + s = getenv(envstr); + if (s) { + strncat(response, s, chars_left); + } else { + printf("WARNING: unknown variable: %s\n", cmd); + strcpy(response, "FAILVariable not implemented"); + } } fastboot_tx_write_str(response); } -static unsigned int rx_bytes_expected(unsigned int maxpacket) +static unsigned int rx_bytes_expected(struct usb_ep *ep) { int rx_remain = download_size - download_bytes; - int rem = 0; - if (rx_remain < 0) + unsigned int rem; + unsigned int maxpacket = ep->maxpacket; + + if (rx_remain <= 0) return 0; - if (rx_remain > EP_BUFFER_SIZE) + else if (rx_remain > EP_BUFFER_SIZE) return EP_BUFFER_SIZE; - if (rx_remain < maxpacket) { - rx_remain = maxpacket; - } else if (rx_remain % maxpacket != 0) { - rem = rx_remain % maxpacket; + + /* + * Some controllers e.g. DWC3 don't like OUT transfers to be + * not ending in maxpacket boundary. So just make them happy by + * always requesting for integral multiple of maxpackets. + * This shouldn't bother controllers that don't care about it. + */ + rem = rx_remain % maxpacket; + if (rem > 0) rx_remain = rx_remain + (maxpacket - rem); - } + 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]; + char response[FASTBOOT_RESPONSE_LEN]; unsigned int transfer_size = download_size - download_bytes; const unsigned char *buffer = req->buf; unsigned int buffer_size = req->actual; unsigned int pre_dot_num, now_dot_num; - unsigned int max; if (req->status != 0) { printf("Bad status: %d\n", req->status); @@ -427,7 +487,7 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) if (buffer_size < transfer_size) transfer_size = buffer_size; - memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes, + memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes, buffer, transfer_size); pre_dot_num = download_bytes / BYTES_PER_DOT; @@ -450,16 +510,12 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) req->complete = rx_handler_command; req->length = EP_BUFFER_SIZE; - sprintf(response, "OKAY"); + strcpy(response, "OKAY"); fastboot_tx_write_str(response); printf("\ndownloading of %d bytes finished\n", download_bytes); } else { - max = is_high_speed ? hs_ep_out.wMaxPacketSize : - fs_ep_out.wMaxPacketSize; - req->length = rx_bytes_expected(max); - if (req->length < ep->maxpacket) - req->length = ep->maxpacket; + req->length = rx_bytes_expected(ep); } req->actual = 0; @@ -469,8 +525,7 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) static void cb_download(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; - unsigned int max; + char response[FASTBOOT_RESPONSE_LEN]; strsep(&cmd, ":"); download_size = simple_strtoul(cmd, NULL, 16); @@ -479,18 +534,14 @@ static void cb_download(struct usb_ep *ep, struct usb_request *req) 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) { + strcpy(response, "FAILdata invalid size"); + } else if (download_size > CONFIG_FASTBOOT_BUF_SIZE) { download_size = 0; - sprintf(response, "FAILdata too large"); + strcpy(response, "FAILdata too large"); } else { sprintf(response, "DATA%08x", download_size); req->complete = rx_handler_dl_image; - max = is_high_speed ? hs_ep_out.wMaxPacketSize : - fs_ep_out.wMaxPacketSize; - req->length = rx_bytes_expected(max); - if (req->length < ep->maxpacket) - req->length = ep->maxpacket; + req->length = rx_bytes_expected(ep); } fastboot_tx_write_str(response); } @@ -530,19 +581,27 @@ static void cb_continue(struct usb_ep *ep, struct usb_request *req) static void cb_flash(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN]; strsep(&cmd, ":"); if (!cmd) { - error("missing partition name\n"); + error("missing partition name"); fastboot_tx_write_str("FAILmissing partition name"); return; } - strcpy(response, "FAILno flash device defined"); + /* initialize the response buffer */ + fb_response_str = response; + + fastboot_fail("no flash device defined"); #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV - fb_mmc_flash_write(cmd, (void *)CONFIG_USB_FASTBOOT_BUF_ADDR, - download_bytes, response); + fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR, + download_bytes); +#endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV + fb_nand_flash_write(cmd, + (void *)CONFIG_FASTBOOT_BUF_ADDR, + download_bytes); #endif fastboot_tx_write_str(response); } @@ -551,7 +610,7 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req) static void cb_oem(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; -#ifdef CONFIG_FASTBOOT_FLASH +#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV if (strncmp("format", cmd + 4, 6) == 0) { char cmdbuf[32]; sprintf(cmdbuf, "gpt write mmc %x $partitions", @@ -574,7 +633,7 @@ static void cb_oem(struct usb_ep *ep, struct usb_request *req) static void cb_erase(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN]; strsep(&cmd, ":"); if (!cmd) { @@ -583,10 +642,15 @@ static void cb_erase(struct usb_ep *ep, struct usb_request *req) return; } - strcpy(response, "FAILno flash device defined"); + /* initialize the response buffer */ + fb_response_str = response; + fastboot_fail("no flash device defined"); #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV - fb_mmc_erase(cmd, response); + fb_mmc_erase(cmd); +#endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV + fb_nand_erase(cmd); #endif fastboot_tx_write_str(response); } @@ -646,7 +710,7 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) } if (!func_cb) { - error("unknown command: %s\n", cmdbuf); + error("unknown command: %s", cmdbuf); fastboot_tx_write_str("FAILunknown command"); } else { if (req->actual < req->length) { @@ -654,7 +718,7 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) buf[req->actual] = 0; func_cb(ep, req); } else { - error("buffer overflow\n"); + error("buffer overflow"); fastboot_tx_write_str("FAILbuffer overflow"); } }