/* buffer for urb ... with extra space in case of babble */
char (*buffer)[8];
- dma_addr_t buffer_dma; /* DMA address for buffer */
union {
struct usb_hub_status hub;
struct usb_port_status port;
status change */
unsigned long busy_bits[1]; /* ports being reset or
resumed */
+ unsigned long removed_bits[1]; /* ports with a "removed"
+ device present */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
unsigned mA_per_port; /* current for each child */
+ unsigned init_done:1;
unsigned limited_power:1;
unsigned quiescing:1;
unsigned disconnected:1;
unsigned has_indicators:1;
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
+ struct delayed_work init_work;
+ void **port_owners;
};
MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
/*
+ * Device SATA8000 FW1.0 from DATAST0R Technology Corp requires about
+ * 10 seconds to send reply for the initial 64-byte descriptor request.
+ */
+/* define initial 64-byte descriptor request timeout in milliseconds */
+static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT;
+module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(initial_descriptor_timeout,
+ "initial 64-byte descriptor request timeout in milliseconds "
+ "(default 5000 - 5.0 seconds)");
+
+/*
* As of 2.6.10 we introduce a new USB device initialization scheme which
* closely resembles the way Windows works. Hopefully it will be compatible
* with a wider range of devices than the old scheme. However some previously
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";
}
/* Note that hdev or one of its children must be locked! */
-static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev)
+static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
{
+ if (!hdev || !hdev->actconfig)
+ return NULL;
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
{
unsigned long flags;
- /* Suppress autosuspend until khubd runs */
- to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
-
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
+
+ /* Suppress autosuspend until khubd runs */
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
void usb_kick_khubd(struct usb_device *hdev)
{
- /* FIXME: What if hdev isn't bound to the hub driver? */
- kick_khubd(hdev_to_hub(hdev));
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ if (hub)
+ kick_khubd(hub);
}
{
struct usb_hub *hub = urb->context;
int status = urb->status;
- int i;
+ unsigned i;
unsigned long bits;
switch (status) {
static inline int
hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)
{
- return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+ return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,
tt, NULL, 0, 1000);
}
* talking to TTs must queue control transfers (not just bulk and iso), so
* both can talk to the same hub concurrently.
*/
-static void hub_tt_kevent (struct work_struct *work)
+static void hub_tt_work(struct work_struct *work)
{
struct usb_hub *hub =
- container_of(work, struct usb_hub, tt.kevent);
+ container_of(work, struct usb_hub, tt.clear_work);
unsigned long flags;
int limit = 100;
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;
+ const struct hc_driver *drv;
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 */
spin_unlock_irqrestore (&hub->tt.lock, flags);
status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt);
- spin_lock_irqsave (&hub->tt.lock, flags);
-
if (status)
dev_err (&hdev->dev,
"clear tt %d (%04x) error %d\n",
clear->tt, clear->devinfo, status);
+
+ /* Tell the HCD, even if the operation failed */
+ drv = clear->hcd->driver;
+ if (drv->clear_tt_buffer_complete)
+ (drv->clear_tt_buffer_complete)(clear->hcd, clear->ep);
+
kfree(clear);
+ spin_lock_irqsave(&hub->tt.lock, flags);
}
spin_unlock_irqrestore (&hub->tt.lock, flags);
}
/**
- * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub
- * @udev: the device whose split transaction failed
- * @pipe: identifies the endpoint of the failed transaction
+ * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
+ * @urb: an URB associated with the failed or incomplete split transaction
*
* High speed HCDs use this to tell the hub driver that some split control or
* bulk transaction failed in a way that requires clearing internal state of
* It may not be possible for that hub to handle additional full (or low)
* speed transactions until that state is fully cleared out.
*/
-void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
+int usb_hub_clear_tt_buffer(struct urb *urb)
{
+ struct usb_device *udev = urb->dev;
+ int pipe = urb->pipe;
struct usb_tt *tt = udev->tt;
unsigned long flags;
struct usb_tt_clear *clear;
if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) {
dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
/* FIXME recover somehow ... RESET_TT? */
- return;
+ return -ENOMEM;
}
/* info that CLEAR_TT_BUFFER needs */
: (USB_ENDPOINT_XFER_BULK << 11);
if (usb_pipein (pipe))
clear->devinfo |= 1 << 15;
-
+
+ /* info for completion callback */
+ clear->hcd = bus_to_hcd(udev->bus);
+ clear->ep = urb->ep;
+
/* tell keventd to clear state for this TT */
spin_lock_irqsave (&tt->lock, flags);
list_add_tail (&clear->clear_list, &tt->clear_list);
- schedule_work (&tt->kevent);
+ schedule_work(&tt->clear_work);
spin_unlock_irqrestore (&tt->lock, flags);
+ return 0;
}
-EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer);
+EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);
-static void hub_power_on(struct usb_hub *hub)
+/* If do_delay is false, return the number of milliseconds the caller
+ * needs to delay.
+ */
+static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
{
int port1;
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
+ unsigned delay;
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
/* Wait at least 100 msec for power to become stable */
- msleep(max(pgood_delay, (unsigned) 100));
+ delay = max(pgood_delay, (unsigned) 100);
+ if (do_delay)
+ msleep(delay);
+ return delay;
}
static int hub_hub_status(struct usb_hub *hub,
kick_khubd(hub);
}
+/**
+ * usb_remove_device - disable a device's port on its parent hub
+ * @udev: device to be disabled and removed
+ * Context: @udev locked, must be able to sleep.
+ *
+ * After @udev's port has been disabled, khubd is notified and it will
+ * see that the device has been disconnected. When the device is
+ * physically unplugged and something is plugged in, the events will
+ * be received and processed normally.
+ */
+int usb_remove_device(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+ struct usb_interface *intf;
+
+ if (!udev->parent) /* Can't remove a root hub */
+ return -EINVAL;
+ hub = hdev_to_hub(udev->parent);
+ intf = to_usb_interface(hub->intfdev);
+
+ usb_autopm_get_interface(intf);
+ set_bit(udev->portnum, hub->removed_bits);
+ hub_port_logical_disconnect(hub, udev->portnum);
+ usb_autopm_put_interface(intf);
+ return 0;
+}
+
enum hub_activation_type {
- HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
+ HUB_INIT, HUB_INIT2, HUB_INIT3, /* INITs must come first */
+ HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};
+static void hub_init_func2(struct work_struct *ws);
+static void hub_init_func3(struct work_struct *ws);
+
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
struct usb_device *hdev = hub->hdev;
int port1;
int status;
bool need_debounce_delay = false;
+ unsigned delay;
+
+ /* Continue a partial initialization */
+ if (type == HUB_INIT2)
+ goto init2;
+ if (type == HUB_INIT3)
+ goto init3;
/* After a resume, port power should still be on.
* For any other type of activation, turn it on.
*/
- if (type != HUB_RESUME)
- hub_power_on(hub);
+ if (type != HUB_RESUME) {
+
+ /* Speed up system boot by using a delayed_work for the
+ * hub's initial power-up delays. This is pretty awkward
+ * and the implementation looks like a home-brewed sort of
+ * setjmp/longjmp, but it saves at least 100 ms for each
+ * root hub (assuming usbcore is compiled into the kernel
+ * rather than as a module). It adds up.
+ *
+ * This can't be done for HUB_RESUME or HUB_RESET_RESUME
+ * because for those activation types the ports have to be
+ * operational when we return. In theory this could be done
+ * for HUB_POST_RESET, but it's easier not to.
+ */
+ if (type == HUB_INIT) {
+ delay = hub_power_on(hub, false);
+ PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
+ schedule_delayed_work(&hub->init_work,
+ msecs_to_jiffies(delay));
+
+ /* Suppress autosuspend until init is done */
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
+ return; /* Continues at init2: below */
+ } else {
+ hub_power_on(hub, true);
+ }
+ }
+ init2:
/* Check each port and set hub->change_bits to let khubd know
* which ports need attention.
USB_PORT_FEAT_C_ENABLE);
}
+ /* We can forget about a "removed" device when there's a
+ * physical disconnect or the connect status changes.
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_bit(port1, hub->removed_bits);
+
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
/* Tell khubd to disconnect the device or
* check for a new connection
* If any port-status changes do occur during this delay, khubd
* will see them later and handle them normally.
*/
- if (need_debounce_delay)
- msleep(HUB_DEBOUNCE_STABLE);
-
+ if (need_debounce_delay) {
+ delay = HUB_DEBOUNCE_STABLE;
+
+ /* Don't do a long sleep inside a workqueue routine */
+ if (type == HUB_INIT2) {
+ PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
+ schedule_delayed_work(&hub->init_work,
+ msecs_to_jiffies(delay));
+ return; /* Continues at init3: below */
+ } else {
+ msleep(delay);
+ }
+ }
+ init3:
hub->quiescing = 0;
+ hub->init_done = 1;
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status < 0)
/* Scan all ports that need attention */
kick_khubd(hub);
+
+ /* Allow autosuspend if it was suppressed */
+ if (type <= HUB_INIT3)
+ usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+}
+
+/* Implement the continuations for the delays above */
+static void hub_init_func2(struct work_struct *ws)
+{
+ struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
+
+ hub_activate(hub, HUB_INIT2);
+}
+
+static void hub_init_func3(struct work_struct *ws)
+{
+ struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
+
+ hub_activate(hub, HUB_INIT3);
}
enum hub_quiescing_type {
struct usb_device *hdev = hub->hdev;
int i;
+ cancel_delayed_work_sync(&hub->init_work);
+ if (!hub->init_done) {
+ hub->init_done = 1;
+ usb_autopm_put_interface_no_suspend(
+ to_usb_interface(hub->intfdev));
+ }
+
/* khubd and related activity won't re-trigger */
hub->quiescing = 1;
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
- cancel_work_sync(&hub->tt.kevent);
+ cancel_work_sync(&hub->tt.clear_work);
}
/* caller has locked the hub device */
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
+ struct usb_hcd *hcd;
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
int maxp, ret;
- char *message;
+ char *message = "out of memory";
- hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
- &hub->buffer_dma);
+ hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
if (!hub->buffer) {
- message = "can't allocate hub irq buffer";
ret = -ENOMEM;
goto fail;
}
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
if (!hub->status) {
- message = "can't kmalloc hub status buffer";
ret = -ENOMEM;
goto fail;
}
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
- message = "can't kmalloc hub descriptor";
ret = -ENOMEM;
goto fail;
}
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
+ hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
+ if (!hub->port_owners) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
spin_lock_init (&hub->tt.lock);
INIT_LIST_HEAD (&hub->tt.clear_list);
- INIT_WORK (&hub->tt.kevent, hub_tt_kevent);
+ INIT_WORK(&hub->tt.clear_work, hub_tt_work);
switch (hdev->descriptor.bDeviceProtocol) {
case 0:
break;
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);
dev_dbg(hub_dev, "%umA bus power budget for each child\n",
hub->mA_per_port);
+ /* Update the HCD's internal representation of this hub before khubd
+ * starts getting port status changes for devices under the hub.
+ */
+ hcd = bus_to_hcd(hdev->bus);
+ if (hcd->driver->update_hub_device) {
+ ret = hcd->driver->update_hub_device(hcd, hdev,
+ &hub->tt, GFP_KERNEL);
+ if (ret < 0) {
+ message = "can't update HCD hub info";
+ goto fail;
+ }
+ }
+
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) {
message = "can't get hub status";
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
- message = "couldn't allocate interrupt urb";
ret = -ENOMEM;
goto fail;
}
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
- hub->urb->transfer_dma = hub->buffer_dma;
- hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
- list_del_init(&hub->event_list);
+ if (!list_empty(&hub->event_list)) {
+ list_del_init(&hub->event_list);
+ usb_autopm_put_interface_no_suspend(intf);
+ }
hub->disconnected = 1;
spin_unlock_irq(&hub_event_lock);
hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
+ hub->hdev->maxchild = 0;
if (hub->hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
usb_free_urb(hub->urb);
+ kfree(hub->port_owners);
kfree(hub->descriptor);
kfree(hub->status);
- usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
- hub->buffer_dma);
+ kfree(hub->buffer);
kref_put(&hub->kref, hub_release);
}
hdev = interface_to_usbdev(intf);
if (hdev->level == MAX_TOPO_LEVEL) {
- dev_err(&intf->dev, "Unsupported bus topology: "
- "hub nested too deep\n");
+ dev_err(&intf->dev,
+ "Unsupported bus topology: hub nested too deep\n");
return -E2BIG;
}
hub->intfdev = &intf->dev;
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
+ INIT_DELAYED_WORK(&hub->init_work, NULL);
usb_get_intf(intf);
usb_set_intfdata (intf, hub);
}
}
+/*
+ * Allow user programs to claim ports on a hub. When a device is attached
+ * to one of these "claimed" ports, the program will "own" the device.
+ */
+static int find_port_owner(struct usb_device *hdev, unsigned port1,
+ void ***ppowner)
+{
+ if (hdev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (port1 == 0 || port1 > hdev->maxchild)
+ return -EINVAL;
+
+ /* This assumes that devices not managed by the hub driver
+ * will always have maxchild equal to 0.
+ */
+ *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ return 0;
+}
+
+/* In the following three functions, the caller must hold hdev's lock */
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+{
+ int rc;
+ void **powner;
+
+ rc = find_port_owner(hdev, port1, &powner);
+ if (rc)
+ return rc;
+ if (*powner)
+ return -EBUSY;
+ *powner = owner;
+ return rc;
+}
+
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+{
+ int rc;
+ void **powner;
+
+ rc = find_port_owner(hdev, port1, &powner);
+ if (rc)
+ return rc;
+ if (*powner != owner)
+ return -ENOENT;
+ *powner = NULL;
+ return rc;
+}
+
+void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+{
+ int n;
+ void **powner;
+
+ n = find_port_owner(hdev, 1, &powner);
+ if (n == 0) {
+ for (; n < hdev->maxchild; (++n, ++powner)) {
+ if (*powner == owner)
+ *powner = NULL;
+ }
+ }
+}
+
+/* The caller must hold udev's lock */
+bool usb_device_is_owned(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+
+ if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
+ return false;
+ hub = hdev_to_hub(udev->parent);
+ return !!hub->port_owners[udev->portnum - 1];
+}
+
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
+EXPORT_SYMBOL_GPL(usb_set_device_state);
/*
* WUSB devices are simple: they have no hubs behind, so the mapping
* 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)
{
usb_autosuspend_device(udev->parent);
usb_pm_unlock(udev);
- /* Stop any autosuspend requests already submitted */
- cancel_rearming_delayed_work(&udev->autosuspend);
+ /* Stop any autosuspend or autoresume requests already submitted */
+ cancel_delayed_work_sync(&udev->autosuspend);
+ cancel_work_sync(&udev->autoresume);
}
#else
*/
dev_dbg (&udev->dev, "unregistering device\n");
usb_disable_device(udev, 0);
+ usb_hcd_synchronize_unlinks(udev);
+ usb_remove_ep_devs(&udev->ep0);
usb_unlock_device(udev);
- /* Remove the device-specific files from sysfs. This must be
- * done with udev unlocked, because some of the attribute
- * routines try to acquire the device lock.
- */
- usb_remove_sysfs_dev_files(udev);
-
/* Unregister the device. The device driver is responsible
- * for removing the device files from usbfs and sysfs and for
- * de-configuring the device.
+ * for de-configuring the device and invoking the remove-device
+ * notifier chain (used by usbfs and possibly others).
*/
device_del(&udev->dev);
dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
- dev_info(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, "
- "SerialNumber=%d\n",
+ dev_info(&udev->dev,
+ "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
udev->descriptor.iSerialNumber);
#endif
/**
- * usb_configure_device_otg - FIXME (usbcore-internal)
+ * usb_enumerate_device_otg - FIXME (usbcore-internal)
* @udev: newly addressed device (in ADDRESS state)
*
- * Do configuration for On-The-Go devices
+ * Finish enumeration for On-The-Go devices
*/
-static int usb_configure_device_otg(struct usb_device *udev)
+static int usb_enumerate_device_otg(struct usb_device *udev)
{
int err = 0;
if (!udev->bus->is_b_host
&& udev->config
&& udev->parent == udev->bus->root_hub) {
- struct usb_otg_descriptor *desc = 0;
+ struct usb_otg_descriptor *desc = NULL;
struct usb_bus *bus = udev->bus;
/* descriptor may appear anywhere in config */
* customize to match your product.
*/
dev_info(&udev->dev,
- "can't set HNP mode; %d\n",
+ "can't set HNP mode: %d\n",
err);
bus->b_hnp_enable = 0;
}
* (Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
- err = usb_port_suspend(udev);
+ err = usb_port_suspend(udev, PMSG_SUSPEND);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
/**
- * usb_configure_device - Detect and probe device intfs/otg (usbcore-internal)
+ * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
* @udev: newly addressed device (in ADDRESS state)
*
* This is only called by usb_new_device() and usb_authorize_device()
* the string descriptors, as they will be errored out by the device
* until it has been authorized.
*/
-static int usb_configure_device(struct usb_device *udev)
+static int usb_enumerate_device(struct usb_device *udev)
{
int err;
udev->descriptor.iManufacturer);
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
}
- err = usb_configure_device_otg(udev);
+ err = usb_enumerate_device_otg(udev);
fail:
return err;
}
* usb_new_device - perform initial device setup (usbcore-internal)
* @udev: newly addressed device (in ADDRESS state)
*
- * This is called with devices which have been enumerated, but not yet
- * configured. The device descriptor is available, but not descriptors
+ * This is called with devices which have been detected but not fully
+ * enumerated. The device descriptor is available, but not descriptors
* for any device configuration. The caller must have locked either
* the parent hub (if udev is a normal device) or else the
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
{
int err;
- usb_detect_quirks(udev); /* Determine quirks */
- err = usb_configure_device(udev); /* detect & probe dev/intfs */
+ /* Increment the parent's count of unsuspended children */
+ if (udev->parent)
+ usb_autoresume_device(udev->parent);
+
+ usb_detect_quirks(udev);
+ err = usb_enumerate_device(udev); /* Read descriptors */
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)));
- /* Increment the parent's count of unsuspended children */
- if (udev->parent)
- usb_autoresume_device(udev->parent);
+ /* Tell the world! */
+ announce_device(udev);
/* Register the device. The device driver is responsible
- * for adding the device files to sysfs and for configuring
- * the device.
+ * for configuring the device and invoking the add-device
+ * notifier chain (used by usbfs and possibly others).
*/
err = device_add(&udev->dev);
if (err) {
goto fail;
}
- /* put device-specific files into sysfs */
- usb_create_sysfs_dev_files(udev);
-
- /* Tell the world! */
- announce_device(udev);
+ (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
return err;
fail:
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+ usb_stop_pm(udev);
return err;
}
*/
int usb_deauthorize_device(struct usb_device *usb_dev)
{
- unsigned cnt;
usb_lock_device(usb_dev);
if (usb_dev->authorized == 0)
goto out_unauthorized;
+
usb_dev->authorized = 0;
usb_set_configuration(usb_dev, -1);
+
+ kfree(usb_dev->product);
usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+ kfree(usb_dev->manufacturer);
usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+ kfree(usb_dev->serial);
usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- kfree(usb_dev->config);
- usb_dev->config = NULL;
- for (cnt = 0; cnt < usb_dev->descriptor.bNumConfigurations; cnt++)
- kfree(usb_dev->rawdescriptors[cnt]);
+
+ usb_destroy_configuration(usb_dev);
usb_dev->descriptor.bNumConfigurations = 0;
- kfree(usb_dev->rawdescriptors);
+
out_unauthorized:
usb_unlock_device(usb_dev);
return 0;
int usb_authorize_device(struct usb_device *usb_dev)
{
int result = 0, c;
+
usb_lock_device(usb_dev);
if (usb_dev->authorized == 1)
goto out_authorized;
- kfree(usb_dev->product);
- usb_dev->product = NULL;
- kfree(usb_dev->manufacturer);
- usb_dev->manufacturer = NULL;
- kfree(usb_dev->serial);
- usb_dev->serial = NULL;
+
result = usb_autoresume_device(usb_dev);
if (result < 0) {
dev_err(&usb_dev->dev,
"authorization: %d\n", result);
goto error_device_descriptor;
}
+
+ kfree(usb_dev->product);
+ usb_dev->product = NULL;
+ kfree(usb_dev->manufacturer);
+ usb_dev->manufacturer = NULL;
+ kfree(usb_dev->serial);
+ usb_dev->serial = NULL;
+
usb_dev->authorized = 1;
- result = usb_configure_device(usb_dev);
+ result = usb_enumerate_device(usb_dev);
if (result < 0)
- goto error_configure;
+ goto error_enumerate;
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
}
}
dev_info(&usb_dev->dev, "authorized to connect\n");
-error_configure:
+
+error_enumerate:
error_device_descriptor:
+ usb_autosuspend_device(usb_dev);
error_autoresume:
out_authorized:
usb_unlock_device(usb_dev); // complements locktree
*
* Returns 0 on success, else negative errno.
*/
-int usb_port_suspend(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
- if (status)
+ if (status) {
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status);
+ /* bail if autosuspend is requested */
+ if (msg.event & PM_EVENT_AUTO)
+ return status;
+ }
}
/* see 7.1.7.6 */
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
/* paranoia: "should not happen" */
- (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ if (udev->do_remote_wakeup)
+ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
} else {
/* device has up to 10 msec to fully suspend */
dev_dbg(&udev->dev, "usb %ssuspend\n",
- udev->auto_pm ? "auto-" : "");
+ (msg.event & PM_EVENT_AUTO ? "auto-" : ""));
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
u16 devstatus;
/* caller owns the udev device lock */
- dev_dbg(&udev->dev, "finish %sresume\n",
- udev->reset_resume ? "reset-" : "");
+ dev_dbg(&udev->dev, "%s\n",
+ udev->reset_resume ? "finish reset-resume" : "finish resume");
/* usb ch9 identifies four variants of SUSPENDED, based on what
* state the device resumes to. Linux currently won't see the
NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (status)
- dev_dbg(&udev->dev, "disable remote "
- "wakeup, status %d\n", status);
+ dev_dbg(&udev->dev,
+ "disable remote wakeup, status %d\n",
+ status);
}
status = 0;
}
*
* Returns 0 on success, else negative errno.
*/
-int usb_port_resume(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
} else {
/* drive resume for at least 20 msec */
dev_dbg(&udev->dev, "usb %sresume\n",
- udev->auto_pm ? "auto-" : "");
+ (msg.event & PM_EVENT_AUTO ? "auto-" : ""));
msleep(25);
/* Virtual root hubs can trigger on GET_PORT_STATUS to
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
usb_mark_last_busy(udev);
- status = usb_external_resume_device(udev);
+ status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
}
return status;
}
/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
-int usb_port_suspend(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
return 0;
}
/* However we may need to do a reset-resume */
-int usb_port_resume(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
udev = hdev->children [port1-1];
if (udev && udev->can_submit) {
- if (!hdev->auto_pm)
+ if (!(msg.event & PM_EVENT_AUTO))
dev_dbg(&intf->dev, "port %d nyet suspended\n",
port1);
return -EBUSY;
void usb_ep0_reinit(struct usb_device *udev)
{
- usb_disable_endpoint(udev, 0 + USB_DIR_IN);
- usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
- usb_enable_endpoint(udev, &udev->ep0);
+ usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);
+ usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);
+ usb_enable_endpoint(udev, &udev->ep0, true);
}
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);
}
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;
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) {
* 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 = __constant_cpu_to_le16(512);
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
break;
case USB_SPEED_HIGH: /* fixed at 64 */
- udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
/* to determine the ep0 maxpacket size, try to read
* the device descriptor to get bMaxPacketSize0 and
* then correct our initial guess.
*/
- udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
break;
case USB_SPEED_LOW: /* fixed at 8 */
- udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
break;
default:
goto fail;
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) {
* 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;
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
USB_DT_DEVICE << 8, 0,
buf, GET_DESCRIPTOR_BUFSIZE,
- USB_CTRL_GET_TIMEOUT);
+ initial_descriptor_timeout);
switch (buf->bMaxPacketSize0) {
case 8: case 16: case 32: case 64: case 255:
if (buf->bDescriptorType ==
goto fail;
}
if (r) {
- dev_err(&udev->dev, "device descriptor "
- "read/%s, error %d\n",
- "64", r);
+ dev_err(&udev->dev,
+ "device descriptor read/64, error %d\n",
+ r);
retval = -EMSGSIZE;
continue;
}
* 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)
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;
}
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
- dev_err(&udev->dev, "device descriptor "
- "read/%s, error %d\n",
- "8", retval);
+ dev_err(&udev->dev,
+ "device descriptor read/8, error %d\n",
+ retval);
if (retval >= 0)
retval = -EMSGSIZE;
} else {
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)) {
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
- dev_err(&udev->dev, "device descriptor read/%s, error %d\n",
- "all", retval);
+ dev_err(&udev->dev, "device descriptor read/all, error %d\n",
+ retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
else
delta = 8;
if (delta > hub->mA_per_port)
- dev_warn(&udev->dev, "%dmA is over %umA budget "
- "for port %d!\n",
- delta, hub->mA_per_port, port1);
+ dev_warn(&udev->dev,
+ "%dmA is over %umA budget for port %d!\n",
+ delta, hub->mA_per_port, port1);
remaining -= delta;
}
if (remaining < 0) {
USB_PORT_STAT_C_ENABLE);
#endif
- /* Try to use the debounce delay for protection against
- * port-enable changes caused, for example, by EMI.
- */
- if (portchange & (USB_PORT_STAT_C_CONNECTION |
- USB_PORT_STAT_C_ENABLE)) {
- status = hub_port_debounce(hub, port1);
- if (status < 0) {
- if (printk_ratelimit())
- dev_err (hub_dev, "connect-debounce failed, "
- "port %d disabled\n", port1);
- portstatus &= ~USB_PORT_STAT_CONNECTION;
- } else {
- portstatus = status;
- }
- }
-
/* Try to resuscitate an existing device */
udev = hdev->children[port1-1];
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
-
usb_lock_device(udev);
if (portstatus & USB_PORT_STAT_ENABLE) {
status = 0; /* Nothing to do */
- } else if (!udev->persist_enabled) {
- status = -ENODEV; /* Mustn't resuscitate */
#ifdef CONFIG_USB_SUSPEND
- } else if (udev->state == USB_STATE_SUSPENDED) {
+ } else if (udev->state == USB_STATE_SUSPENDED &&
+ udev->persist_enabled) {
/* For a suspended device, treat this as a
* remote wakeup event.
*/
- if (udev->do_remote_wakeup)
- status = remote_wakeup(udev);
-
- /* Otherwise leave it be; devices can't tell the
- * difference between suspended and disabled.
- */
- else
- status = 0;
+ status = remote_wakeup(udev);
#endif
} else {
- status = usb_reset_device(udev);
+ status = -ENODEV; /* Don't resuscitate */
}
usb_unlock_device(udev);
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
- /* Return now if debouncing failed or nothing is connected */
- if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+ /* We can forget about a "removed" device when there's a physical
+ * disconnect or the connect status changes.
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_bit(port1, hub->removed_bits);
+
+ if (portchange & (USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE)) {
+ status = hub_port_debounce(hub, port1);
+ if (status < 0) {
+ if (printk_ratelimit())
+ dev_err(hub_dev, "connect-debounce failed, "
+ "port %d disabled\n", port1);
+ portstatus &= ~USB_PORT_STAT_CONNECTION;
+ } else {
+ portstatus = status;
+ }
+ }
+
+ /* Return now if debouncing failed or nothing is connected or
+ * the device was "removed".
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ test_bit(port1, hub->removed_bits)) {
/* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
&& !(portstatus & (1 << USB_PORT_FEAT_POWER)))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
-
+
if (portstatus & USB_PORT_STAT_ENABLE)
goto done;
return;
}
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;
* disconnected while waiting for the lock to succeed. */
usb_lock_device(hdev);
if (unlikely(hub->disconnected))
- goto loop;
+ goto loop2;
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
USB_PORT_FEAT_C_SUSPEND);
udev = hdev->children[i-1];
if (udev) {
+ /* TRSMRCY = 10 msec */
+ msleep(10);
+
usb_lock_device(udev);
ret = remote_wakeup(hdev->
children[i-1]);
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_OVER_CURRENT);
- hub_power_on(hub);
+ hub_power_on(hub, true);
}
if (portchange & USB_PORT_STAT_C_RESET) {
dev_dbg (hub_dev, "overcurrent change\n");
msleep(500); /* Cool down */
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
- hub_power_on(hub);
+ hub_power_on(hub, true);
}
}
-loop_autopm:
- /* Allow autosuspend if we're not going to run again */
- if (list_empty(&hub->event_list))
- usb_autopm_enable(intf);
-loop:
+ loop_autopm:
+ /* Balance the usb_autopm_get_interface() above */
+ usb_autopm_put_interface_no_suspend(intf);
+ loop:
+ /* Balance the usb_autopm_get_interface_no_resume() in
+ * kick_khubd() and allow autosuspend.
+ */
+ usb_autopm_put_interface(intf);
+ loop2:
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
{
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret = 0;
int port1 = udev->portnum;
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
}
-
+
+ /* Restore the device's previous configuration */
if (!udev->actconfig)
goto done;
+ 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);
+ goto re_enumerate;
+ }
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0,
udev->actconfig->desc.bConfigurationValue, 0,
dev_err(&udev->dev,
"can't restore configuration #%d (error=%d)\n",
udev->actconfig->desc.bConfigurationValue, ret);
+ mutex_unlock(&hcd->bandwidth_mutex);
goto re_enumerate;
}
+ mutex_unlock(&hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
+ /* Put interfaces back into the same altsettings as before.
+ * Don't bother to send the Set-Interface request for interfaces
+ * that were already in altsetting 0; besides being unnecessary,
+ * many devices can't handle it. Instead just reset the host-side
+ * endpoint state.
+ */
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *intf = udev->actconfig->interface[i];
+ struct usb_host_config *config = udev->actconfig;
+ struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc;
- /* set_interface resets host side toggle even
- * for altsetting zero. the interface may have no driver.
- */
desc = &intf->cur_altsetting->desc;
- ret = usb_set_interface(udev, desc->bInterfaceNumber,
- desc->bAlternateSetting);
+ if (desc->bAlternateSetting == 0) {
+ usb_disable_interface(udev, intf, true);
+ usb_enable_interface(udev, intf, true);
+ ret = 0;
+ } else {
+ /* We've just reset the device, so it will think alt
+ * setting 0 is installed. For usb_set_interface() to
+ * work properly, we need to set the current alternate
+ * interface setting to 0 (or the first alt setting, if
+ * the device doesn't have alt setting 0).
+ */
+ intf->cur_altsetting =
+ usb_find_alt_setting(config, i, 0);
+ if (!intf->cur_altsetting)
+ intf->cur_altsetting =
+ &config->intf_cache[i]->altsetting[0];
+ ret = usb_set_interface(udev, desc->bInterfaceNumber,
+ desc->bAlternateSetting);
+ }
if (ret < 0) {
dev_err(&udev->dev, "failed to restore interface %d "
"altsetting %d (error=%d)\n",
USB_INTERFACE_BOUND)
rebind = 1;
}
- if (rebind)
+ if (ret == 0 && rebind)
usb_rebind_intf(cintf);
}
}
return ret;
}
EXPORT_SYMBOL_GPL(usb_reset_device);
+
+
+/**
+ * usb_queue_reset_device - Reset a USB device from an atomic context
+ * @iface: USB interface belonging to the device to reset
+ *
+ * This function can be used to reset a USB device from an atomic
+ * context, where usb_reset_device() won't work (as it blocks).
+ *
+ * Doing a reset via this method is functionally equivalent to calling
+ * usb_reset_device(), except for the fact that it is delayed to a
+ * workqueue. This means that any drivers bound to other interfaces
+ * might be unbound, as well as users from usbfs in user space.
+ *
+ * Corner cases:
+ *
+ * - Scheduling two resets at the same time from two different drivers
+ * attached to two different interfaces of the same device is
+ * possible; depending on how the driver attached to each interface
+ * handles ->pre_reset(), the second reset might happen or not.
+ *
+ * - If a driver is unbound and it had a pending reset, the reset will
+ * be cancelled.
+ *
+ * - This function can be called during .probe() or .disconnect()
+ * times. On return from .disconnect(), any pending resets will be
+ * cancelled.
+ *
+ * There is no no need to lock/unlock the @reset_ws as schedule_work()
+ * does its own.
+ *
+ * NOTE: We don't do any reference count tracking because it is not
+ * needed. The lifecycle of the work_struct is tied to the
+ * usb_interface. Before destroying the interface we cancel the
+ * work_struct, so the fact that work_struct is queued and or
+ * running means the interface (and thus, the device) exist and
+ * are referenced.
+ */
+void usb_queue_reset_device(struct usb_interface *iface)
+{
+ schedule_work(&iface->reset_ws);
+}
+EXPORT_SYMBOL_GPL(usb_queue_reset_device);