ChangeSet 1.1587.3.40, 2004/05/05 14:10:42-07:00, [EMAIL PROTECTED]
[PATCH] PATCH: (as268) Import device-reset changes from gadget-2.6 tree
This patch imports the changes that David Brownell has made to the
device-reset functions in his gadget-2.6 tree. Once these ongoing
troubling questions about locking are settled, I'll add support for the
"descriptors changed" case.
drivers/usb/core/hub.c | 196 ++++++++++++++++++++++++-------------------------
1 files changed, 99 insertions(+), 97 deletions(-)
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c Fri May 14 15:29:31 2004
+++ b/drivers/usb/core/hub.c Fri May 14 15:29:31 2004
@@ -38,7 +38,6 @@
/* Wakes up khubd */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
-static DECLARE_MUTEX(usb_address0_sem);
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
static LIST_HEAD(hub_list); /* List of all hubs (for cleanup) */
@@ -962,7 +961,7 @@
return -1;
}
-int hub_port_disable(struct usb_device *hub, int port)
+static int hub_port_disable(struct usb_device *hub, int port)
{
int ret;
@@ -1042,6 +1041,8 @@
static int
hub_port_init (struct usb_device *hub, struct usb_device *dev, int port)
{
+ static DECLARE_MUTEX(usb_address0_sem);
+
int i, j, retval = -ENODEV;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = dev->speed;
@@ -1447,7 +1448,7 @@
hub_port_connect_change(hub, i, portstatus,
portchange);
} else if (portchange & USB_PORT_STAT_C_ENABLE) {
dev_dbg (hubdev (dev),
- "port %d enable change, status %x\n",
+ "port %d enable change, status %08x\n",
i + 1, portstatus);
clear_port_feature(dev,
i + 1, USB_PORT_FEAT_C_ENABLE);
@@ -1559,6 +1560,9 @@
.id_table = hub_id_table,
};
+/*
+ * This should be a separate module.
+ */
int usb_hub_init(void)
{
pid_t pid;
@@ -1602,25 +1606,68 @@
usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */
+
+static int config_descriptors_changed(struct usb_device *dev)
+{
+ unsigned index;
+ unsigned len = 0;
+ struct usb_config_descriptor *buf;
+
+ for (index = 0; index < dev->descriptor.bNumConfigurations; index++) {
+ if (len < dev->config[index].desc.wTotalLength)
+ len = dev->config[index].desc.wTotalLength;
+ }
+ buf = kmalloc (len, SLAB_KERNEL);
+ if (buf == 0) {
+ dev_err(&dev->dev, "no mem to re-read configs after reset\n");
+ /* assume the worst */
+ return 1;
+ }
+ for (index = 0; index < dev->descriptor.bNumConfigurations; index++) {
+ int length;
+ int old_length = dev->config[index].desc.wTotalLength;
+
+ length = usb_get_descriptor(dev, USB_DT_CONFIG, index, buf,
+ old_length);
+ if (length < old_length) {
+ dev_dbg(&dev->dev, "config index %d, error %d\n",
+ index, length);
+ break;
+ }
+ if (memcmp (buf, dev->rawdescriptors[index], old_length)
+ != 0) {
+ dev_dbg(&dev->dev, "config index %d changed (#%d)\n",
+ index, buf->bConfigurationValue);
+/* FIXME enable this when we can re-enumerate after reset;
+ * until then DFU-ish drivers need this and other workarounds
+ */
+// break;
+ }
+ }
+ kfree(buf);
+ return index != dev->descriptor.bNumConfigurations;
+}
+
/*
- * WARNING - If a driver calls usb_reset_device, you should simulate a
- * disconnect() and probe() for other interfaces you doesn't claim. This
- * is left up to the driver writer right now. This insures other drivers
- * have a chance to re-setup their interface.
+ * WARNING - don't reset any device unless drivers for all of its
+ * interfaces are expecting that reset! Maybe some driver->reset()
+ * method should eventually help ensure sufficient cooperation.
*
- * Take a look at proc_resetdevice in devio.c for some sample code to
- * do this.
- * Use this only from within your probe function, otherwise use
- * usb_reset_device() below, which ensure proper locking
+ * This is the same as usb_reset_device() except that the caller
+ * already holds dev->serialize. For example, it's safe to use
+ * this from a driver probe() routine after downloading new firmware.
*/
-int usb_physical_reset_device(struct usb_device *dev)
+int __usb_reset_device(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
- struct usb_device_descriptor *descriptor;
+ struct usb_device_descriptor descriptor = dev->descriptor;
int i, ret, port = -1;
- if (!parent) {
- err("attempting to reset root hub!");
+ if (dev->maxchild) {
+ /* this requires hub- or hcd-specific logic;
+ * see hub_reset() and OHCI hc_restart()
+ */
+ dev_dbg(&dev->dev, "%s for hub!\n", __FUNCTION__);
return -EINVAL;
}
@@ -1633,114 +1680,69 @@
if (port < 0)
return -ENOENT;
- descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);
- if (!descriptor) {
- return -ENOMEM;
- }
-
- down(&usb_address0_sem);
-
- /* Send a reset to the device */
- if (hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) {
- hub_port_disable(parent, port);
- up(&usb_address0_sem);
- kfree(descriptor);
- return(-ENODEV);
- }
-
- /* Reprogram the Address */
- ret = usb_set_address(dev);
- if (ret < 0) {
- err("USB device not accepting new address (error=%d)", ret);
- hub_port_disable(parent, port);
- up(&usb_address0_sem);
- kfree(descriptor);
- return ret;
- }
-
- /* Let the SET_ADDRESS settle */
- wait_ms(10);
-
- up(&usb_address0_sem);
-
- /*
- * Now we fetch the configuration descriptors for the device and
- * see if anything has changed. If it has, we dump the current
- * parsed descriptors and reparse from scratch. Then we leave
- * the device alone for the caller to finish setting up.
- *
- * If nothing changed, we reprogram the configuration and then
- * the alternate settings.
- */
-
- ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
- sizeof(*descriptor));
- if (ret < 0) {
- kfree(descriptor);
- return ret;
- }
-
- le16_to_cpus(&descriptor->bcdUSB);
- le16_to_cpus(&descriptor->idVendor);
- le16_to_cpus(&descriptor->idProduct);
- le16_to_cpus(&descriptor->bcdDevice);
-
- if (memcmp(&dev->descriptor, descriptor, sizeof(*descriptor))) {
- kfree(descriptor);
- usb_destroy_configuration(dev);
-
- /* FIXME Linux doesn't yet handle these "device morphed"
- * paths. DFU variants need this to work ... and they
- * include the "config descriptors changed" case this
- * doesn't yet detect!
- */
- dev->state = USB_STATE_NOTATTACHED;
- dev_err(&dev->dev, "device morphed (DFU?), nyet supported\n");
-
- return -ENODEV;
- }
-
- kfree(descriptor);
+ ret = hub_port_init(parent, dev, port);
+ if (ret < 0)
+ goto re_enumerate;
+
+ /* Device might have changed firmware (DFU or similar) */
+ if (memcmp(&dev->descriptor, &descriptor, sizeof descriptor)
+ || config_descriptors_changed (dev)) {
+ dev_info(&dev->dev, "device firmware changed\n");
+ dev->descriptor = descriptor; /* for disconnect() calls */
+ goto re_enumerate;
+ }
+
+ if (!dev->actconfig)
+ return 0;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0,
dev->actconfig->desc.bConfigurationValue, 0,
NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
if (ret < 0) {
- err("failed to set dev %s active configuration (error=%d)",
- dev->devpath, ret);
- return ret;
- }
+ dev_err(&dev->dev,
+ "can't restore configuration #%d (error=%d)\n",
+ dev->actconfig->desc.bConfigurationValue, ret);
+ goto re_enumerate;
+ }
dev->state = USB_STATE_CONFIGURED;
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf = dev->actconfig->interface[i];
struct usb_interface_descriptor *desc;
+ /* set_interface resets host side toggle and halt status even
+ * for altsetting zero. the interface may have no driver.
+ */
desc = &intf->cur_altsetting->desc;
ret = usb_set_interface(dev, desc->bInterfaceNumber,
desc->bAlternateSetting);
if (ret < 0) {
- err("failed to set active alternate setting "
- "for dev %s interface %d (error=%d)",
- dev->devpath, desc->bInterfaceNumber, ret);
- return ret;
+ dev_err(&dev->dev, "failed to restore interface %d "
+ "altsetting %d (error=%d)\n",
+ desc->bInterfaceNumber,
+ desc->bAlternateSetting,
+ ret);
+ goto re_enumerate;
}
}
return 0;
+
+re_enumerate:
+ /* FIXME make some task re-enumerate; don't just mark unusable */
+ dev->state = USB_STATE_NOTATTACHED;
+ return -ENODEV;
}
+EXPORT_SYMBOL(__usb_reset_device);
int usb_reset_device(struct usb_device *udev)
{
- struct device *gdev = &udev->dev;
int r;
- down_read(&gdev->bus->subsys.rwsem);
- r = usb_physical_reset_device(udev);
- up_read(&gdev->bus->subsys.rwsem);
+ down(&udev->serialize);
+ r = __usb_reset_device(udev);
+ up(&udev->serialize);
return r;
}
-
-
-------------------------------------------------------
This SF.Net email is sponsored by: SourceForge.net Broadband
Sign-up now for SourceForge Broadband and get the fastest
6.0/768 connection for only $19.95/mo for the first 3 months!
http://ads.osdn.com/?ad_id%62&alloc_ida84&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel