summaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c134
1 files changed, 106 insertions, 28 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index be86ae3f4088..2af3b4f06054 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -155,6 +155,8 @@ static inline char *portspeed(int portstatus)
return "480 Mb/s";
else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
return "1.5 Mb/s";
+ else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
+ return "5.0 Gb/s";
else
return "12 Mb/s";
}
@@ -457,13 +459,13 @@ static void hub_tt_kevent (struct work_struct *work)
spin_lock_irqsave (&hub->tt.lock, flags);
while (--limit && !list_empty (&hub->tt.clear_list)) {
- struct list_head *temp;
+ struct list_head *next;
struct usb_tt_clear *clear;
struct usb_device *hdev = hub->hdev;
int status;
- temp = hub->tt.clear_list.next;
- clear = list_entry (temp, struct usb_tt_clear, clear_list);
+ next = hub->tt.clear_list.next;
+ clear = list_entry (next, struct usb_tt_clear, clear_list);
list_del (&clear->clear_list);
/* drop lock so HCD can concurrently report other TT errors */
@@ -951,6 +953,9 @@ static int hub_configure(struct usb_hub *hub,
ret);
hub->tt.hub = hdev;
break;
+ case 3:
+ /* USB 3.0 hubs don't have a TT */
+ break;
default:
dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
hdev->descriptor.bDeviceProtocol);
@@ -1323,6 +1328,11 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
* 0 is reserved by USB for default address; (b) Linux's USB stack
* uses always #1 for the root hub of the controller. So USB stack's
* port #1, which is wusb virtual-port #0 has address #2.
+ *
+ * Devices connected under xHCI are not as simple. The host controller
+ * supports virtualization, so the hardware assigns device addresses and
+ * the HCD must setup data structures before issuing a set address
+ * command to the hardware.
*/
static void choose_address(struct usb_device *udev)
{
@@ -1642,6 +1652,9 @@ int usb_new_device(struct usb_device *udev)
err = usb_configure_device(udev); /* detect & probe dev/intfs */
if (err < 0)
goto fail;
+ dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
+ udev->devnum, udev->bus->busnum,
+ (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
/* export the usbdev device-node for libusb */
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
@@ -2395,19 +2408,29 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit);
static int hub_set_address(struct usb_device *udev, int devnum)
{
int retval;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- if (devnum <= 1)
+ /*
+ * The host controller will choose the device address,
+ * instead of the core having chosen it earlier
+ */
+ if (!hcd->driver->address_device && devnum <= 1)
return -EINVAL;
if (udev->state == USB_STATE_ADDRESS)
return 0;
if (udev->state != USB_STATE_DEFAULT)
return -EINVAL;
- retval = usb_control_msg(udev, usb_sndaddr0pipe(),
- USB_REQ_SET_ADDRESS, 0, devnum, 0,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (hcd->driver->address_device) {
+ retval = hcd->driver->address_device(hcd, udev);
+ } else {
+ retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+ USB_REQ_SET_ADDRESS, 0, devnum, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (retval == 0)
+ update_address(udev, devnum);
+ }
if (retval == 0) {
/* Device now using proper address. */
- update_address(udev, devnum);
usb_set_device_state(udev, USB_STATE_ADDRESS);
usb_ep0_reinit(udev);
}
@@ -2430,6 +2453,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
static DEFINE_MUTEX(usb_address0_mutex);
struct usb_device *hdev = hub->hdev;
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
@@ -2452,11 +2476,24 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
mutex_lock(&usb_address0_mutex);
- /* Reset the device; full speed may morph to high speed */
- retval = hub_port_reset(hub, port1, udev, delay);
- if (retval < 0) /* error or disconnect */
+ if ((hcd->driver->flags & HCD_USB3) && udev->config) {
+ /* FIXME this will need special handling by the xHCI driver. */
+ dev_dbg(&udev->dev,
+ "xHCI reset of configured device "
+ "not supported yet.\n");
+ retval = -EINVAL;
goto fail;
- /* success, speed is known */
+ } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+ /* Don't reset USB 3.0 devices during an initial setup */
+ usb_set_device_state(udev, USB_STATE_DEFAULT);
+ } else {
+ /* Reset the device; full speed may morph to high speed */
+ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
+ retval = hub_port_reset(hub, port1, udev, delay);
+ if (retval < 0) /* error or disconnect */
+ goto fail;
+ /* success, speed is known */
+ }
retval = -ENODEV;
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
@@ -2471,6 +2508,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
*/
switch (udev->speed) {
+ case USB_SPEED_SUPER:
case USB_SPEED_VARIABLE: /* fixed at 512 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
break;
@@ -2496,16 +2534,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
case USB_SPEED_LOW: speed = "low"; break;
case USB_SPEED_FULL: speed = "full"; break;
case USB_SPEED_HIGH: speed = "high"; break;
+ case USB_SPEED_SUPER:
+ speed = "super";
+ break;
case USB_SPEED_VARIABLE:
speed = "variable";
type = "Wireless ";
break;
default: speed = "?"; break;
}
- dev_info (&udev->dev,
- "%s %s speed %sUSB device using %s and address %d\n",
- (udev->config) ? "reset" : "new", speed, type,
- udev->bus->controller->driver->name, devnum);
+ if (udev->speed != USB_SPEED_SUPER)
+ dev_info(&udev->dev,
+ "%s %s speed %sUSB device using %s and address %d\n",
+ (udev->config) ? "reset" : "new", speed, type,
+ udev->bus->controller->driver->name, devnum);
/* Set up TT records, if needed */
if (hdev->tt) {
@@ -2530,7 +2572,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* value.
*/
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
- if (USE_NEW_SCHEME(retry_counter)) {
+ /*
+ * 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;
@@ -2596,7 +2642,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* unauthorized address in the Connect Ack sequence;
* authorization will assign the final address.
*/
- if (udev->wusb == 0) {
+ if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum);
if (retval >= 0)
@@ -2609,13 +2655,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
devnum, retval);
goto fail;
}
+ if (udev->speed == USB_SPEED_SUPER) {
+ devnum = udev->devnum;
+ dev_info(&udev->dev,
+ "%s SuperSpeed USB device using %s and address %d\n",
+ (udev->config) ? "reset" : "new",
+ udev->bus->controller->driver->name, devnum);
+ }
/* cope with hardware quirkiness:
* - let SET_ADDRESS settle, some device hardware wants it
* - read ep0 maxpacket even for high and low speed,
*/
msleep(10);
- if (USE_NEW_SCHEME(retry_counter))
+ if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
break;
}
@@ -2634,8 +2687,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (retval)
goto fail;
- i = udev->descriptor.bMaxPacketSize0 == 0xff? /* wusb device? */
- 512 : udev->descriptor.bMaxPacketSize0;
+ if (udev->descriptor.bMaxPacketSize0 == 0xff ||
+ udev->speed == USB_SPEED_SUPER)
+ i = 512;
+ else
+ i = udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed != USB_SPEED_FULL ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2847,19 +2903,41 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
usb_set_device_state(udev, USB_STATE_POWERED);
- udev->speed = USB_SPEED_UNKNOWN;
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
- /* set the address */
- choose_address(udev);
- if (udev->devnum <= 0) {
- status = -ENOTCONN; /* Don't retry */
- goto loop;
+ /*
+ * 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 & (1 << USB_PORT_FEAT_SUPERSPEED)))
+ udev->speed = USB_SPEED_SUPER;
+ else
+ udev->speed = USB_SPEED_UNKNOWN;
+
+ /*
+ * xHCI needs to issue an address device command later
+ * in the hub_port_init sequence for SS/HS/FS/LS devices.
+ */
+ if (!(hcd->driver->flags & HCD_USB3)) {
+ /* set the address */
+ choose_address(udev);
+ if (udev->devnum <= 0) {
+ status = -ENOTCONN; /* Don't retry */
+ goto loop;
+ }
}
- /* reset and get descriptor */
+ /* reset (non-USB 3.0 devices) and get descriptor */
status = hub_port_init(hub, udev, port1, i);
if (status < 0)
goto loop;