diff options
author | Dan Williams <dan.j.williams@intel.com> | 2013-12-05 17:07:27 -0800 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2013-12-10 13:54:37 -0800 |
commit | 48fc7dbd52c0559647291f33a10ccdc6cdbe4c72 (patch) | |
tree | 4a2403420a69b2d40e38a53302771ec3d3eb6b66 /drivers/usb/host/xhci.c | |
parent | 40fcd88b8d49fd911518190c985112097d3a8a17 (diff) |
usb: xhci: change enumeration scheme to 'new scheme' by default
Change the default enumeration scheme for xhci attached non-SuperSpeed
devices from:
Reset
SetAddress [xhci address-device BSR = 0]
GetDescriptor(8)
GetDescriptor(18)
...to:
Reset
[xhci address-device BSR = 1]
GetDescriptor(64)
Reset
SetAddress [xhci address-device BSR = 0]
GetDescriptor(18)
...as some devices misbehave when encountering a SetAddress command
prior to GetDescriptor. There are known legacy devices that require
this scheme, but testing has found at least one USB3 device that fails
enumeration when presented with this ordering. For now, follow the ehci
case and enable 'new scheme' by default for non-SuperSpeed devices.
To support this enumeration scheme on xhci the AddressDevice operation
needs to be performed twice. The first instance of the command enables
the HC's device and slot context info for the device, but omits sending
the device a SetAddress command (BSR == block set address request).
Then, after GetDescriptor completes, follow up with the full
AddressDevice+SetAddress operation.
As mentioned before, this ordering of events with USB3 devices causes an
extra state transition to be exposed to xhci. Previously USB3 devices
would transition directly from 'enabled' to 'addressed' and never need
to underrun responses to 'get descriptor'. We do see the 64-byte
descriptor fetch the correct data, but the following 18-byte descriptor
read after the reset gets:
bLength = 0
bDescriptorType = 0
bcdUSB = 0
bDeviceClass = 0
bDeviceSubClass = 0
bDeviceProtocol = 0
bMaxPacketSize0 = 9
instead of:
bLength = 12
bDescriptorType = 1
bcdUSB = 300
bDeviceClass = 0
bDeviceSubClass = 0
bDeviceProtocol = 0
bMaxPacketSize0 = 9
which results in the discovery process looping until falling back to
'old scheme' enumeration.
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: David Moore <david.moore@gmail.com>
Suggested-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r-- | drivers/usb/host/xhci.c | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7fe6f664054f..6598f7ee7938 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3709,12 +3709,13 @@ disable_slot: } /* - * Issue an Address Device command (which will issue a SetAddress request to - * the device). + * Issue an Address Device command and optionally send a corresponding + * SetAddress request to the device. * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so * we should only issue and wait on one address command at the same time. */ -int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, + enum xhci_setup_dev setup) { unsigned long flags; int timeleft; @@ -3773,7 +3774,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) spin_lock_irqsave(&xhci->lock, flags); cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring); ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma, - udev->slot_id); + udev->slot_id, setup); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg_trace(xhci, trace_xhci_dbg_address, @@ -3868,6 +3869,16 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } +int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS); +} + +int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY); +} + /* * Transfer the port index into real index in the HW port status * registers. Caculate offset between the port's PORTSC register |