#include <linux/mutex.h>
#include <linux/freezer.h>
-#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include "hcd.h"
#include "hub.h"
-#ifdef CONFIG_USB_PERSIST
-#define USB_PERSIST 1
-#else
-#define USB_PERSIST 0
+/* if we are in debug mode, always announce new devices */
+#ifdef DEBUG
+#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
+#define CONFIG_USB_ANNOUNCE_NEW_DEVICES
+#endif
#endif
struct usb_hub {
return status;
}
+static int hub_port_status(struct usb_hub *hub, int port1,
+ u16 *status, u16 *change)
+{
+ int ret;
+
+ mutex_lock(&hub->status_mutex);
+ ret = get_port_status(hub->hdev, port1, &hub->status->port);
+ if (ret < 4) {
+ dev_err(hub->intfdev,
+ "%s failed (err = %d)\n", __func__, ret);
+ if (ret >= 0)
+ ret = -EIO;
+ } else {
+ *status = le16_to_cpu(hub->status->port.wPortStatus);
+ *change = le16_to_cpu(hub->status->port.wPortChange);
+ ret = 0;
+ }
+ mutex_unlock(&hub->status_mutex);
+ return ret;
+}
+
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
schedule_work (&tt->kevent);
spin_unlock_irqrestore (&tt->lock, flags);
}
+EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer);
static void hub_power_on(struct usb_hub *hub)
{
ret = get_hub_status(hub->hdev, &hub->status->hub);
if (ret < 0)
dev_err (hub->intfdev,
- "%s failed (err = %d)\n", __FUNCTION__, ret);
+ "%s failed (err = %d)\n", __func__, ret);
else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
*change = le16_to_cpu(hub->status->hub.wHubChange);
}
/* caller has locked the hub device */
-static int hub_pre_reset(struct usb_interface *intf)
+static void hub_stop(struct usb_hub *hub)
{
- struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = hub->hdev;
int i;
usb_disconnect(&hdev->children[i]);
}
hub_quiesce(hub);
+}
+
+#define HUB_RESET 1
+#define HUB_RESUME 2
+#define HUB_RESET_RESUME 3
+
+#ifdef CONFIG_PM
+
+static void hub_restart(struct usb_hub *hub, int type)
+{
+ struct usb_device *hdev = hub->hdev;
+ int port1;
+
+ /* Check each of the children to see if they require
+ * USB-PERSIST handling or disconnection. Also check
+ * each unoccupied port to make sure it is still disabled.
+ */
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+ struct usb_device *udev = hdev->children[port1-1];
+ int status = 0;
+ u16 portstatus, portchange;
+
+ if (!udev || udev->state == USB_STATE_NOTATTACHED) {
+ if (type != HUB_RESET) {
+ status = hub_port_status(hub, port1,
+ &portstatus, &portchange);
+ if (status == 0 && (portstatus &
+ USB_PORT_STAT_ENABLE))
+ clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
+ continue;
+ }
+
+ /* Was the power session lost while we were suspended? */
+ switch (type) {
+ case HUB_RESET_RESUME:
+ portstatus = 0;
+ portchange = USB_PORT_STAT_C_CONNECTION;
+ break;
+
+ case HUB_RESET:
+ case HUB_RESUME:
+ status = hub_port_status(hub, port1,
+ &portstatus, &portchange);
+ break;
+ }
+
+ /* For "USB_PERSIST"-enabled children we must
+ * mark the child device for reset-resume and
+ * turn off the various status changes to prevent
+ * khubd from disconnecting it later.
+ */
+ if (udev->persist_enabled && status == 0 &&
+ !(portstatus & USB_PORT_STAT_ENABLE)) {
+ if (portchange & USB_PORT_STAT_C_ENABLE)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_ENABLE);
+ if (portchange & USB_PORT_STAT_C_CONNECTION)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ udev->reset_resume = 1;
+ }
+
+ /* Otherwise for a reset_resume we must disconnect the child,
+ * but as we may not lock the child device here
+ * we have to do a "logical" disconnect.
+ */
+ else if (type == HUB_RESET_RESUME)
+ hub_port_logical_disconnect(hub, port1);
+ }
+
+ hub_activate(hub);
+}
+
+#endif /* CONFIG_PM */
+
+/* caller has locked the hub device */
+static int hub_pre_reset(struct usb_interface *intf)
+{
+ struct usb_hub *hub = usb_get_intfdata(intf);
+
+ hub_stop(hub);
return 0;
}
/* Disconnect all children and quiesce the hub */
hub->error = 0;
- hub_pre_reset(intf);
+ hub_stop(hub);
usb_set_intfdata (intf, NULL);
if (udev->children[i])
recursively_mark_NOTATTACHED(udev->children[i]);
}
- if (udev->state == USB_STATE_SUSPENDED)
+ if (udev->state == USB_STATE_SUSPENDED) {
udev->discon_suspended = 1;
+ udev->active_duration -= jiffies;
+ }
udev->state = USB_STATE_NOTATTACHED;
}
else
device_init_wakeup(&udev->dev, 0);
}
+ if (udev->state == USB_STATE_SUSPENDED &&
+ new_state != USB_STATE_SUSPENDED)
+ udev->active_duration -= jiffies;
+ else if (new_state == USB_STATE_SUSPENDED &&
+ udev->state != USB_STATE_SUSPENDED)
+ udev->active_duration += jiffies;
udev->state = new_state;
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
+/*
+ * WUSB devices are simple: they have no hubs behind, so the mapping
+ * device <-> virtual port number becomes 1:1. Why? to simplify the
+ * life of the device connection logic in
+ * drivers/usb/wusbcore/devconnect.c. When we do the initial secret
+ * handshake we need to assign a temporary address in the unauthorized
+ * space. For simplicity we use the first virtual port number found to
+ * be free [drivers/usb/wusbcore/devconnect.c:wusbhc_devconnect_ack()]
+ * and that becomes it's address [X < 128] or its unauthorized address
+ * [X | 0x80].
+ *
+ * We add 1 as an offset to the one-based USB-stack port number
+ * (zero-based wusb virtual port index) for two reasons: (a) dev addr
+ * 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.
+ */
static void choose_address(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
/* If khubd ever becomes multithreaded, this will need a lock */
-
- /* Try to allocate the next devnum beginning at bus->devnum_next. */
- devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
- bus->devnum_next);
- if (devnum >= 128)
- devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
-
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
-
+ if (udev->wusb) {
+ devnum = udev->portnum + 1;
+ BUG_ON(test_bit(devnum, bus->devmap.devicemap));
+ } else {
+ /* Try to allocate the next devnum beginning at
+ * bus->devnum_next. */
+ devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
+ bus->devnum_next);
+ if (devnum >= 128)
+ devnum = find_next_zero_bit(bus->devmap.devicemap,
+ 128, 1);
+ bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ }
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
}
+static void update_address(struct usb_device *udev, int devnum)
+{
+ /* The address for a WUSB device is managed by wusbcore. */
+ if (!udev->wusb)
+ udev->devnum = devnum;
+}
+
#ifdef CONFIG_USB_SUSPEND
static void usb_stop_pm(struct usb_device *udev)
int i;
if (!udev) {
- pr_debug ("%s nodev\n", __FUNCTION__);
+ pr_debug ("%s nodev\n", __func__);
return;
}
put_device(&udev->dev);
}
-#ifdef DEBUG
+#ifdef CONFIG_USB_ANNOUNCE_NEW_DEVICES
static void show_string(struct usb_device *udev, char *id, char *string)
{
if (!string)
dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string);
}
+static void announce_device(struct usb_device *udev)
+{
+ 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",
+ udev->descriptor.iManufacturer,
+ udev->descriptor.iProduct,
+ udev->descriptor.iSerialNumber);
+ show_string(udev, "Product", udev->product);
+ show_string(udev, "Manufacturer", udev->manufacturer);
+ show_string(udev, "SerialNumber", udev->serial);
+}
#else
-static inline void show_string(struct usb_device *udev, char *id, char *string)
-{}
+static inline void announce_device(struct usb_device *udev) { }
#endif
-
#ifdef CONFIG_USB_OTG
#include "otg_whitelist.h"
#endif
}
/* Tell the world! */
- dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
- "SerialNumber=%d\n",
- udev->descriptor.iManufacturer,
- udev->descriptor.iProduct,
- udev->descriptor.iSerialNumber);
- show_string(udev, "Product", udev->product);
- show_string(udev, "Manufacturer", udev->manufacturer);
- show_string(udev, "SerialNumber", udev->serial);
+ announce_device(udev);
return err;
fail:
}
-static int hub_port_status(struct usb_hub *hub, int port1,
- u16 *status, u16 *change)
-{
- int ret;
-
- mutex_lock(&hub->status_mutex);
- ret = get_port_status(hub->hdev, port1, &hub->status->port);
- if (ret < 4) {
- dev_err (hub->intfdev,
- "%s failed (err = %d)\n", __FUNCTION__, ret);
- if (ret >= 0)
- ret = -EIO;
- } else {
- *status = le16_to_cpu(hub->status->port.wPortStatus);
- *change = le16_to_cpu(hub->status->port.wPortChange);
- ret = 0;
- }
- mutex_unlock(&hub->status_mutex);
- return ret;
-}
-
-
/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
static unsigned hub_is_wusb(struct usb_hub *hub)
{
case 0:
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
- udev->devnum = 0; /* Device now at address 0 */
+ update_address(udev, 0);
/* FALL THROUGH */
case -ENOTCONN:
case -ENODEV:
* the host and the device is the same as it was when the device
* suspended.
*
- * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this
- * routine won't check that the port is still enabled. Furthermore,
- * if @udev->reset_resume is set then finish_port_resume() above will
+ * If @udev->reset_resume is set then this routine won't check that the
+ * port is still enabled. Furthermore, finish_port_resume() above will
* reset @udev. The end result is that a broken power session can be
* recovered and @udev will appear to persist across a loss of VBUS power.
*
* to it will be lost. Using the USB_PERSIST facility, the device can be
* made to appear as if it had not disconnected.
*
- * This facility is inherently dangerous. Although usb_reset_device()
- * makes every effort to insure that the same device is present after the
+ * This facility can be dangerous. Although usb_reset_device() makes
+ * every effort to insure that the same device is present after the
* reset as before, it cannot provide a 100% guarantee. Furthermore it's
* quite possible for a device to remain unaltered but its media to be
* changed. If the user replaces a flash memory card while the system is
status = hub_port_status(hub, port1, &portstatus, &portchange);
SuspendCleared:
- if (USB_PERSIST && udev->reset_resume)
+ if (udev->reset_resume)
want_flags = USB_PORT_STAT_POWER
| USB_PORT_STAT_CONNECTION;
else
}
clear_bit(port1, hub->busy_bits);
- if (!hub->hdev->parent && !hub->busy_bits[0])
- usb_enable_root_hub_irq(hub->hdev->bus);
if (status == 0)
status = finish_port_resume(udev);
}
}
- dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
+ dev_dbg(&intf->dev, "%s\n", __func__);
/* stop khubd and related activity */
hub_quiesce(hub);
static int hub_resume(struct usb_interface *intf)
{
- struct usb_hub *hub = usb_get_intfdata (intf);
-
- dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
+ struct usb_hub *hub = usb_get_intfdata(intf);
- /* tell khubd to look for changes on this hub */
- hub_activate(hub);
+ dev_dbg(&intf->dev, "%s\n", __func__);
+ hub_restart(hub, HUB_RESUME);
return 0;
}
static int hub_reset_resume(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
- struct usb_device *hdev = hub->hdev;
- int port1;
+ dev_dbg(&intf->dev, "%s\n", __func__);
hub_power_on(hub);
-
- for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- struct usb_device *child = hdev->children[port1-1];
-
- if (child) {
-
- /* For "USB_PERSIST"-enabled children we must
- * mark the child device for reset-resume and
- * turn off the connect-change status to prevent
- * khubd from disconnecting it later.
- */
- if (USB_PERSIST && child->persist_enabled) {
- child->reset_resume = 1;
- clear_port_feature(hdev, port1,
- USB_PORT_FEAT_C_CONNECTION);
-
- /* Otherwise we must disconnect the child,
- * but as we may not lock the child device here
- * we have to do a "logical" disconnect.
- */
- } else {
- hub_port_logical_disconnect(hub, port1);
- }
- }
- }
-
- hub_activate(hub);
+ hub_restart(hub, HUB_RESET_RESUME);
return 0;
}
*
* The USB host controller driver calls this function when its root hub
* is resumed and Vbus power has been interrupted or the controller
- * has been reset. The routine marks @rhdev as having lost power. When
- * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST
- * is enabled then it will carry out power-session recovery, otherwise
- * it will disconnect all the child devices.
+ * has been reset. The routine marks @rhdev as having lost power.
+ * When the hub driver is resumed it will take notice and carry out
+ * power-session recovery for all the "USB-PERSIST"-enabled child devices;
+ * the others will be disconnected.
*/
void usb_root_hub_lost_power(struct usb_device *rhdev)
{
return portstatus;
}
-static void ep0_reinit(struct usb_device *udev)
+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);
}
+EXPORT_SYMBOL_GPL(usb_ep0_reinit);
#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN)
USB_REQ_SET_ADDRESS, 0, devnum, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (retval == 0) {
- udev->devnum = devnum; /* Device now using proper address */
+ /* Device now using proper address. */
+ update_address(udev, devnum);
usb_set_device_state(udev, USB_STATE_ADDRESS);
- ep0_reinit(udev);
+ usb_ep0_reinit(udev);
}
return retval;
}
#undef GET_DESCRIPTOR_BUFSIZE
}
- for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
- retval = hub_set_address(udev, devnum);
- if (retval >= 0)
+ /*
+ * If device is WUSB, we already assigned an
+ * unauthorized address in the Connect Ack sequence;
+ * authorization will assign the final address.
+ */
+ if (udev->wusb == 0) {
+ for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
+ retval = hub_set_address(udev, devnum);
+ if (retval >= 0)
+ break;
+ msleep(200);
+ }
+ if (retval < 0) {
+ dev_err(&udev->dev,
+ "device not accepting address %d, error %d\n",
+ devnum, retval);
+ goto fail;
+ }
+
+ /* 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))
break;
- msleep(200);
- }
- if (retval < 0) {
- dev_err(&udev->dev,
- "device not accepting address %d, error %d\n",
- devnum, retval);
- goto fail;
- }
-
- /* 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))
- break;
+ }
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
if (retval)
goto fail;
- i = udev->descriptor.bMaxPacketSize0 == 0xff?
+ i = udev->descriptor.bMaxPacketSize0 == 0xff? /* wusb device? */
512 : udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed != USB_SPEED_FULL ||
}
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
- ep0_reinit(udev);
+ usb_ep0_reinit(udev);
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
- udev->devnum = devnum; /* for disconnect processing */
+ update_address(udev, devnum); /* for disconnect processing */
}
mutex_unlock(&usb_address0_mutex);
return retval;
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);
loop_disable:
hub_port_disable(hub, port1, 1);
loop:
- ep0_reinit(udev);
+ usb_ep0_reinit(udev);
release_address(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
+ dev_err(hub_dev, "unable to enumerate USB device on port %d\n", port1);
done:
hub_port_disable(hub, port1, 1);
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
- hub_pre_reset(intf);
+ hub_stop(hub);
goto loop;
}
hub->activating = 0;
- /* If this is a root hub, tell the HCD it's okay to
- * re-enable port-change interrupts now. */
- if (!hdev->parent && !hub->busy_bits[0])
- usb_enable_root_hub_irq(hdev->bus);
-
loop_autopm:
/* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
static int hub_thread(void *__unused)
{
+ /* khubd needs to be freezable to avoid intefering with USB-PERSIST
+ * port handover. Otherwise it might see that a full-speed device
+ * was gone before the EHCI controller had handed its port over to
+ * the companion full-speed controller.
+ */
set_freezable();
+
do {
hub_events();
wait_event_freezable(khubd_wait,
usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */
-static int config_descriptors_changed(struct usb_device *udev)
+static int descriptors_changed(struct usb_device *udev,
+ struct usb_device_descriptor *old_device_descriptor)
{
- unsigned index;
- unsigned len = 0;
- struct usb_config_descriptor *buf;
+ int changed = 0;
+ unsigned index;
+ unsigned serial_len = 0;
+ unsigned len;
+ unsigned old_length;
+ int length;
+ char *buf;
+
+ if (memcmp(&udev->descriptor, old_device_descriptor,
+ sizeof(*old_device_descriptor)) != 0)
+ return 1;
+
+ /* Since the idVendor, idProduct, and bcdDevice values in the
+ * device descriptor haven't changed, we will assume the
+ * Manufacturer and Product strings haven't changed either.
+ * But the SerialNumber string could be different (e.g., a
+ * different flash card of the same brand).
+ */
+ if (udev->serial)
+ serial_len = strlen(udev->serial) + 1;
+ len = serial_len;
for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
- if (len < le16_to_cpu(udev->config[index].desc.wTotalLength))
- len = le16_to_cpu(udev->config[index].desc.wTotalLength);
+ old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
+ len = max(len, old_length);
}
+
buf = kmalloc(len, GFP_NOIO);
if (buf == NULL) {
dev_err(&udev->dev, "no mem to re-read configs after reset\n");
return 1;
}
for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
- int length;
- int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
-
+ old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
old_length);
- if (length < old_length) {
+ if (length != old_length) {
dev_dbg(&udev->dev, "config index %d, error %d\n",
index, length);
+ changed = 1;
break;
}
if (memcmp (buf, udev->rawdescriptors[index], old_length)
!= 0) {
dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
- index, buf->bConfigurationValue);
+ index,
+ ((struct usb_config_descriptor *) buf)->
+ bConfigurationValue);
+ changed = 1;
break;
}
}
+
+ if (!changed && serial_len) {
+ length = usb_string(udev, udev->descriptor.iSerialNumber,
+ buf, serial_len);
+ if (length + 1 != serial_len) {
+ dev_dbg(&udev->dev, "serial string error %d\n",
+ length);
+ changed = 1;
+ } else if (memcmp(buf, udev->serial, length) != 0) {
+ dev_dbg(&udev->dev, "serial string changed\n");
+ changed = 1;
+ }
+ }
+
kfree(buf);
- return index != udev->descriptor.bNumConfigurations;
+ return changed;
}
/**
if (!parent_hdev) {
/* this requires hcd-specific logic; see OHCI hc_restart() */
- dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
+ dev_dbg(&udev->dev, "%s for root hub!\n", __func__);
return -EISDIR;
}
parent_hub = hdev_to_hub(parent_hdev);
/* ep0 maxpacket size may change; let the HCD know about it.
* Other endpoints will be handled by re-enumeration. */
- ep0_reinit(udev);
+ usb_ep0_reinit(udev);
ret = hub_port_init(parent_hub, udev, port1, i);
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
break;
}
clear_bit(port1, parent_hub->busy_bits);
- if (!parent_hdev->parent && !parent_hub->busy_bits[0])
- usb_enable_root_hub_irq(parent_hdev->bus);
if (ret < 0)
goto re_enumerate;
/* Device might have changed firmware (DFU or similar) */
- if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor)
- || config_descriptors_changed (udev)) {
+ if (descriptors_changed(udev, &descriptor)) {
dev_info(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
-EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL_GPL(usb_reset_device);
/**
* usb_reset_composite_device - warn interface drivers and perform a USB port reset
* this from a driver probe() routine after downloading new firmware.
* For calls that might not occur during probe(), drivers should lock
* the device using usb_lock_device_for_reset().
- *
- * The interface locks are acquired during the pre_reset stage and released
- * during the post_reset stage. However if iface is not NULL and is
- * currently being probed, we assume that the caller already owns its
- * lock.
*/
int usb_reset_composite_device(struct usb_device *udev,
struct usb_interface *iface)
{
int ret;
+ int i;
struct usb_host_config *config = udev->actconfig;
if (udev->state == USB_STATE_NOTATTACHED ||
iface = NULL;
if (config) {
- int i;
- struct usb_interface *cintf;
- struct usb_driver *drv;
-
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
- cintf = config->interface[i];
- if (cintf != iface)
- down(&cintf->dev.sem);
- if (device_is_registered(&cintf->dev) &&
- cintf->dev.driver) {
+ struct usb_interface *cintf = config->interface[i];
+ struct usb_driver *drv;
+
+ if (cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->pre_reset)
(drv->pre_reset)(cintf);
ret = usb_reset_device(udev);
if (config) {
- int i;
- struct usb_interface *cintf;
- struct usb_driver *drv;
-
for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
- cintf = config->interface[i];
- if (device_is_registered(&cintf->dev) &&
- cintf->dev.driver) {
+ struct usb_interface *cintf = config->interface[i];
+ struct usb_driver *drv;
+
+ if (cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->post_reset)
(drv->post_reset)(cintf);
/* FIXME: Unbind if post_reset returns an error or isn't defined */
}
- if (cintf != iface)
- up(&cintf->dev.sem);
}
}
usb_autosuspend_device(udev);
return ret;
}
-EXPORT_SYMBOL(usb_reset_composite_device);
+EXPORT_SYMBOL_GPL(usb_reset_composite_device);