X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=drivers%2Fusb%2Fgadget%2Ff_fastboot.c;h=7acffb6c87e062757f2bc820100b2896c75a0d27;hb=9b643e312d528f291966c1f30b0d90bf3b1d43dc;hp=310175acfed369a76e0dacd61b6f5b76f0e714e6;hpb=b4a0b4006fe4d6542899abf402c0569b11aa18e2;p=people%2Fms%2Fu-boot.git diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 310175acfe..7acffb6c87 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; @@ -61,8 +67,7 @@ static struct usb_endpoint_descriptor fs_ep_in = { .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 = { @@ -70,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 = { @@ -79,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 = { @@ -94,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 */ @@ -122,6 +148,22 @@ 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) { @@ -136,6 +178,7 @@ 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); + const char *s; /* DYNAMIC interface numbers assignments */ id = usb_interface_id(c, f); @@ -159,7 +202,19 @@ 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 = env_get("serial#"); + if (s) + g_dnl_set_serialnumber((char *)s); return 0; } @@ -214,15 +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); - else - ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out); + 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; @@ -236,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; @@ -277,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; @@ -301,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); @@ -317,8 +373,20 @@ static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) do_reset(NULL, 0, 0, NULL); } +int __weak fb_set_reboot_flag(void) +{ + return -ENOSYS; +} + static void cb_reboot(struct usb_ep *ep, struct usb_request *req) { + char *cmd = req->buf; + if (!strcmp_l1("reboot-bootloader", cmd)) { + if (fb_set_reboot_flag()) { + fastboot_tx_write_str("FAILCannot set reboot flag"); + return; + } + } fastboot_func->in_req->complete = compl_do_reset; fastboot_tx_write_str("OKAY"); } @@ -333,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; @@ -342,7 +410,7 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) strsep(&cmd, ":"); if (!cmd) { - error("missing variable\n"); + pr_err("missing variable"); fastboot_tx_write_str("FAILmissing var"); return; } @@ -355,35 +423,65 @@ 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#"); + s = env_get("serial#"); if (s) strncat(response, s, chars_left); else strcpy(response, "FAILValue not set"); } else { - error("unknown variable: %s\n", cmd); - strcpy(response, "FAILVariable not implemented"); + char *envstr; + + envstr = malloc(strlen("fastboot.") + strlen(cmd) + 1); + if (!envstr) { + fastboot_tx_write_str("FAILmalloc error"); + return; + } + + sprintf(envstr, "fastboot.%s", cmd); + s = env_get(envstr); + if (s) { + strncat(response, s, chars_left); + } else { + printf("WARNING: unknown variable: %s\n", cmd); + strcpy(response, "FAILVariable not implemented"); + } + + free(envstr); } fastboot_tx_write_str(response); } -static unsigned int rx_bytes_expected(void) +static unsigned int rx_bytes_expected(struct usb_ep *ep) { int rx_remain = download_size - download_bytes; - 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; + + /* + * 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; @@ -397,7 +495,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; @@ -420,14 +518,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 { - req->length = rx_bytes_expected(); - if (req->length < ep->maxpacket) - req->length = ep->maxpacket; + req->length = rx_bytes_expected(ep); } req->actual = 0; @@ -437,7 +533,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]; + char response[FASTBOOT_RESPONSE_LEN]; strsep(&cmd, ":"); download_size = simple_strtoul(cmd, NULL, 16); @@ -446,16 +542,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; - req->length = rx_bytes_expected(); - if (req->length < ep->maxpacket) - req->length = ep->maxpacket; + req->length = rx_bytes_expected(ep); } fastboot_tx_write_str(response); } @@ -467,7 +561,7 @@ static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) puts("Booting kernel..\n"); - sprintf(boot_addr_start, "0x%lx", load_addr); + sprintf(boot_addr_start, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR); do_bootm(NULL, 0, 2, bootm_args); /* This only happens if image is somehow faulty so we start over */ @@ -495,19 +589,76 @@ 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) { + pr_err("missing partition name"); + fastboot_tx_write_str("FAILmissing partition name"); + return; + } + + /* 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_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); +} +#endif + +static void cb_oem(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; +#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV + if (strncmp("format", cmd + 4, 6) == 0) { + char cmdbuf[32]; + sprintf(cmdbuf, "gpt write mmc %x $partitions", + CONFIG_FASTBOOT_FLASH_MMC_DEV); + if (run_command(cmdbuf, 0)) + fastboot_tx_write_str("FAIL"); + else + fastboot_tx_write_str("OKAY"); + } else +#endif + if (strncmp("unlock", cmd + 4, 8) == 0) { + fastboot_tx_write_str("FAILnot implemented"); + } + else { + fastboot_tx_write_str("FAILunknown oem command"); + } +} + +#ifdef CONFIG_FASTBOOT_FLASH +static void cb_erase(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[FASTBOOT_RESPONSE_LEN]; strsep(&cmd, ":"); if (!cmd) { - error("missing partition name\n"); + pr_err("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_erase(cmd); +#endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV + fb_nand_erase(cmd); #endif fastboot_tx_write_str(response); } @@ -539,8 +690,15 @@ static const struct cmd_dispatch_info cmd_dispatch_info[] = { { .cmd = "flash", .cb = cb_flash, + }, { + .cmd = "erase", + .cb = cb_erase, }, #endif + { + .cmd = "oem", + .cb = cb_oem, + }, }; static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) @@ -549,6 +707,9 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; int i; + if (req->status != 0 || req->length == 0) + return; + 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; @@ -557,7 +718,7 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) } if (!func_cb) { - error("unknown command: %s\n", cmdbuf); + pr_err("unknown command: %.*s", req->actual, cmdbuf); fastboot_tx_write_str("FAILunknown command"); } else { if (req->actual < req->length) { @@ -565,14 +726,12 @@ 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"); + pr_err("buffer overflow"); fastboot_tx_write_str("FAILbuffer overflow"); } } - if (req->status == 0) { - *cmdbuf = '\0'; - req->actual = 0; - usb_ep_queue(ep, req, 0); - } + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); }