From 4e2c4ad3604ba6f5053090749d64ed3ce5914805 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:51:22 +0200 Subject: [PATCH 01/21] usb: ehci: Properly set hub devnum and portnr with usb-1 hubs in the chain For full / low speed devices we need to get the devnum and portnr of the tt, so of the first upstream usb-2 hub, not of the parent device (which may be a usb-1 hub). Signed-off-by: Hans de Goede --- drivers/usb/host/ehci-hcd.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index eaf59134cb0e..41af302e036c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -273,6 +273,29 @@ static inline u8 ehci_encode_speed(enum usb_device_speed speed) return QH_FULL_SPEED; } +static void ehci_update_endpt2_dev_n_port(struct usb_device *dev, + struct QH *qh) +{ + struct usb_device *ttdev; + + if (dev->speed != USB_SPEED_LOW && dev->speed != USB_SPEED_FULL) + return; + + /* + * For full / low speed devices we need to get the devnum and portnr of + * the tt, so of the first upstream usb-2 hub, there may be usb-1 hubs + * in the tree before that one! + */ + ttdev = dev; + while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) + ttdev = ttdev->parent; + if (!ttdev->parent) + return; + + qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) | + QH_ENDPT2_HUBADDR(ttdev->parent->devnum)); +} + static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) @@ -390,10 +413,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | QH_ENDPT1_DEVADDR(usb_pipedevice(pipe)); qh->qh_endpt1 = cpu_to_hc32(endpt); - endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | - QH_ENDPT2_HUBADDR(dev->parent->devnum) | - QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); + endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); qh->qh_endpt2 = cpu_to_hc32(endpt); + ehci_update_endpt2_dev_n_port(dev, qh); qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); @@ -1201,12 +1223,10 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, (1 << 0)); /* S-mask: microframe 0 */ if (dev->speed == USB_SPEED_LOW || dev->speed == USB_SPEED_FULL) { - debug("TT: port: %d, hub address: %d\n", - dev->portnr, dev->parent->devnum); - qh->qh_endpt2 |= cpu_to_hc32((dev->portnr << 23) | - (dev->parent->devnum << 16) | - (0x1c << 8)); /* C-mask: microframes 2-4 */ + /* C-mask: microframes 2-4 */ + qh->qh_endpt2 |= cpu_to_hc32((0x1c << 8)); } + ehci_update_endpt2_dev_n_port(dev, qh); td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); From ea7b30c58973d59396c71d5880a652639d479a68 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:51:23 +0200 Subject: [PATCH 02/21] usb: ehci: Add missing cache flush to destroy_int_queue Signed-off-by: Hans de Goede --- drivers/usb/host/ehci-hcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 41af302e036c..1a0ddc3d5105 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1342,6 +1342,8 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) if (NEXT_QH(cur) == queue->first) { debug("found candidate. removing from chain\n"); cur->qh_link = queue->last->qh_link; + flush_dcache_range((uint32_t)cur, + ALIGN_END_ADDR(struct QH, cur, 1)); result = 0; break; } From 415548d88446134549917aae026f53dbbee36fd2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:51:24 +0200 Subject: [PATCH 03/21] usb: ehci: poll_int_queue check real qtd, not the overlay When we first start an int queue, the qh's overlay area is all zeros. This gets filled by the hc with the actual qtd values as soon as it advances the queue, but we may call poll_int_queue before then, in which case we would think the transfer has completed as the hc has not yet copied the qt_token to the overlay, so the active flag is not set. This fixes this by checking the actual qtd token, rather then the overlay. This also fixes a (theoretical) race where we see the completion in the overlay and free and re-use the qtd before the hc has completed writing back the overlay to the actual qtd. Signed-off-by: Hans de Goede --- drivers/usb/host/ehci-hcd.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1a0ddc3d5105..635a3b45110c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1297,6 +1297,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) { struct QH *cur = queue->current; + struct qTD *cur_td; /* depleted queue */ if (cur == NULL) { @@ -1304,20 +1305,21 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) return NULL; } /* still active */ - invalidate_dcache_range((uint32_t)cur, - ALIGN_END_ADDR(struct QH, cur, 1)); - if (cur->qh_overlay.qt_token & cpu_to_hc32(0x80)) { - debug("Exit poll_int_queue with no completed intr transfer. " - "token is %x\n", cur->qh_overlay.qt_token); + cur_td = &queue->tds[queue->current - queue->first]; + invalidate_dcache_range((uint32_t)cur_td, + ALIGN_END_ADDR(struct qTD, cur_td, 1)); + if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) & + QT_TOKEN_STATUS_ACTIVE) { + debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", + hc32_to_cpu(cur_td->qt_token)); return NULL; } if (!(cur->qh_link & QH_LINK_TERMINATE)) queue->current++; else queue->current = NULL; - debug("Exit poll_int_queue with completed intr transfer. " - "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, - &cur->qh_overlay.qt_token, queue->first); + debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n", + hc32_to_cpu(cur_td->qt_token), cur, queue->first); return cur->buffer; } From 36b73109c49c1359a5fcc790c00717cb27468c12 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:51:25 +0200 Subject: [PATCH 04/21] usb: ehci: Make periodic_schedules a per controller variable Periodic schedules tracks how many int_queue-s are active, and decides whether or not to en/disable the periodic schedule based on this. This is clearly a per controller thing. Signed-off-by: Hans de Goede --- drivers/usb/host/ehci-hcd.c | 9 ++++----- drivers/usb/host/ehci.h | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 635a3b45110c..6323c508375a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -996,6 +996,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) * Set up periodic list * Step 1: Parent QH for all periodic transfers. */ + ehcic[index].periodic_schedules = 0; periodic = &ehcic[index].periodic_queue; memset(periodic, 0, sizeof(*periodic)); periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); @@ -1154,8 +1155,6 @@ disable_periodic(struct ehci_ctrl *ctrl) return 0; } -static int periodic_schedules; - struct int_queue * create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, int elementsize, void *buffer) @@ -1278,7 +1277,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, debug("FATAL: periodic should never fail, but did"); goto fail3; } - periodic_schedules++; + ctrl->periodic_schedules++; debug("Exit create_int_queue\n"); return result; @@ -1335,7 +1334,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) debug("FATAL: periodic should never fail, but did"); goto out; } - periodic_schedules--; + ctrl->periodic_schedules--; struct QH *cur = &ctrl->periodic_queue; timeout = get_timer(0) + 500; /* abort after 500ms */ @@ -1357,7 +1356,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) } } - if (periodic_schedules > 0) { + if (ctrl->periodic_schedules > 0) { result = enable_periodic(ctrl); if (result < 0) debug("FATAL: periodic should never fail, but did"); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 093eb4b83235..433e703da82b 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -246,6 +246,7 @@ struct ehci_ctrl { struct QH qh_list __aligned(USB_DMA_MINALIGN); struct QH periodic_queue __aligned(USB_DMA_MINALIGN); uint32_t *periodic_list; + int periodic_schedules; int ntds; }; From 9b2393812e2b844919ac601b893e6210d5eb6e2c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:51:26 +0200 Subject: [PATCH 05/21] usb: kbd: Fix unaligned buffer usage in usb_kbd_setled() Signed-off-by: Hans de Goede --- common/usb_kbd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/usb_kbd.c b/common/usb_kbd.c index c34fd5c786a2..87f4125a9f2f 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -170,11 +170,12 @@ static void usb_kbd_setled(struct usb_device *dev) { struct usb_interface *iface = &dev->config.if_desc[0]; struct usb_kbd_pdata *data = dev->privptr; - uint32_t leds = data->flags & USB_KBD_LEDMASK; + ALLOC_ALIGN_BUFFER(uint32_t, leds, 1, USB_DMA_MINALIGN); + *leds = data->flags & USB_KBD_LEDMASK; usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0x200, iface->desc.bInterfaceNumber, (void *)&leds, 1, 0); + 0x200, iface->desc.bInterfaceNumber, leds, 1, 0); } #define CAPITAL_MASK 0x20 From 0ea09dfe873657df47d83d5d4e0090bbf05c6abb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:54:34 +0200 Subject: [PATCH 06/21] usb: kbd: Do not treat -ENODEV as an error for usb_kbd_deregister ENODEV menas no usb keyboard was registered, threat this as a successful usb_kbd_deregister. Signed-off-by: Hans de Goede --- common/usb_kbd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 87f4125a9f2f..4c17b0d8ec37 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -8,6 +8,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include +#include #include #include #include @@ -559,7 +560,11 @@ int drv_usb_kbd_init(void) int usb_kbd_deregister(void) { #ifdef CONFIG_SYS_STDIO_DEREGISTER - return stdio_deregister(DEVNAME); + int ret = stdio_deregister(DEVNAME); + if (ret && ret != -ENODEV) + return ret; + + return 0; #else return 1; #endif From 6e78c74f62ebfa378d1a2f9b83423b23545c28bf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:54:35 +0200 Subject: [PATCH 07/21] usb: kbd: On a "usb reset" call usb_kbd_deregister() before calling usb_stop() We need to call usb_kbd_deregister() before calling usb_stop(). usbkbd's stdio_dev->priv points to the usb_device, and usb_kbd_testc dereferences usb_device->privptr. usb_stop zeros usb_device, leaving usb_device->privptr NULL, causing bad things (tm) to happen once control returns to the main loop and usb_kbd_testc gets called. Calling usb_kbd_deregister() avoids this. Note that we do not allow the "usb reset" to continue when the deregister fails. This will be fixed in a later patch. For the same reasons always fail "usb stop" if the usb_kbd_deregister() fails, even in the force path. This can happen when CONFIG_SYS_STDIO_DEREGISTER is not set. Signed-off-by: Hans de Goede --- common/cmd_usb.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 2519497dad8c..b2aa44c2a724 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -430,6 +430,16 @@ static int do_usbboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #endif /* CONFIG_USB_STORAGE */ +static int do_usb_stop_keyboard(void) +{ +#ifdef CONFIG_USB_KEYBOARD + if (usb_kbd_deregister() != 0) { + printf("USB not stopped: usbkbd still using USB\n"); + return 1; + } +#endif + return 0; +} /****************************************************************************** * usb command intepreter @@ -450,6 +460,8 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if ((strncmp(argv[1], "reset", 5) == 0) || (strncmp(argv[1], "start", 5) == 0)) { bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start"); + if (do_usb_stop_keyboard() != 0) + return 1; usb_stop(); printf("(Re)start USB...\n"); if (usb_init() >= 0) { @@ -468,19 +480,10 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } if (strncmp(argv[1], "stop", 4) == 0) { -#ifdef CONFIG_USB_KEYBOARD - if (argc == 2) { - if (usb_kbd_deregister() != 0) { - printf("USB not stopped: usbkbd still" - " using USB\n"); - return 1; - } - } else { - /* forced stop, switch console in to serial */ + if (argc != 2) console_assign(stdin, "serial"); - usb_kbd_deregister(); - } -#endif + if (do_usb_stop_keyboard() != 0) + return 1; printf("stopping USB..\n"); usb_stop(); return 0; From 3f78a28037c65b92d2717b7787902a853c2506a4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:54:36 +0200 Subject: [PATCH 08/21] usb: kbd: Remove check for already being registered We now always properly deregister the keyboard before calling drv_usb_kbd_init(), so we can drop the check for already being registered. Signed-off-by: Hans de Goede --- common/usb_kbd.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 4c17b0d8ec37..d4d5f485218c 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -490,7 +490,7 @@ static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) /* Search for keyboard and register it if found. */ int drv_usb_kbd_init(void) { - struct stdio_dev usb_kbd_dev, *old_dev; + struct stdio_dev usb_kbd_dev; struct usb_device *dev; char *stdinname = getenv("stdin"); int error, i; @@ -509,16 +509,6 @@ int drv_usb_kbd_init(void) if (usb_kbd_probe(dev, 0) != 1) continue; - /* We found a keyboard, check if it is already registered. */ - debug("USB KBD: found set up device.\n"); - old_dev = stdio_get_by_name(DEVNAME); - if (old_dev) { - /* Already registered, just return ok. */ - debug("USB KBD: is already registered.\n"); - usb_kbd_deregister(); - return 1; - } - /* Register the keyboard */ debug("USB KBD: register.\n"); memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); From 32d019265d1f0c334f2f86407abf295d46bd2f4d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:54:37 +0200 Subject: [PATCH 09/21] stdio: Add force parameter to stdio_deregister In some cases we really want to move forward with a deregister, add a force parameter to allow this, and replace the dev with a nulldev in this case. Signed-off-by: Hans de Goede --- common/stdio.c | 13 ++++++++++--- common/usb_kbd.c | 2 +- drivers/serial/serial-uclass.c | 2 +- include/stdio_dev.h | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/common/stdio.c b/common/stdio.c index c878103a4826..82328150cba1 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -34,6 +34,9 @@ char *stdio_names[MAX_FILES] = { "stdin", "stdout", "stderr" }; #define CONFIG_SYS_DEVICE_NULLDEV 1 #endif +#ifdef CONFIG_SYS_STDIO_DEREGISTER +#define CONFIG_SYS_DEVICE_NULLDEV 1 +#endif #ifdef CONFIG_SYS_DEVICE_NULLDEV void nulldev_putc(struct stdio_dev *dev, const char c) @@ -172,7 +175,7 @@ int stdio_register(struct stdio_dev *dev) * returns 0 if success, -1 if device is assigned and 1 if devname not found */ #ifdef CONFIG_SYS_STDIO_DEREGISTER -int stdio_deregister_dev(struct stdio_dev *dev) +int stdio_deregister_dev(struct stdio_dev *dev, int force) { int l; struct list_head *pos; @@ -181,6 +184,10 @@ int stdio_deregister_dev(struct stdio_dev *dev) /* get stdio devices (ListRemoveItem changes the dev list) */ for (l=0 ; l< MAX_FILES; l++) { if (stdio_devices[l] == dev) { + if (force) { + strcpy(temp_names[l], "nulldev"); + continue; + } /* Device is assigned -> report error */ return -1; } @@ -202,7 +209,7 @@ int stdio_deregister_dev(struct stdio_dev *dev) return 0; } -int stdio_deregister(const char *devname) +int stdio_deregister(const char *devname, int force) { struct stdio_dev *dev; @@ -211,7 +218,7 @@ int stdio_deregister(const char *devname) if (!dev) /* device not found */ return -ENODEV; - return stdio_deregister_dev(dev); + return stdio_deregister_dev(dev, force); } #endif /* CONFIG_SYS_STDIO_DEREGISTER */ diff --git a/common/usb_kbd.c b/common/usb_kbd.c index d4d5f485218c..dcb693d0c3b1 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -550,7 +550,7 @@ int drv_usb_kbd_init(void) int usb_kbd_deregister(void) { #ifdef CONFIG_SYS_STDIO_DEREGISTER - int ret = stdio_deregister(DEVNAME); + int ret = stdio_deregister(DEVNAME, 0); if (ret && ret != -ENODEV) return ret; diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 1ac943f69288..fd010cac4286 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -198,7 +198,7 @@ static int serial_pre_remove(struct udevice *dev) #ifdef CONFIG_SYS_STDIO_DEREGISTER struct serial_dev_priv *upriv = dev->uclass_priv; - if (stdio_deregister_dev(upriv->sdev)) + if (stdio_deregister_dev(upriv->sdev), 0) return -EPERM; #endif diff --git a/include/stdio_dev.h b/include/stdio_dev.h index 268de8ea7005..24da23fe5011 100644 --- a/include/stdio_dev.h +++ b/include/stdio_dev.h @@ -103,8 +103,8 @@ int stdio_init(void); void stdio_print_current_devices(void); #ifdef CONFIG_SYS_STDIO_DEREGISTER -int stdio_deregister(const char *devname); -int stdio_deregister_dev(struct stdio_dev *dev); +int stdio_deregister(const char *devname, int force); +int stdio_deregister_dev(struct stdio_dev *dev, int force); #endif struct list_head* stdio_get_list(void); struct stdio_dev* stdio_get_by_name(const char* name); From 8a8a2257ec55a997d97edf6664249a628248fe01 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Sep 2014 16:54:38 +0200 Subject: [PATCH 10/21] usb: kbd: Allow "usb reset" to continue when an usb kbd is used Use the new force parameter to make the stdio_deregister succeed, replacing stdin with a nulldev, and assume that the usb keyboard will come back after the reset. Signed-off-by: Hans de Goede --- common/cmd_usb.c | 8 ++++---- common/usb_kbd.c | 4 ++-- include/usb.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/cmd_usb.c b/common/cmd_usb.c index b2aa44c2a724..c192498257fe 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -430,10 +430,10 @@ static int do_usbboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #endif /* CONFIG_USB_STORAGE */ -static int do_usb_stop_keyboard(void) +static int do_usb_stop_keyboard(int force) { #ifdef CONFIG_USB_KEYBOARD - if (usb_kbd_deregister() != 0) { + if (usb_kbd_deregister(force) != 0) { printf("USB not stopped: usbkbd still using USB\n"); return 1; } @@ -460,7 +460,7 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if ((strncmp(argv[1], "reset", 5) == 0) || (strncmp(argv[1], "start", 5) == 0)) { bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start"); - if (do_usb_stop_keyboard() != 0) + if (do_usb_stop_keyboard(1) != 0) return 1; usb_stop(); printf("(Re)start USB...\n"); @@ -482,7 +482,7 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (strncmp(argv[1], "stop", 4) == 0) { if (argc != 2) console_assign(stdin, "serial"); - if (do_usb_stop_keyboard() != 0) + if (do_usb_stop_keyboard(0) != 0) return 1; printf("stopping USB..\n"); usb_stop(); diff --git a/common/usb_kbd.c b/common/usb_kbd.c index dcb693d0c3b1..fdc083c70cf9 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -547,10 +547,10 @@ int drv_usb_kbd_init(void) } /* Deregister the keyboard. */ -int usb_kbd_deregister(void) +int usb_kbd_deregister(int force) { #ifdef CONFIG_SYS_STDIO_DEREGISTER - int ret = stdio_deregister(DEVNAME, 0); + int ret = stdio_deregister(DEVNAME, force); if (ret && ret != -ENODEV) return ret; diff --git a/include/usb.h b/include/usb.h index d9fedeeff7de..c355fbe48198 100644 --- a/include/usb.h +++ b/include/usb.h @@ -216,7 +216,7 @@ int usb_host_eth_scan(int mode); #ifdef CONFIG_USB_KEYBOARD int drv_usb_kbd_init(void); -int usb_kbd_deregister(void); +int usb_kbd_deregister(int force); #endif /* routines */ From b9c99d32466b90aa2269eca99545f045a23266ab Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 11 Sep 2014 15:26:10 +0200 Subject: [PATCH 11/21] usb: dfu: thor: gadget: Remove dead code This code is not used anymore in the current DFU implementation and can be safely removed. Signed-off-by: Lukasz Majewski --- drivers/usb/gadget/f_dfu.c | 8 -------- drivers/usb/gadget/f_thor.c | 10 ---------- 2 files changed, 18 deletions(-) diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 3e4f02932b9d..d040606e6f1d 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -81,14 +81,6 @@ static struct usb_descriptor_header *dfu_runtime_descs[] = { NULL, }; -static const struct usb_qualifier_descriptor dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - .bcdUSB = __constant_cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_VENDOR_SPEC, - .bNumConfigurations = 1, -}; - static const char dfu_name[] = "Device Firmware Upgrade"; /* diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index c85b0fbd3ca5..78519fa41ff4 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -458,16 +458,6 @@ static struct usb_endpoint_descriptor hs_int_desc = { .bInterval = 0x9, }; -static struct usb_qualifier_descriptor dev_qualifier = { - .bLength = sizeof(dev_qualifier), - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = __constant_cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_VENDOR_SPEC, - - .bNumConfigurations = 2, -}; - /* * This attribute vendor descriptor is necessary for correct operation with * Windows version of THOR download program From 9f3b8ed14cef1e277f1362f121e16975bba2026f Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Wed, 10 Sep 2014 08:55:00 +0200 Subject: [PATCH 12/21] usb: dfu: add fullspeed support for DFU DFU now can use also fullspeed. Signed-off-by: Heiko Schocher Cc: Tom Rini Cc: Lukasz Majewski Cc: Marek Vasut Cc: Liu Bin Cc: Lukas Stockmann --- drivers/usb/gadget/f_dfu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index d040606e6f1d..16fc9ddf82bd 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -229,6 +229,7 @@ static inline void to_dfu_mode(struct f_dfu *f_dfu) { f_dfu->usb_function.strings = dfu_strings; f_dfu->usb_function.hs_descriptors = f_dfu->function; + f_dfu->usb_function.descriptors = f_dfu->function; f_dfu->dfu_state = DFU_STATE_dfuIDLE; } @@ -236,6 +237,7 @@ static inline void to_runtime_mode(struct f_dfu *f_dfu) { f_dfu->usb_function.strings = NULL; f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.descriptors = dfu_runtime_descs; } static int handle_upload(struct usb_request *req, u16 len) @@ -800,6 +802,7 @@ static int dfu_bind_config(struct usb_configuration *c) return -ENOMEM; f_dfu->usb_function.name = "dfu"; f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.descriptors = dfu_runtime_descs; f_dfu->usb_function.bind = dfu_bind; f_dfu->usb_function.unbind = dfu_unbind; f_dfu->usb_function.set_alt = dfu_set_alt; From ece91016c46b9945485b41f50589ea92f1f04ce1 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Wed, 10 Sep 2014 08:55:01 +0200 Subject: [PATCH 13/21] arm: am335x: siemens board use in DFU mode fullspeed only Siemens boards are now using DFU in fullspeed only. For this CONFIG_USB_GADGET_DUALSPEED is undefined. Signed-off-by: Heiko Schocher Cc: Tom Rini Cc: Lukasz Majewski Cc: Marek Vasut Cc: Liu Bin Cc: Lukas Stockmann --- include/configs/siemens-am33x-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/configs/siemens-am33x-common.h b/include/configs/siemens-am33x-common.h index bf9752f87424..1ce0965912b1 100644 --- a/include/configs/siemens-am33x-common.h +++ b/include/configs/siemens-am33x-common.h @@ -231,7 +231,7 @@ #define CONFIG_MUSB_GADGET #define CONFIG_MUSB_PIO_ONLY #define CONFIG_MUSB_DISABLE_BULK_COMBINE_SPLIT -#define CONFIG_USB_GADGET_DUALSPEED +#undef CONFIG_USB_GADGET_DUALSPEED #define CONFIG_USB_GADGET_VBUS_DRAW 2 #define CONFIG_MUSB_HOST From f9935c87b6c8e81ef8d621bd06d24a78eec8a821 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 19 Sep 2014 17:06:46 -0700 Subject: [PATCH 14/21] usb: f_mass_storage: set removable flag in do_inquiry based on LUN Without this flag, tools like Alex Page's USB Image Tool won't see drives exposed over USB Gadget as removable, and won't allow access to them. http://www.alexpage.de/usb-image-tool/ The code was pulled from the main-line kernel: drivers/usb/gadget/function/f_mass_storage.c Signed-off-by: Eric Nelson --- drivers/usb/gadget/f_mass_storage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index f274d9679fb9..e045957d0723 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1110,6 +1110,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) memset(buf, 0, 8); buf[0] = TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; buf[2] = 2; /* ANSI SCSI level 2 */ buf[3] = 2; /* SCSI-2 INQUIRY data format */ buf[4] = 31; /* Additional length */ From 23d1d10c4248f1b9f838e512e7f18e600f06b348 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Fri, 19 Sep 2014 14:15:12 +0800 Subject: [PATCH 15/21] usb: gadget: fastboot: improve download progress bar When download is ongoing, if the actual size of one transfer is not the same as BYTES_PER_DOT, which will cause the dot won't print anymore. Then it will let the user thinking it is stuck, actually it is transfering without dot printed. So, improve the method to show the progress bar (print dot). Signed-off-by: Bo Shen Acked-by: Marek Vasut --- drivers/usb/gadget/f_fastboot.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 38c09658cc6a..9d15f6352e77 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -386,6 +386,7 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) 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; if (req->status != 0) { printf("Bad status: %d\n", req->status); @@ -398,7 +399,15 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes, buffer, transfer_size); + pre_dot_num = download_bytes / BYTES_PER_DOT; download_bytes += transfer_size; + now_dot_num = download_bytes / BYTES_PER_DOT; + + if (pre_dot_num != now_dot_num) { + putc('.'); + if (!(now_dot_num % 74)) + putc('\n'); + } /* Check if transfer is done */ if (download_bytes >= download_size) { @@ -420,11 +429,6 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) 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); } From e206799370f77097a29577599960b7f123f61b8c Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Sun, 28 Sep 2014 18:35:14 -0700 Subject: [PATCH 16/21] usb: ci_udc: respect CONFIG_USB_GADGET_DUALSPEED Force full-speed (12 Mbit/s) operation if CONFIG_USB_GADGET_DUALSPEED is not defined. The controller is capable of high-speed (480 Mbit/s) operation, but some designs may require the use of lower-speed operation. Signed-off-by: Eric Nelson --- drivers/usb/gadget/ci_udc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 2572b346ebfa..b0ef35e745a3 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -777,6 +777,11 @@ static int ci_pullup(struct usb_gadget *gadget, int is_on) /* select DEVICE mode */ writel(USBMODE_DEVICE, &udc->usbmode); +#if !defined(CONFIG_USB_GADGET_DUALSPEED) + /* Port force Full-Speed Connect */ + setbits_le32(&udc->portsc, PFSC); +#endif + writel(0xffffffff, &udc->epflush); /* Turn on the USB connection by enabling the pullup resistor */ From c674a6660e9934cf332bb007726997b1c3686bf1 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 30 Sep 2014 12:05:40 -0700 Subject: [PATCH 17/21] usb: gadget: fastboot: add max-download-size variable Current Android Fastboot seems to use 'max-download-size' instead of 'downloadsize' variable to indicate the maximum size of sparse segments. See function get_target_sparse_limit() in file fastboot/fastboot.c in the AOSP: https://android.googlesource.com/platform/system/core/+/master Signed-off-by: Eric Nelson --- drivers/usb/gadget/f_fastboot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 9d15f6352e77..3e1c0a9faf29 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -351,7 +351,8 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) strncat(response, FASTBOOT_VERSION, chars_left); } else if (!strcmp_l1("bootloader-version", cmd)) { strncat(response, U_BOOT_VERSION, chars_left); - } else if (!strcmp_l1("downloadsize", cmd)) { + } else if (!strcmp_l1("downloadsize", cmd) || + !strcmp_l1("max-download-size", cmd)) { char str_num[12]; sprintf(str_num, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE); From 84c24f66c2971f2a4149f60228bfc08f29ba9d30 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 30 Sep 2014 12:05:41 -0700 Subject: [PATCH 18/21] usb: gadget: fastboot: explicitly set radix of maximum download size The processing of the max-download-size variable requires a radix specifier, or the fastboot host tool will interpret it as an octal number. See function get_target_sparse_limit() in file fastboot/fastboot.c in the AOSP: https://android.googlesource.com/platform/system/core/+/master Signed-off-by: Eric Nelson --- drivers/usb/gadget/f_fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 3e1c0a9faf29..392379dce418 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -355,7 +355,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, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE); + sprintf(str_num, "0x%08x", CONFIG_USB_FASTBOOT_BUF_SIZE); strncat(response, str_num, chars_left); } else if (!strcmp_l1("serialno", cmd)) { s = getenv("serial#"); From d1fcbae1173f00917f5a71e4c074a61120605021 Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Sat, 4 Oct 2014 01:46:10 +0200 Subject: [PATCH 19/21] usb: tegra: ULPI regression on tegra20 Trying to enumerate USB devices connected via ULPI to T20 failed as follows: USB2: ULPI integrity check failed Git bisecting revealed the following commit being at odds: commit 2d34151f7501ddaa599897f0d89ad576126b03eb usb: tegra: refactor PHY type selection Looking at above commit one quickly identifies a copy paste error which this patch fixes. Happy ULPIing again (;-p). Signed-off-by: Marcel Ziswiler --- drivers/usb/host/ehci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 33e5ea9ebdd0..5f0a98e8b80b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -305,11 +305,11 @@ static void init_phy_mux(struct fdt_usb *config, uint pts, #if defined(CONFIG_TEGRA20) if (config->periph_id == PERIPH_ID_USBD) { clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK, - PTS_UTMI << PTS1_SHIFT); + pts << PTS1_SHIFT); clrbits_le32(&usbctlr->port_sc1, STS1); } else { clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, - PTS_UTMI << PTS_SHIFT); + pts << PTS_SHIFT); clrbits_le32(&usbctlr->port_sc1, STS); } #else From 16b61d13bab361853564da401b15fc34ae1dfea7 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 22 Jul 2014 12:47:02 +0200 Subject: [PATCH 20/21] usb: musb-new: core: set MUSB_POWER_HSENAB in MUSB_POWER for host mode This bit allows the MUSB controller to negotiate for high-speed mode when the device is reset by the hub. If unset, Babble errors occur with high-speed mass storage devices right after the first packet. This condition is not caught by the interrupt handles in U-Boot, so no recovery is done, and the USB communication is stuck. To fix this, set the bit unconditionally, not only for CONFIG_USB_GADGET_DUALSPEED but also for host-only modes. Signed-off-by: Daniel Mack --- drivers/usb/musb-new/musb_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c index 4edd6d729d1d..242cc30b1c2d 100644 --- a/drivers/usb/musb-new/musb_core.c +++ b/drivers/usb/musb-new/musb_core.c @@ -942,9 +942,7 @@ void musb_start(struct musb *musb) /* put into basic highspeed mode and start session */ musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE -#ifdef CONFIG_USB_GADGET_DUALSPEED | MUSB_POWER_HSENAB -#endif /* ENSUSPEND wedges tusb */ /* | MUSB_POWER_ENSUSPEND */ ); From e2140588dd2f3e619f21d9575281b7c7ea771c09 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 1 Oct 2014 14:30:56 -0700 Subject: [PATCH 21/21] usb: gadget: fastboot: terminate commands with NULL Without NULL termination, various commands will read past the end of input. In particular, this was noticed with error() calls in cb_getvar and simple_strtoul() in cb_download. Since the download callback happens elsewhere, the 4k buffer should always be sufficient to handle command arguments. Signed-off-by: Eric Nelson --- drivers/usb/gadget/f_fastboot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 392379dce418..71b62e5005a1 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -546,7 +546,14 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) error("unknown command: %s\n", cmdbuf); fastboot_tx_write_str("FAILunknown command"); } else { - func_cb(ep, req); + if (req->actual < req->length) { + u8 *buf = (u8 *)req->buf; + buf[req->actual] = 0; + func_cb(ep, req); + } else { + error("buffer overflow\n"); + fastboot_tx_write_str("FAILbuffer overflow"); + } } if (req->status == 0) {