summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hcd.h8
-rw-r--r--drivers/usb/core/hub.c74
-rw-r--r--drivers/usb/core/usb.c14
3 files changed, 76 insertions, 20 deletions
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 4f6ee60d97c6..ae6d9db41ca9 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -226,6 +226,14 @@ struct hc_driver {
void (*relinquish_port)(struct usb_hcd *, int);
/* has a port been handed over to a companion? */
int (*port_handed_over)(struct usb_hcd *, int);
+
+ /* xHCI specific functions */
+ /* Called by usb_alloc_dev to alloc HC device structures */
+ int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
+ /* Called by usb_release_dev to free HC device structures */
+ void (*free_dev)(struct usb_hcd *, struct usb_device *);
+ /* Returns the hardware-chosen device address */
+ int (*address_device)(struct usb_hcd *, struct usb_device *udev);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3c28bde6cbd5..2af3b4f06054 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1328,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)
{
@@ -1647,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)));
@@ -2400,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);
}
@@ -2525,10 +2543,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
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) {
@@ -2553,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;
@@ -2619,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)
@@ -2632,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;
}
@@ -2877,13 +2907,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
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
@@ -2901,6 +2924,19 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
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 (non-USB 3.0 devices) and get descriptor */
status = hub_port_init(hub, udev, port1, i);
if (status < 0)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index f026991d0bdf..55b8d3a22d26 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -184,11 +184,16 @@ EXPORT_SYMBOL_GPL(usb_find_interface);
static void usb_release_dev(struct device *dev)
{
struct usb_device *udev;
+ struct usb_hcd *hcd;
udev = to_usb_device(dev);
+ hcd = bus_to_hcd(udev->bus);
usb_destroy_configuration(udev);
- usb_put_hcd(bus_to_hcd(udev->bus));
+ /* Root hubs aren't real devices, so don't free HCD resources */
+ if (hcd->driver->free_dev && udev->parent)
+ hcd->driver->free_dev(hcd, udev);
+ usb_put_hcd(hcd);
kfree(udev->product);
kfree(udev->manufacturer);
kfree(udev->serial);
@@ -348,6 +353,13 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
kfree(dev);
return NULL;
}
+ /* Root hubs aren't true devices, so don't allocate HCD resources */
+ if (usb_hcd->driver->alloc_dev && parent &&
+ !usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
+ usb_put_hcd(bus_to_hcd(bus));
+ kfree(dev);
+ return NULL;
+ }
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;