diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-16 15:04:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-16 15:04:26 -0700 |
commit | 971f115a50afbe409825c9f3399d5a3b9aca4381 (patch) | |
tree | cb42dc07a032e325f22b64d961587c081225c6d6 /drivers/usb/core | |
parent | 2e270d84223262a38d4755c61d55f5c73ea89e56 (diff) | |
parent | 500132a0f26ad7d9916102193cbc6c1b1becb373 (diff) | |
download | kernel_samsung_tuna-971f115a50afbe409825c9f3399d5a3b9aca4381.zip kernel_samsung_tuna-971f115a50afbe409825c9f3399d5a3b9aca4381.tar.gz kernel_samsung_tuna-971f115a50afbe409825c9f3399d5a3b9aca4381.tar.bz2 |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (172 commits)
USB: Add support for SuperSpeed isoc endpoints
xhci: Clean up cycle bit math used during stalls.
xhci: Fix cycle bit calculation during stall handling.
xhci: Update internal dequeue pointers after stalls.
USB: Disable auto-suspend for USB 3.0 hubs.
USB: Remove bogus USB_PORT_STAT_SUPER_SPEED symbol.
xhci: Return canceled URBs immediately when host is halted.
xhci: Fixes for suspend/resume of shared HCDs.
xhci: Fix re-init on power loss after resume.
xhci: Make roothub functions deal with device removal.
xhci: Limit roothub ports to 15 USB3 & 31 USB2 ports.
xhci: Return a USB 3.0 hub descriptor for USB3 roothub.
xhci: Register second xHCI roothub.
xhci: Change xhci_find_slot_id_by_port() API.
xhci: Refactor bus suspend state into a struct.
xhci: Index with a port array instead of PORTSC addresses.
USB: Set usb_hcd->state and flags for shared roothubs.
usb: Make core allocate resources per PCI-device.
usb: Store bus type in usb_hcd, not in driver flags.
usb: Change usb_hcd->bandwidth_mutex to a pointer.
...
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/buffer.c | 26 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 56 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 281 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 199 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 22 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 11 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 13 |
8 files changed, 433 insertions, 180 deletions
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 2c69654..b0585e6 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -10,7 +10,7 @@ #include <linux/slab.h> #include <linux/device.h> #include <linux/mm.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/usb.h> @@ -22,7 +22,7 @@ */ /* FIXME tune these based on pool statistics ... */ -static const size_t pool_max [HCD_BUFFER_POOLS] = { +static const size_t pool_max[HCD_BUFFER_POOLS] = { /* platforms without dma-friendly caches might need to * prevent cacheline sharing... */ @@ -51,7 +51,7 @@ static const size_t pool_max [HCD_BUFFER_POOLS] = { int hcd_buffer_create(struct usb_hcd *hcd) { char name[16]; - int i, size; + int i, size; if (!hcd->self.controller->dma_mask && !(hcd->driver->flags & HCD_LOCAL_MEM)) @@ -64,7 +64,7 @@ int hcd_buffer_create(struct usb_hcd *hcd) snprintf(name, sizeof name, "buffer-%d", size); hcd->pool[i] = dma_pool_create(name, hcd->self.controller, size, size, 0); - if (!hcd->pool [i]) { + if (!hcd->pool[i]) { hcd_buffer_destroy(hcd); return -ENOMEM; } @@ -99,14 +99,14 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) */ void *hcd_buffer_alloc( - struct usb_bus *bus, + struct usb_bus *bus, size_t size, gfp_t mem_flags, dma_addr_t *dma ) { struct usb_hcd *hcd = bus_to_hcd(bus); - int i; + int i; /* some USB hosts just use PIO */ if (!bus->controller->dma_mask && @@ -116,21 +116,21 @@ void *hcd_buffer_alloc( } for (i = 0; i < HCD_BUFFER_POOLS; i++) { - if (size <= pool_max [i]) - return dma_pool_alloc(hcd->pool [i], mem_flags, dma); + if (size <= pool_max[i]) + return dma_pool_alloc(hcd->pool[i], mem_flags, dma); } return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags); } void hcd_buffer_free( - struct usb_bus *bus, + struct usb_bus *bus, size_t size, - void *addr, + void *addr, dma_addr_t dma ) { struct usb_hcd *hcd = bus_to_hcd(bus); - int i; + int i; if (!addr) return; @@ -142,8 +142,8 @@ void hcd_buffer_free( } for (i = 0; i < HCD_BUFFER_POOLS; i++) { - if (size <= pool_max [i]) { - dma_pool_free(hcd->pool [i], addr, dma); + if (size <= pool_max[i]) { + dma_pool_free(hcd->pool[i], addr, dma); return; } } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index fca6172..38072e4 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1659,6 +1659,11 @@ static int usb_runtime_suspend(struct device *dev) return -EAGAIN; status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); + /* The PM core reacts badly unless the return code is 0, + * -EAGAIN, or -EBUSY, so always return -EBUSY on an error. + */ + if (status != 0) + return -EBUSY; return status; } diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 64a035b..ce22f4a 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -192,13 +192,13 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", pci_name(dev)); retval = -ENODEV; - goto err1; + goto disable_pci; } hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); if (!hcd) { retval = -ENOMEM; - goto err1; + goto disable_pci; } if (driver->flags & HCD_MEMORY) { @@ -209,13 +209,13 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) driver->description)) { dev_dbg(&dev->dev, "controller already in use\n"); retval = -EBUSY; - goto err2; + goto clear_companion; } hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (hcd->regs == NULL) { dev_dbg(&dev->dev, "error mapping memory\n"); retval = -EFAULT; - goto err3; + goto release_mem_region; } } else { @@ -236,7 +236,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (region == PCI_ROM_RESOURCE) { dev_dbg(&dev->dev, "no i/o regions available\n"); retval = -EBUSY; - goto err2; + goto clear_companion; } } @@ -244,24 +244,24 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) - goto err4; + goto unmap_registers; set_hs_companion(dev, hcd); if (pci_dev_run_wake(dev)) pm_runtime_put_noidle(&dev->dev); return retval; - err4: +unmap_registers: if (driver->flags & HCD_MEMORY) { iounmap(hcd->regs); - err3: +release_mem_region: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); } else release_region(hcd->rsrc_start, hcd->rsrc_len); - err2: +clear_companion: clear_hs_companion(dev, hcd); usb_put_hcd(hcd); - err1: +disable_pci: pci_disable_device(dev); dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval); return retval; @@ -363,11 +363,17 @@ static int check_root_hub_suspended(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); - if (!(hcd->state == HC_STATE_SUSPENDED || - hcd->state == HC_STATE_HALT)) { + if (HCD_RH_RUNNING(hcd)) { dev_warn(dev, "Root hub is not suspended\n"); return -EBUSY; } + if (hcd->shared_hcd) { + hcd = hcd->shared_hcd; + if (HCD_RH_RUNNING(hcd)) { + dev_warn(dev, "Secondary root hub is not suspended\n"); + return -EBUSY; + } + } return 0; } @@ -386,17 +392,22 @@ static int suspend_common(struct device *dev, bool do_wakeup) if (retval) return retval; - if (hcd->driver->pci_suspend) { + if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) { /* Optimization: Don't suspend if a root-hub wakeup is * pending and it would cause the HCD to wake up anyway. */ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) return -EBUSY; + if (do_wakeup && hcd->shared_hcd && + HCD_WAKEUP_PENDING(hcd->shared_hcd)) + return -EBUSY; retval = hcd->driver->pci_suspend(hcd, do_wakeup); suspend_report_result(hcd->driver->pci_suspend, retval); /* Check again in case wakeup raced with pci_suspend */ - if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) || + (retval == 0 && do_wakeup && hcd->shared_hcd && + HCD_WAKEUP_PENDING(hcd->shared_hcd))) { if (hcd->driver->pci_resume) hcd->driver->pci_resume(hcd, false); retval = -EBUSY; @@ -427,7 +438,9 @@ static int resume_common(struct device *dev, int event) struct usb_hcd *hcd = pci_get_drvdata(pci_dev); int retval; - if (hcd->state != HC_STATE_SUSPENDED) { + if (HCD_RH_RUNNING(hcd) || + (hcd->shared_hcd && + HCD_RH_RUNNING(hcd->shared_hcd))) { dev_dbg(dev, "can't resume, not suspended!\n"); return 0; } @@ -441,8 +454,10 @@ static int resume_common(struct device *dev, int event) pci_set_master(pci_dev); clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + if (hcd->shared_hcd) + clear_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags); - if (hcd->driver->pci_resume) { + if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { if (event != PM_EVENT_AUTO_RESUME) wait_for_companions(pci_dev, hcd); @@ -450,6 +465,8 @@ static int resume_common(struct device *dev, int event) event == PM_EVENT_RESTORE); if (retval) { dev_err(dev, "PCI post-resume error %d!\n", retval); + if (hcd->shared_hcd) + usb_hc_died(hcd->shared_hcd); usb_hc_died(hcd); } } @@ -475,10 +492,11 @@ static int hcd_pci_suspend_noirq(struct device *dev) pci_save_state(pci_dev); - /* If the root hub is HALTed rather than SUSPENDed, - * disallow remote wakeup. + /* If the root hub is dead rather than suspended, disallow remote + * wakeup. usb_hc_died() should ensure that both hosts are marked as + * dying, so we only need to check the primary roothub. */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) device_set_wakeup_enable(dev, 0); dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e935f71..02b4dbf 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -297,7 +297,7 @@ static const u8 ss_rh_config_descriptor[] = { /* one configuration */ 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* __le16 wTotalLength; FIXME */ + 0x1f, 0x00, /* __le16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ @@ -327,11 +327,14 @@ static const u8 ss_rh_config_descriptor[] = { /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) * see hub.c:hub_configure() for details. */ (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, - 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ - /* - * All 3.0 hubs should have an endpoint companion descriptor, - * but we're ignoring that for now. FIXME? - */ + 0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ + + /* one SuperSpeed endpoint companion descriptor */ + 0x06, /* __u8 ss_bLength */ + 0x30, /* __u8 ss_bDescriptorType; SuperSpeed EP Companion */ + 0x00, /* __u8 ss_bMaxBurst; allows 1 TX between ACKs */ + 0x00, /* __u8 ss_bmAttributes; 1 packet per service interval */ + 0x02, 0x00 /* __le16 ss_wBytesPerInterval; 15 bits for max 15 ports */ }; /*-------------------------------------------------------------------------*/ @@ -504,7 +507,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (wValue & 0xff00) { case USB_DT_DEVICE << 8: - switch (hcd->driver->flags & HCD_MASK) { + switch (hcd->speed) { case HCD_USB3: bufp = usb3_rh_dev_descriptor; break; @@ -522,7 +525,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) patch_protocol = 1; break; case USB_DT_CONFIG << 8: - switch (hcd->driver->flags & HCD_MASK) { + switch (hcd->speed) { case HCD_USB3: bufp = ss_rh_config_descriptor; len = sizeof ss_rh_config_descriptor; @@ -983,7 +986,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) usb_hc_died (hcd); /* This time clean up */ } @@ -1089,13 +1092,10 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) * Check the host controller's state and add the URB to the * endpoint's queue. */ - switch (hcd->state) { - case HC_STATE_RUNNING: - case HC_STATE_RESUMING: + if (HCD_RH_RUNNING(hcd)) { urb->unlinked = 0; list_add_tail(&urb->urb_list, &urb->ep->urb_list); - break; - default: + } else { rc = -ESHUTDOWN; goto done; } @@ -1153,6 +1153,8 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, dev_warn(hcd->self.controller, "Unlink after no-IRQ? " "Controller is probably using the wrong IRQ.\n"); set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + if (hcd->shared_hcd) + set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags); } return 0; @@ -1262,7 +1264,7 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) +void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) dma_unmap_single(hcd->self.controller, @@ -1279,13 +1281,21 @@ void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) /* Make it safe to call this routine more than once */ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL); } -EXPORT_SYMBOL_GPL(unmap_urb_setup_for_dma); +EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_setup_for_dma); -void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + if (hcd->driver->unmap_urb_for_dma) + hcd->driver->unmap_urb_for_dma(hcd, urb); + else + usb_hcd_unmap_urb_for_dma(hcd, urb); +} + +void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; - unmap_urb_setup_for_dma(hcd, urb); + usb_hcd_unmap_urb_setup_for_dma(hcd, urb); dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_flags & URB_DMA_MAP_SG) @@ -1314,11 +1324,20 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } -EXPORT_SYMBOL_GPL(unmap_urb_for_dma); +EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { + if (hcd->driver->map_urb_for_dma) + return hcd->driver->map_urb_for_dma(hcd, urb, mem_flags); + else + return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); +} + +int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ enum dma_data_direction dir; int ret = 0; @@ -1410,10 +1429,11 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL))) - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); } return ret; } +EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma); /*-------------------------------------------------------------------------*/ @@ -1913,7 +1933,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!HC_IS_RUNNING (hcd->state)) + if (!HCD_RH_RUNNING(hcd)) return -ESHUTDOWN; return hcd->driver->get_frame_number (hcd); } @@ -1930,9 +1950,15 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "bus %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend"); + return 0; + } + if (!hcd->driver->bus_suspend) { status = -ENOENT; } else { + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); hcd->state = HC_STATE_QUIESCING; status = hcd->driver->bus_suspend(hcd); } @@ -1940,7 +1966,12 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; } else { - hcd->state = old_state; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = old_state; + } + spin_unlock_irq(&hcd_root_hub_lock); dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "suspend", status); } @@ -1955,9 +1986,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume"); + return 0; + } if (!hcd->driver->bus_resume) return -ENOENT; - if (hcd->state == HC_STATE_RUNNING) + if (HCD_RH_RUNNING(hcd)) return 0; hcd->state = HC_STATE_RESUMING; @@ -1966,10 +2001,15 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); - usb_set_device_state(rhdev, rhdev->actconfig - ? USB_STATE_CONFIGURED - : USB_STATE_ADDRESS); - hcd->state = HC_STATE_RUNNING; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + usb_set_device_state(rhdev, rhdev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + } + spin_unlock_irq(&hcd_root_hub_lock); } else { hcd->state = old_state; dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", @@ -2080,12 +2120,14 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) */ local_irq_save(flags); - if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) { + if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) { rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; } else { set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + if (hcd->shared_hcd) + set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags); if (unlikely(hcd->state == HC_STATE_HALT)) usb_hc_died(hcd); @@ -2105,7 +2147,9 @@ EXPORT_SYMBOL_GPL(usb_hcd_irq); * * This is called by bus glue to report a USB host controller that died * while operations may still have been pending. It's called automatically - * by the PCI glue, so only glue for non-PCI busses should need to call it. + * by the PCI glue, so only glue for non-PCI busses should need to call it. + * + * Only call this function with the primary HCD. */ void usb_hc_died (struct usb_hcd *hcd) { @@ -2114,6 +2158,8 @@ void usb_hc_died (struct usb_hcd *hcd) dev_err (hcd->self.controller, "HC died; cleaning up\n"); spin_lock_irqsave (&hcd_root_hub_lock, flags); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + set_bit(HCD_FLAG_DEAD, &hcd->flags); if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2122,17 +2168,31 @@ void usb_hc_died (struct usb_hcd *hcd) USB_STATE_NOTATTACHED); usb_kick_khubd (hcd->self.root_hub); } + if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) { + hcd = hcd->shared_hcd; + if (hcd->rh_registered) { + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + + /* make khubd clean up old urbs and devices */ + usb_set_device_state(hcd->self.root_hub, + USB_STATE_NOTATTACHED); + usb_kick_khubd(hcd->self.root_hub); + } + } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); + /* Make sure that the other roothub is also deallocated. */ } EXPORT_SYMBOL_GPL (usb_hc_died); /*-------------------------------------------------------------------------*/ /** - * usb_create_hcd - create and initialize an HCD structure + * usb_create_shared_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd * @dev: device for this HC, stored in hcd->self.controller * @bus_name: value to store in hcd->self.bus_name + * @primary_hcd: a pointer to the usb_hcd structure that is sharing the + * PCI device. Only allocate certain resources for the primary HCD * Context: !in_interrupt() * * Allocate a struct usb_hcd, with extra space at the end for the @@ -2141,8 +2201,9 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * * If memory is unavailable, returns NULL. */ -struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, - struct device *dev, const char *bus_name) +struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, + struct device *dev, const char *bus_name, + struct usb_hcd *primary_hcd) { struct usb_hcd *hcd; @@ -2151,7 +2212,24 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, dev_dbg (dev, "hcd alloc failed\n"); return NULL; } - dev_set_drvdata(dev, hcd); + if (primary_hcd == NULL) { + hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), + GFP_KERNEL); + if (!hcd->bandwidth_mutex) { + kfree(hcd); + dev_dbg(dev, "hcd bandwidth mutex alloc failed\n"); + return NULL; + } + mutex_init(hcd->bandwidth_mutex); + dev_set_drvdata(dev, hcd); + } else { + hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; + hcd->primary_hcd = primary_hcd; + primary_hcd->primary_hcd = primary_hcd; + hcd->shared_hcd = primary_hcd; + primary_hcd->shared_hcd = hcd; + } + kref_init(&hcd->kref); usb_bus_init(&hcd->self); @@ -2165,19 +2243,53 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, #ifdef CONFIG_USB_SUSPEND INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif - mutex_init(&hcd->bandwidth_mutex); hcd->driver = driver; + hcd->speed = driver->flags & HCD_MASK; hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller"; return hcd; } +EXPORT_SYMBOL_GPL(usb_create_shared_hcd); + +/** + * usb_create_hcd - create and initialize an HCD structure + * @driver: HC driver that will use this hcd + * @dev: device for this HC, stored in hcd->self.controller + * @bus_name: value to store in hcd->self.bus_name + * Context: !in_interrupt() + * + * Allocate a struct usb_hcd, with extra space at the end for the + * HC driver's private data. Initialize the generic members of the + * hcd structure. + * + * If memory is unavailable, returns NULL. + */ +struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, + struct device *dev, const char *bus_name) +{ + return usb_create_shared_hcd(driver, dev, bus_name, NULL); +} EXPORT_SYMBOL_GPL(usb_create_hcd); +/* + * Roothubs that share one PCI device must also share the bandwidth mutex. + * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is + * deallocated. + * + * Make sure to only deallocate the bandwidth_mutex when the primary HCD is + * freed. When hcd_release() is called for the non-primary HCD, set the + * primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be + * freed shortly). + */ static void hcd_release (struct kref *kref) { struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); + if (usb_hcd_is_primary_hcd(hcd)) + kfree(hcd->bandwidth_mutex); + else + hcd->shared_hcd->shared_hcd = NULL; kfree(hcd); } @@ -2196,6 +2308,54 @@ void usb_put_hcd (struct usb_hcd *hcd) } EXPORT_SYMBOL_GPL(usb_put_hcd); +int usb_hcd_is_primary_hcd(struct usb_hcd *hcd) +{ + if (!hcd->primary_hcd) + return 1; + return hcd == hcd->primary_hcd; +} +EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd); + +static int usb_hcd_request_irqs(struct usb_hcd *hcd, + unsigned int irqnum, unsigned long irqflags) +{ + int retval; + + if (hcd->driver->irq) { + + /* IRQF_DISABLED doesn't work as advertised when used together + * with IRQF_SHARED. As usb_hcd_irq() will always disable + * interrupts we can remove it here. + */ + if (irqflags & IRQF_SHARED) + irqflags &= ~IRQF_DISABLED; + + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", + hcd->driver->description, hcd->self.busnum); + retval = request_irq(irqnum, &usb_hcd_irq, irqflags, + hcd->irq_descr, hcd); + if (retval != 0) { + dev_err(hcd->self.controller, + "request interrupt %d failed\n", + irqnum); + return retval; + } + hcd->irq = irqnum; + dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum, + (hcd->driver->flags & HCD_MEMORY) ? + "io mem" : "io base", + (unsigned long long)hcd->rsrc_start); + } else { + hcd->irq = -1; + if (hcd->rsrc_start) + dev_info(hcd->self.controller, "%s 0x%08llx\n", + (hcd->driver->flags & HCD_MEMORY) ? + "io mem" : "io base", + (unsigned long long)hcd->rsrc_start); + } + return 0; +} + /** * usb_add_hcd - finish generic HCD structure initialization and register * @hcd: the usb_hcd structure to initialize @@ -2236,7 +2396,7 @@ int usb_add_hcd(struct usb_hcd *hcd, } hcd->self.root_hub = rhdev; - switch (hcd->driver->flags & HCD_MASK) { + switch (hcd->speed) { case HCD_USB11: rhdev->speed = USB_SPEED_FULL; break; @@ -2256,6 +2416,12 @@ int usb_add_hcd(struct usb_hcd *hcd, */ device_init_wakeup(&rhdev->dev, 1); + /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is + * registered. But since the controller can die at any time, + * let's initialize the flag before touching the hardware. + */ + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + /* "reset" is misnamed; its role is now one-time init. the controller * should already have been reset (and boot firmware kicked off etc). */ @@ -2271,38 +2437,15 @@ int usb_add_hcd(struct usb_hcd *hcd, dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); /* enable irqs just before we start the controller */ - if (hcd->driver->irq) { - - /* IRQF_DISABLED doesn't work as advertised when used together - * with IRQF_SHARED. As usb_hcd_irq() will always disable - * interrupts we can remove it here. - */ - if (irqflags & IRQF_SHARED) - irqflags &= ~IRQF_DISABLED; - - snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", - hcd->driver->description, hcd->self.busnum); - if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, - hcd->irq_descr, hcd)) != 0) { - dev_err(hcd->self.controller, - "request interrupt %d failed\n", irqnum); + if (usb_hcd_is_primary_hcd(hcd)) { + retval = usb_hcd_request_irqs(hcd, irqnum, irqflags); + if (retval) goto err_request_irq; - } - hcd->irq = irqnum; - dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum, - (hcd->driver->flags & HCD_MEMORY) ? - "io mem" : "io base", - (unsigned long long)hcd->rsrc_start); - } else { - hcd->irq = -1; - if (hcd->rsrc_start) - dev_info(hcd->self.controller, "%s 0x%08llx\n", - (hcd->driver->flags & HCD_MEMORY) ? - "io mem" : "io base", - (unsigned long long)hcd->rsrc_start); } - if ((retval = hcd->driver->start(hcd)) < 0) { + hcd->state = HC_STATE_RUNNING; + retval = hcd->driver->start(hcd); + if (retval < 0) { dev_err(hcd->self.controller, "startup error %d\n", retval); goto err_hcd_driver_start; } @@ -2323,6 +2466,7 @@ int usb_add_hcd(struct usb_hcd *hcd, return retval; error_create_attr_group: + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING(hcd->state)) hcd->state = HC_STATE_QUIESCING; spin_lock_irq(&hcd_root_hub_lock); @@ -2344,7 +2488,7 @@ err_register_root_hub: clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); err_hcd_driver_start: - if (hcd->irq >= 0) + if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0) free_irq(irqnum, hcd); err_request_irq: err_hcd_driver_setup: @@ -2375,6 +2519,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_get_dev(rhdev); sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; @@ -2407,8 +2552,10 @@ void usb_remove_hcd(struct usb_hcd *hcd) clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); - if (hcd->irq >= 0) - free_irq(hcd->irq, hcd); + if (usb_hcd_is_primary_hcd(hcd)) { + if (hcd->irq >= 0) + free_irq(hcd->irq, hcd); + } usb_put_dev(hcd->self.root_hub); usb_deregister_bus(&hcd->self); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 19d3435..564eaa5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -82,6 +82,10 @@ struct usb_hub { void **port_owners; }; +static inline int hub_is_superspeed(struct usb_device *hdev) +{ + return (hdev->descriptor.bDeviceProtocol == 3); +} /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can @@ -151,14 +155,14 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); static int usb_reset_and_verify_device(struct usb_device *udev); -static inline char *portspeed(int portstatus) +static inline char *portspeed(struct usb_hub *hub, int portstatus) { + if (hub_is_superspeed(hub->hdev)) + return "5.0 Gb/s"; if (portstatus & USB_PORT_STAT_HIGH_SPEED) return "480 Mb/s"; else if (portstatus & USB_PORT_STAT_LOW_SPEED) return "1.5 Mb/s"; - else if (portstatus & USB_PORT_STAT_SUPER_SPEED) - return "5.0 Gb/s"; else return "12 Mb/s"; } @@ -172,14 +176,23 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev) } /* USB 2.0 spec Section 11.24.4.5 */ -static int get_hub_descriptor(struct usb_device *hdev, void *data, int size) +static int get_hub_descriptor(struct usb_device *hdev, void *data) { - int i, ret; + int i, ret, size; + unsigned dtype; + + if (hub_is_superspeed(hdev)) { + dtype = USB_DT_SS_HUB; + size = USB_DT_SS_HUB_SIZE; + } else { + dtype = USB_DT_HUB; + size = sizeof(struct usb_hub_descriptor); + } for (i = 0; i < 3; i++) { ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - USB_DT_HUB << 8, 0, data, size, + dtype << 8, 0, data, size, USB_CTRL_GET_TIMEOUT); if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) return ret; @@ -365,6 +378,16 @@ static int hub_port_status(struct usb_hub *hub, int port1, } else { *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); + + if ((hub->hdev->parent != NULL) && + hub_is_superspeed(hub->hdev)) { + /* Translate the USB 3 port status */ + u16 tmp = *status & USB_SS_PORT_STAT_MASK; + if (*status & USB_SS_PORT_STAT_POWER) + tmp |= USB_PORT_STAT_POWER; + *status = tmp; + } + ret = 0; } mutex_unlock(&hub->status_mutex); @@ -607,7 +630,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) if (hdev->children[port1-1] && set_state) usb_set_device_state(hdev->children[port1-1], USB_STATE_NOTATTACHED); - if (!hub->error) + if (!hub->error && !hub_is_superspeed(hub->hdev)) ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", @@ -616,7 +639,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) } /* - * Disable a port and mark a logical connnect-change event, so that some + * Disable a port and mark a logical connect-change event, so that some * time later khubd will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. */ @@ -769,12 +792,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * USB3 protocol ports will automatically transition * to Enabled state when detect an USB3.0 device attach. * Do not disable USB3 protocol ports. - * FIXME: USB3 root hub and external hubs are treated - * differently here. */ - if (hdev->descriptor.bDeviceProtocol != 3 || - (!hdev->parent && - !(portstatus & USB_PORT_STAT_SUPER_SPEED))) { + if (!hub_is_superspeed(hdev)) { clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); portstatus &= ~USB_PORT_STAT_ENABLE; @@ -795,6 +814,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_ENABLE); } + if (portchange & USB_PORT_STAT_C_LINK_STATE) { + need_debounce_delay = true; + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } /* We can forget about a "removed" device when there's a * physical disconnect or the connect status changes. @@ -964,12 +988,23 @@ static int hub_configure(struct usb_hub *hub, goto fail; } + if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) { + ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + HUB_SET_DEPTH, USB_RT_HUB, + hdev->level - 1, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + + if (ret < 0) { + message = "can't set hub depth"; + goto fail; + } + } + /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, * but the hub can/will return fewer bytes here. */ - ret = get_hub_descriptor(hdev, hub->descriptor, - sizeof(*hub->descriptor)); + ret = get_hub_descriptor(hdev, hub->descriptor); if (ret < 0) { message = "can't read hub descriptor"; goto fail; @@ -991,12 +1026,14 @@ static int hub_configure(struct usb_hub *hub, wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); - if (wHubCharacteristics & HUB_CHAR_COMPOUND) { + /* FIXME for USB 3.0, skip for now */ + if ((wHubCharacteristics & HUB_CHAR_COMPOUND) && + !(hub_is_superspeed(hdev))) { int i; char portstr [USB_MAXCHILDREN + 1]; for (i = 0; i < hdev->maxchild; i++) - portstr[i] = hub->descriptor->DeviceRemovable + portstr[i] = hub->descriptor->u.hs.DeviceRemovable [((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; portstr[hdev->maxchild] = 0; @@ -1253,8 +1290,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); - /* Hubs have proper suspend/resume support */ - usb_enable_autosuspend(hdev); + /* Hubs have proper suspend/resume support. USB 3.0 device suspend is + * different from USB 2.0/1.1 device suspend, and unfortunately we + * don't support it yet. So leave autosuspend disabled for USB 3.0 + * external hubs for now. Enable autosuspend for USB 3.0 roothubs, + * since that isn't a "real" hub. + */ + if (!hub_is_superspeed(hdev) || !hdev->parent) + usb_enable_autosuspend(hdev); if (hdev->level == MAX_TOPO_LEVEL) { dev_err(&intf->dev, @@ -1501,6 +1544,13 @@ void usb_set_device_state(struct usb_device *udev, EXPORT_SYMBOL_GPL(usb_set_device_state); /* + * Choose a device number. + * + * Device numbers are used as filenames in usbfs. On USB-1.1 and + * USB-2.0 buses they are also used as device addresses, however on + * USB-3.0 buses the address is assigned by the controller hardware + * and it usually is not the same as the device number. + * * WUSB devices are simple: they have no hubs behind, so the mapping * device <-> virtual port number becomes 1:1. Why? to simplify the * life of the device connection logic in @@ -1522,7 +1572,7 @@ EXPORT_SYMBOL_GPL(usb_set_device_state); * the HCD must setup data structures before issuing a set address * command to the hardware. */ -static void choose_address(struct usb_device *udev) +static void choose_devnum(struct usb_device *udev) { int devnum; struct usb_bus *bus = udev->bus; @@ -1547,7 +1597,7 @@ static void choose_address(struct usb_device *udev) } } -static void release_address(struct usb_device *udev) +static void release_devnum(struct usb_device *udev) { if (udev->devnum > 0) { clear_bit(udev->devnum, udev->bus->devmap.devicemap); @@ -1555,7 +1605,7 @@ static void release_address(struct usb_device *udev) } } -static void update_address(struct usb_device *udev, int devnum) +static void update_devnum(struct usb_device *udev, int devnum) { /* The address for a WUSB device is managed by wusbcore. */ if (!udev->wusb) @@ -1602,7 +1652,8 @@ void usb_disconnect(struct usb_device **pdev) * this quiesces everyting except pending urbs. */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); - dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); + dev_info(&udev->dev, "USB disconnect, device number %d\n", + udev->devnum); usb_lock_device(udev); @@ -1632,7 +1683,7 @@ void usb_disconnect(struct usb_device **pdev) /* Free the device number and delete the parent's children[] * (or root_hub) pointer. */ - release_address(udev); + release_devnum(udev); /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); @@ -2017,7 +2068,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, (portstatus & USB_PORT_STAT_ENABLE)) { if (hub_is_wusb(hub)) udev->speed = USB_SPEED_WIRELESS; - else if (portstatus & USB_PORT_STAT_SUPER_SPEED) + else if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; else if (portstatus & USB_PORT_STAT_HIGH_SPEED) udev->speed = USB_SPEED_HIGH; @@ -2073,7 +2124,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, case 0: /* TRSTRCY = 10 ms; plus some extra */ msleep(10 + 40); - update_address(udev, 0); + update_devnum(udev, 0); if (hcd->driver->reset_device) { status = hcd->driver->reset_device(hcd, udev); if (status < 0) { @@ -2636,7 +2687,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval == 0) { - update_address(udev, devnum); + update_devnum(udev, devnum); /* Device now using proper address. */ usb_set_device_state(udev, USB_STATE_ADDRESS); usb_ep0_reinit(udev); @@ -2741,9 +2792,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, } if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, - "%s %s speed %sUSB device using %s and address %d\n", + "%s %s speed %sUSB device number %d using %s\n", (udev->config) ? "reset" : "new", speed, type, - udev->bus->controller->driver->name, devnum); + devnum, udev->bus->controller->driver->name); /* Set up TT records, if needed */ if (hdev->tt) { @@ -2773,10 +2824,6 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * value. */ for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { - /* - * An xHCI controller cannot send any packets to a device until - * a set address command successfully completes. - */ if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) { struct usb_device_descriptor *buf; int r = 0; @@ -2859,9 +2906,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (udev->speed == USB_SPEED_SUPER) { devnum = udev->devnum; dev_info(&udev->dev, - "%s SuperSpeed USB device using %s and address %d\n", + "%s SuperSpeed USB device number %d using %s\n", (udev->config) ? "reset" : "new", - udev->bus->controller->driver->name, devnum); + devnum, udev->bus->controller->driver->name); } /* cope with hardware quirkiness: @@ -2924,7 +2971,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, fail: if (retval) { hub_port_disable(hub, port1, 0); - update_address(udev, devnum); /* for disconnect processing */ + update_devnum(udev, devnum); /* for disconnect processing */ } mutex_unlock(&usb_address0_mutex); return retval; @@ -3015,7 +3062,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", - port1, portstatus, portchange, portspeed (portstatus)); + port1, portstatus, portchange, portspeed(hub, portstatus)); if (hub->has_indicators) { set_port_led(hub, port1, HUB_LED_AUTO); @@ -3116,32 +3163,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, udev->level = hdev->level + 1; udev->wusb = hub_is_wusb(hub); - /* - * USB 3.0 devices are reset automatically before the connect - * port status change appears, and the root hub port status - * shows the correct speed. We also get port change - * notifications for USB 3.0 devices from the USB 3.0 portion of - * an external USB 3.0 hub, but this isn't handled correctly yet - * FIXME. - */ - - if (!(hcd->driver->flags & HCD_USB3)) - udev->speed = USB_SPEED_UNKNOWN; - else if ((hdev->parent == NULL) && - (portstatus & USB_PORT_STAT_SUPER_SPEED)) + /* Only USB 3.0 devices are connected to SuperSpeed hubs. */ + if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; else udev->speed = USB_SPEED_UNKNOWN; - /* - * Set the address. - * Note xHCI needs to issue an address device command later - * in the hub_port_init sequence for SS/HS/FS/LS devices, - * and xHC will assign an address to the device. But use - * kernel assigned address here, to avoid any address conflict - * issue. - */ - choose_address(udev); + choose_devnum(udev); if (udev->devnum <= 0) { status = -ENOTCONN; /* Don't retry */ goto loop; @@ -3233,7 +3261,7 @@ loop_disable: hub_port_disable(hub, port1, 1); loop: usb_ep0_reinit(udev); - release_address(udev); + release_devnum(udev); hub_free_dev(udev); usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) @@ -3410,12 +3438,19 @@ static void hub_events(void) } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { - dev_err (hub_dev, - "over-current change on port %d\n", - i); + u16 status = 0; + u16 unused; + + dev_dbg(hub_dev, "over-current change on port " + "%d\n", i); clear_port_feature(hdev, i, USB_PORT_FEAT_C_OVER_CURRENT); + msleep(100); /* Cool down */ hub_power_on(hub, true); + hub_port_status(hub, i, &status, &unused); + if (status & USB_PORT_STAT_OVERCURRENT) + dev_err(hub_dev, "over-current " + "condition on port %d\n", i); } if (portchange & USB_PORT_STAT_C_RESET) { @@ -3425,6 +3460,25 @@ static void hub_events(void) clear_port_feature(hdev, i, USB_PORT_FEAT_C_RESET); } + if ((portchange & USB_PORT_STAT_C_BH_RESET) && + hub_is_superspeed(hub->hdev)) { + dev_dbg(hub_dev, + "warm reset change on port %d\n", + i); + clear_port_feature(hdev, i, + USB_PORT_FEAT_C_BH_PORT_RESET); + } + if (portchange & USB_PORT_STAT_C_LINK_STATE) { + clear_port_feature(hub->hdev, i, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } + if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) { + dev_warn(hub_dev, + "config error on port %d\n", + i); + clear_port_feature(hub->hdev, i, + USB_PORT_FEAT_C_PORT_CONFIG_ERROR); + } if (connect_change) hub_port_connect_change(hub, i, @@ -3447,10 +3501,17 @@ static void hub_events(void) hub->limited_power = 0; } if (hubchange & HUB_CHANGE_OVERCURRENT) { - dev_dbg (hub_dev, "overcurrent change\n"); - msleep(500); /* Cool down */ + u16 status = 0; + u16 unused; + + dev_dbg(hub_dev, "over-current change\n"); clear_hub_feature(hdev, C_HUB_OVER_CURRENT); + msleep(500); /* Cool down */ hub_power_on(hub, true); + hub_hub_status(hub, &status, &unused); + if (status & HUB_STATUS_OVERCURRENT) + dev_err(hub_dev, "over-current " + "condition\n"); } } @@ -3699,13 +3760,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev) if (!udev->actconfig) goto done; - mutex_lock(&hcd->bandwidth_mutex); + mutex_lock(hcd->bandwidth_mutex); ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); if (ret < 0) { dev_warn(&udev->dev, "Busted HC? Not enough HCD resources for " "old configuration.\n"); - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -3716,10 +3777,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev) dev_err(&udev->dev, "can't restore configuration #%d (error=%d)\n", udev->actconfig->desc.bConfigurationValue, ret); - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Put interfaces back into the same altsettings as before. diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 8324874..5701e85 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1284,12 +1284,12 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) /* Make sure we have enough bandwidth for this alternate interface. * Remove the current alt setting and add the new alt setting. */ - mutex_lock(&hcd->bandwidth_mutex); + mutex_lock(hcd->bandwidth_mutex); ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); if (ret < 0) { dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", alternate); - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); return ret; } @@ -1311,10 +1311,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) } else if (ret < 0) { /* Re-instate the old alt setting */ usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting); - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); return ret; } - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); /* FIXME drivers shouldn't need to replicate/bugfix the logic here * when they implement async or easily-killable versions of this or @@ -1413,7 +1413,7 @@ int usb_reset_configuration(struct usb_device *dev) config = dev->actconfig; retval = 0; - mutex_lock(&hcd->bandwidth_mutex); + mutex_lock(hcd->bandwidth_mutex); /* Make sure we have enough bandwidth for each alternate setting 0 */ for (i = 0; i < config->desc.bNumInterfaces; i++) { struct usb_interface *intf = config->interface[i]; @@ -1442,7 +1442,7 @@ reset_old_alts: usb_hcd_alloc_bandwidth(dev, NULL, alt, intf->cur_altsetting); } - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); return retval; } retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -1451,7 +1451,7 @@ reset_old_alts: NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval < 0) goto reset_old_alts; - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); /* re-init hc/hcd interface/endpoint state */ for (i = 0; i < config->desc.bNumInterfaces; i++) { @@ -1739,10 +1739,10 @@ free_interfaces: * host controller will not allow submissions to dropped endpoints. If * this call fails, the device state is unchanged. */ - mutex_lock(&hcd->bandwidth_mutex); + mutex_lock(hcd->bandwidth_mutex); ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; } @@ -1761,11 +1761,11 @@ free_interfaces: if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; } - mutex_unlock(&hcd->bandwidth_mutex); + mutex_unlock(hcd->bandwidth_mutex); usb_set_device_state(dev, USB_STATE_CONFIGURED); /* Initialize the new interface structures and the diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index c14fc08..ae334b0 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -366,7 +366,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (xfertype == USB_ENDPOINT_XFER_ISOC) { int n, len; - /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */ + /* SuperSpeed isoc endpoints have up to 16 bursts of up to + * 3 packets each + */ + if (dev->speed == USB_SPEED_SUPER) { + int burst = 1 + ep->ss_ep_comp.bMaxBurst; + int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes); + max *= burst; + max *= mult; + } + /* "high bandwidth" mode, 1-3 packets/uframe? */ if (dev->speed == USB_SPEED_HIGH) { int mult = 1 + ((max >> 11) & 0x03); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index b975450..a9cf484 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -122,6 +122,19 @@ static inline int is_usb_device_driver(struct device_driver *drv) for_devices; } +/* translate USB error codes to codes user space understands */ +static inline int usb_translate_errors(int error_code) +{ + switch (error_code) { + case 0: + case -ENOMEM: + case -ENODEV: + return error_code; + default: + return -EIO; + } +} + /* for labeling diagnostics */ extern const char *usbcore_name; |