diff options
-rw-r--r-- | drivers/dfu/dfu_ram.c | 21 | ||||
-rw-r--r-- | drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/f_fastboot.c | 104 | ||||
-rw-r--r-- | test/py/tests/test_dfu.py | 44 |
4 files changed, 119 insertions, 56 deletions
diff --git a/drivers/dfu/dfu_ram.c b/drivers/dfu/dfu_ram.c index e094a946f6b..c1b00217c91 100644 --- a/drivers/dfu/dfu_ram.c +++ b/drivers/dfu/dfu_ram.c @@ -54,19 +54,26 @@ static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset, int dfu_fill_entity_ram(struct dfu_entity *dfu, char *devstr, char *s) { - char *st; + const char *argv[3]; + const char **parg = argv; + + for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { + *parg = strsep(&s, " "); + if (*parg == NULL) { + error("Invalid number of arguments.\n"); + return -ENODEV; + } + } dfu->dev_type = DFU_DEV_RAM; - st = strsep(&s, " "); - if (strcmp(st, "ram")) { - error("unsupported device: %s\n", st); + if (strcmp(argv[0], "ram")) { + error("unsupported device: %s\n", argv[0]); return -ENODEV; } dfu->layout = DFU_RAM_ADDR; - dfu->data.ram.start = (void *)simple_strtoul(s, &s, 16); - s++; - dfu->data.ram.size = simple_strtoul(s, &s, 16); + dfu->data.ram.start = (void *)simple_strtoul(argv[1], NULL, 16); + dfu->data.ram.size = simple_strtoul(argv[2], NULL, 16); dfu->write_medium = dfu_write_medium_ram; dfu->get_medium_size = dfu_get_medium_size_ram; diff --git a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c index bce9c30ef61..12f5c85c310 100644 --- a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c @@ -229,13 +229,13 @@ static void complete_rx(struct dwc2_udc *dev, u8 ep_num) ROUND(xfer_size, CONFIG_SYS_CACHELINE_SIZE)); req->req.actual += min(xfer_size, req->req.length - req->req.actual); - is_short = (xfer_size < ep->ep.maxpacket); + is_short = !!(xfer_size % ep->ep.maxpacket); debug_cond(DEBUG_OUT_EP != 0, "%s: RX DMA done : ep = %d, rx bytes = %d/%d, " "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", __func__, ep_num, req->req.actual, req->req.length, - is_short, ep_tsr, xfer_size); + is_short, ep_tsr, req->req.length - req->req.actual); if (is_short || req->req.actual == req->req.length) { if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { @@ -292,7 +292,7 @@ static void complete_tx(struct dwc2_udc *dev, u8 ep_num) "%s: TX DMA done : ep = %d, tx bytes = %d/%d, " "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n", __func__, ep_num, req->req.actual, req->req.length, - is_short, ep_tsr, xfer_size); + is_short, ep_tsr, req->req.length - req->req.actual); if (ep_num == 0) { if (dev->ep0state == DATA_STATE_XMIT) { diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 2e87feeece3..28b244a8d00 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -39,6 +39,11 @@ #define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) #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; @@ -57,15 +62,13 @@ static struct f_fastboot *fastboot_func; static unsigned int fastboot_flash_session_id; 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 = { @@ -73,8 +76,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 = { @@ -82,8 +92,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 = { @@ -97,13 +106,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 */ @@ -177,7 +201,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) @@ -236,18 +268,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; @@ -261,7 +288,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; @@ -302,7 +330,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; @@ -427,20 +454,27 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) 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; } @@ -452,7 +486,6 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) 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); @@ -490,11 +523,7 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) 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; @@ -505,7 +534,6 @@ static void cb_download(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; char response[FASTBOOT_RESPONSE_LEN]; - unsigned int max; strsep(&cmd, ":"); download_size = simple_strtoul(cmd, NULL, 16); @@ -521,11 +549,7 @@ static void cb_download(struct usb_ep *ep, struct usb_request *req) } 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); } diff --git a/test/py/tests/test_dfu.py b/test/py/tests/test_dfu.py index 093e8d06787..8649d8731b7 100644 --- a/test/py/tests/test_dfu.py +++ b/test/py/tests/test_dfu.py @@ -30,6 +30,13 @@ env__usb_dev_ports = ( }, ) +# Optional entries (required only when "alt_id_test_file" and +# "alt_id_dummy_file" are specified). +test_file_name = "/dfu_test.bin" +dummy_file_name = "/dfu_dummy.bin" +# Above files are used to generate proper "alt_info" entry +"alt_info": "/%s ext4 0 2;/%s ext4 0 2" % (test_file_name, dummy_file_name), + env__dfu_configs = ( # eMMC, partition 1 { @@ -44,6 +51,24 @@ env__dfu_configs = ( # configurations, but don't want to test every single transfer size # on each, to avoid bloating the overall time taken by testing. "test_sizes": (63, 64, 65), + # This value is optional. + # The name of the environment variable that the the dfu command reads + # alt info from. If unspecified, this defaults to dfu_alt_info, which is + # valid for most systems. Some systems use a different variable name. + # One example is the Odroid XU3, which automatically generates + # $dfu_alt_info, each time the dfu command is run, by concatenating + # $dfu_alt_boot and $dfu_alt_system. + "alt_info_env_name": "dfu_alt_system", + # This value is optional. + # For boards which require the "test file" alt setting number other than + # default (0) it is possible to specify exact file name to be used as + # this parameter. + "alt_id_test_file": test_file_name, + # This value is optional. + # For boards which require the "dummy file" alt setting number other + # than default (1) it is possible to specify exact file name to be used + # as this parameter. + "alt_id_dummy_file": dummy_file_name, }, ) @@ -120,7 +145,11 @@ def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config): u_boot_console.log.action( 'Starting long-running U-Boot dfu shell command') - cmd = 'setenv dfu_alt_info "%s"' % env__dfu_config['alt_info'] + dfu_alt_info_env = env__dfu_config.get('alt_info_env_name', \ + 'dfu_alt_info') + + cmd = 'setenv "%s" "%s"' % (dfu_alt_info_env, + env__dfu_config['alt_info']) u_boot_console.run_command(cmd) cmd = 'dfu 0 ' + env__dfu_config['cmd_params'] @@ -172,7 +201,7 @@ def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config): Nothing. """ - cmd = ['dfu-util', '-a', str(alt_setting), up_dn_load_arg, fn] + cmd = ['dfu-util', '-a', alt_setting, up_dn_load_arg, fn] if 'host_usb_port_path' in env__usb_dev_port: cmd += ['-p', env__usb_dev_port['host_usb_port_path']] u_boot_utils.run_and_log(u_boot_console, cmd) @@ -229,15 +258,15 @@ def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config): u_boot_console.log.action('Writing test data to DFU primary ' + 'altsetting') - dfu_write(0, test_f.abs_fn) + dfu_write(alt_setting_test_file, test_f.abs_fn) u_boot_console.log.action('Writing dummy data to DFU secondary ' + 'altsetting to clear DFU buffers') - dfu_write(1, dummy_f.abs_fn) + dfu_write(alt_setting_dummy_file, dummy_f.abs_fn) u_boot_console.log.action('Reading DFU primary altsetting for ' + 'comparison') - dfu_read(0, readback_fn) + dfu_read(alt_setting_test_file, readback_fn) u_boot_console.log.action('Comparing written and read data') written_hash = test_f.content_hash @@ -260,13 +289,16 @@ def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config): dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'dfu_dummy.bin', 1024) + alt_setting_test_file = env__dfu_config.get('alt_id_test_file', '0') + alt_setting_dummy_file = env__dfu_config.get('alt_id_dummy_file', '1') + ignore_cleanup_errors = True try: start_dfu() u_boot_console.log.action( 'Overwriting DFU primary altsetting with dummy data') - dfu_write(0, dummy_f.abs_fn) + dfu_write(alt_setting_test_file, dummy_f.abs_fn) for size in sizes: with u_boot_console.log.section('Data size %d' % size): |