#include <linux/ioctl.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/pm_runtime.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include "usb.h"
-#include "hcd.h"
-#include "hub.h"
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
/* 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
static inline char *portspeed(int portstatus)
{
- if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
+ if (portstatus & USB_PORT_STAT_HIGH_SPEED)
return "480 Mb/s";
- else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
+ else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
- else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
+ else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
return "5.0 Gb/s";
else
return "12 Mb/s";
{
unsigned long flags;
- /* Suppress autosuspend until khubd runs */
- atomic_set(&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);
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_INIT2, HUB_INIT3,
+ HUB_INIT, HUB_INIT2, HUB_INIT3, /* INITs must come first */
HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};
msecs_to_jiffies(delay));
/* Suppress autosuspend until init is done */
- atomic_set(&to_usb_interface(hub->intfdev)->
- pm_usage_cnt, 1);
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
return; /* Continues at init2: below */
} else {
hub_power_on(hub, true);
!(portstatus & USB_PORT_STAT_CONNECTION) ||
!udev ||
udev->state == USB_STATE_NOTATTACHED)) {
- clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
- portstatus &= ~USB_PORT_STAT_ENABLE;
+ /*
+ * 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))) {
+ clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ portstatus &= ~USB_PORT_STAT_ENABLE;
+ }
}
/* Clear status-change flags; we'll debounce later */
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
/* 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 */
int maxp, ret;
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) {
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);
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);
}
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
+ /* Hubs have proper suspend/resume support */
+ usb_enable_autosuspend(hdev);
+
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
return -ENODEV;
}
+/* No BKL needed */
static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
if (udev->children[i])
recursively_mark_NOTATTACHED(udev->children[i]);
}
- if (udev->state == USB_STATE_SUSPENDED) {
- udev->discon_suspended = 1;
+ if (udev->state == USB_STATE_SUSPENDED)
udev->active_duration -= jiffies;
- }
udev->state = USB_STATE_NOTATTACHED;
}
|| new_state == USB_STATE_SUSPENDED)
; /* No change to wakeup settings */
else if (new_state == USB_STATE_CONFIGURED)
- device_init_wakeup(&udev->dev,
+ device_set_wakeup_capable(&udev->dev,
(udev->actconfig->desc.bmAttributes
& USB_CONFIG_ATT_WAKEUP));
else
- device_init_wakeup(&udev->dev, 0);
+ device_set_wakeup_capable(&udev->dev, 0);
}
if (udev->state == USB_STATE_SUSPENDED &&
new_state != USB_STATE_SUSPENDED)
udev->devnum = devnum;
}
-#ifdef CONFIG_USB_SUSPEND
-
-static void usb_stop_pm(struct usb_device *udev)
+static void hub_free_dev(struct usb_device *udev)
{
- /* Synchronize with the ksuspend thread to prevent any more
- * autosuspend requests from being submitted, and decrement
- * the parent's count of unsuspended children.
- */
- usb_pm_lock(udev);
- if (udev->parent && !udev->discon_suspended)
- usb_autosuspend_device(udev->parent);
- usb_pm_unlock(udev);
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- /* Stop any autosuspend or autoresume requests already submitted */
- cancel_delayed_work_sync(&udev->autosuspend);
- cancel_work_sync(&udev->autoresume);
+ /* 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);
}
-#else
-
-static inline void usb_stop_pm(struct usb_device *udev)
-{ }
-
-#endif
-
/**
* usb_disconnect - disconnect a device (usbcore-internal)
* @pdev: pointer to device being disconnected
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- usb_stop_pm(udev);
-
+ hub_free_dev(udev);
put_device(&udev->dev);
}
#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 */
/**
- * 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;
- /* Increment the parent's count of unsuspended children */
- if (udev->parent)
- usb_autoresume_device(udev->parent);
+ if (udev->parent) {
+ /* Initialize non-root-hub device wakeup to disabled;
+ * device (un)configuration controls wakeup capable
+ * sysfs power/wakeup controls wakeup enabled/disabled
+ */
+ device_init_wakeup(&udev->dev, 0);
+ }
- usb_detect_quirks(udev); /* Determine quirks */
- err = usb_configure_device(udev); /* detect & probe dev/intfs */
+ /* Tell the runtime-PM framework the device is active */
+ pm_runtime_set_active(&udev->dev);
+ pm_runtime_enable(&udev->dev);
+
+ err = usb_enumerate_device(udev); /* Read descriptors */
if (err < 0)
goto fail;
dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
/* Tell the world! */
announce_device(udev);
+ device_enable_async_suspend(&udev->dev);
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others).
fail:
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
- usb_stop_pm(udev);
+ pm_runtime_disable(&udev->dev);
+ pm_runtime_set_suspended(&udev->dev);
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
if (!(portstatus & USB_PORT_STAT_RESET) &&
(portstatus & USB_PORT_STAT_ENABLE)) {
if (hub_is_wusb(hub))
- udev->speed = USB_SPEED_VARIABLE;
+ udev->speed = USB_SPEED_WIRELESS;
+ else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
+ udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
struct usb_device *udev, unsigned int delay)
{
int i, status;
+ struct usb_hcd *hcd;
+ hcd = bus_to_hcd(udev->bus);
/* Block EHCI CF initialization during the port reset.
* Some companion controllers don't like it when they mix.
*/
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
update_address(udev, 0);
+ if (hcd->driver->reset_device) {
+ status = hcd->driver->reset_device(hcd, udev);
+ if (status < 0) {
+ dev_err(&udev->dev, "Cannot reset "
+ "HCD device state\n");
+ break;
+ }
+ }
/* FALL THROUGH */
case -ENOTCONN:
case -ENODEV:
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,
}
/* caller has locked udev */
-static int remote_wakeup(struct usb_device *udev)
+int usb_remote_wakeup(struct usb_device *udev)
{
int status = 0;
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, PMSG_REMOTE_RESUME);
+ status = usb_autoresume_device(udev);
+ if (status == 0) {
+ /* Let the drivers do their thing, then... */
+ usb_autosuspend_device(udev);
+ }
}
return status;
}
return status;
}
-static inline int remote_wakeup(struct usb_device *udev)
-{
- return 0;
-}
-
#endif
static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
#else /* CONFIG_PM */
-static inline int remote_wakeup(struct usb_device *udev)
-{
- return 0;
-}
-
#define hub_suspend NULL
#define hub_resume NULL
#define hub_reset_resume NULL
mutex_lock(&usb_address0_mutex);
- 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;
- } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+ 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 {
*/
switch (udev->speed) {
case USB_SPEED_SUPER:
- case USB_SPEED_VARIABLE: /* fixed at 512 */
+ case USB_SPEED_WIRELESS: /* fixed at 512 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
break;
case USB_SPEED_HIGH: /* fixed at 64 */
case USB_SPEED_SUPER:
speed = "super";
break;
- case USB_SPEED_VARIABLE:
+ case USB_SPEED_WIRELESS:
speed = "variable";
type = "Wireless ";
break;
else
i = udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
- if (udev->speed != USB_SPEED_FULL ||
+ if (udev->speed == USB_SPEED_LOW ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
- dev_err(&udev->dev, "ep0 maxpacket = %d\n", i);
+ dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
retval = -EMSGSIZE;
goto fail;
}
- dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
+ if (udev->speed == USB_SPEED_FULL)
+ dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
+ else
+ dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
}
retval = 0;
-
+ /* notify HCD that we have a device connected and addressed */
+ if (hcd->driver->update_device)
+ hcd->driver->update_device(hcd, udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
/* For a suspended device, treat this as a
* remote wakeup event.
*/
- status = remote_wakeup(udev);
+ status = usb_remote_wakeup(udev);
#endif
} else {
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
+ /* 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);
}
}
- /* Return now if debouncing failed or nothing is connected */
- if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+ /* 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)))
+ && !(portstatus & USB_PORT_STAT_POWER))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
if (!(hcd->driver->flags & HCD_USB3))
udev->speed = USB_SPEED_UNKNOWN;
else if ((hdev->parent == NULL) &&
- (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)))
+ (portstatus & USB_PORT_STAT_SUPER_SPEED))
udev->speed = USB_SPEED_SUPER;
else
udev->speed = USB_SPEED_UNKNOWN;
if (status < 0)
goto loop;
+ usb_detect_quirks(udev);
+ if (udev->quirks & USB_QUIRK_DELAY_INIT)
+ msleep(1000);
+
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
* a "powered" LED, users should notice we didn't enable it
loop:
usb_ep0_reinit(udev);
release_address(udev);
+ hub_free_dev(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
* disconnected while waiting for the lock to succeed. */
usb_lock_device(hdev);
if (unlikely(hub->disconnected))
- goto loop;
+ goto loop_disconnected;
/* 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->
+ ret = usb_remote_wakeup(hdev->
children[i-1]);
usb_unlock_device(udev);
if (ret < 0)
}
}
-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);
+ loop_disconnected:
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
return 0;
}
-static struct usb_device_id hub_id_table [] = {
+static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
- .ioctl = hub_ioctl,
+ .unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
{
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;
/* 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.
* 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;
desc = &intf->cur_altsetting->desc;
usb_enable_interface(udev, intf, true);
ret = 0;
} else {
+ /* Let the bandwidth allocation function know that this
+ * device has been reset, and it will have to use
+ * alternate setting 0 as the current alternate setting.
+ */
+ intf->resetting_device = 1;
ret = usb_set_interface(udev, desc->bInterfaceNumber,
desc->bAlternateSetting);
+ intf->resetting_device = 0;
}
if (ret < 0) {
dev_err(&udev->dev, "failed to restore interface %d "