Hi Greg,
nobody objected, so here is a patch to do configuration changes cleanly.
This is against 2.5. Please apply.
Several suggestions for enhancements were made and I'll follow up on them,
but this here is the basic bugfix.
It does:
-seperate logical and physical configuration changing
-disconnects and reconnects drivers correctly on configuration changes
-introduces the necessary locking
Regards
Oliver
--- usb-2.5/drivers/usb/core/hub.c 2002-10-12 14:49:17.000000000 +0200
+++ con25/drivers/usb/core/hub.c 2002-10-11 15:37:49.000000000 +0200
@@ -1237,7 +1236,7 @@
return 1;
}
- ret = usb_set_configuration(dev, dev->actconfig->bConfigurationValue);
+ ret = usb_physical_set_conf(dev, dev->actconfig->bConfigurationValue);
if (ret < 0) {
err("failed to set dev %s active configuration (error=%d)",
dev->devpath, ret);
--- usb-2.5/drivers/usb/core/message.c 2002-10-08 21:30:09.000000000 +0200
+++ con25/drivers/usb/core/message.c 2002-10-12 21:31:02.000000000 +0200
@@ -838,6 +838,54 @@
}
/**
+ * usb_physical_set_conf - send the actual message changing configuration
+ * @dev: the device whose configuration is being updated
+ * @configuration: the configuration being chosen.
+ * Context: !in_interrupt ()
+ *
+ * Caller must make sure that disconnect processing waits for this to complete
+ */
+static inline struct usb_config_descriptor *find_valid_config(struct usb_device *dev,
+int configuration)
+{
+ int i;
+
+ for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+ if (dev->config[i].bConfigurationValue == configuration) {
+ return &dev->config[i];
+
+ }
+ }
+
+ return NULL;
+}
+
+int usb_physical_set_conf(struct usb_device *dev, int configuration)
+{
+ int r;
+ struct usb_config_descriptor *cp;
+
+ r = -EINVAL;
+ cp = find_valid_config(dev, configuration);
+ if (!cp)
+ goto err;
+
+ r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
+ NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
+ if (r)
+ goto err;
+
+ dev->actconfig = cp;
+ dev->toggle[0] = 0;
+ dev->toggle[1] = 0;
+ usb_set_maxpacket(dev);
+
+err:
+ return r;
+
+}
+
+/**
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
* @configuration: the configuration being chosen.
@@ -871,7 +919,7 @@
{
int i, ret;
struct usb_config_descriptor *cp = NULL;
-
+
for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].bConfigurationValue == configuration) {
cp = &dev->config[i];
@@ -883,17 +931,28 @@
return -EINVAL;
}
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
- NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
- return ret;
+ down(&dev->serialize);
- dev->actconfig = cp;
- dev->toggle[0] = 0;
- dev->toggle[1] = 0;
- usb_set_maxpacket(dev);
+ for (i = 0; i < USB_MAXCHILDREN; i++) {
+ struct usb_device **child = dev->children + i;
+ if (*child) {
+ ret = -EBUSY;
+ goto err; /* refuse if children were harmed */
+ }
+ }
+
+ usb_reap_interfaces(dev); /* get rid of all interfaces */
+
+ if ((ret = usb_physical_set_conf(dev, configuration)))
+ goto err;
+
+ dev->desired_conf = configuration; /* for pm */
+
+ ret = usb_register_interfaces(dev); /* reevaluate device */
- return 0;
+err:
+ up(&dev->serialize);
+ return ret;
}
--- usb-2.5/drivers/usb/core/usb.c 2002-10-12 14:49:17.000000000 +0200
+++ con25/drivers/usb/core/usb.c 2002-10-12 21:28:47.000000000 +0200
@@ -78,6 +75,7 @@
{
struct usb_interface * intf = to_usb_interface(dev);
struct usb_driver * driver = to_usb_driver(dev->driver);
+ struct usb_device * udev = interface_to_usbdev(intf);
const struct usb_device_id *id;
int error = -ENODEV;
int m;
@@ -97,7 +95,9 @@
if (id) {
dbg ("%s - got id", __FUNCTION__);
down (&driver->serialize);
+ down (&udev->serialize);
error = driver->probe (intf, id);
+ up (&udev->serialize);
up (&driver->serialize);
}
if (!error)
@@ -113,10 +113,12 @@
{
struct usb_interface *intf;
struct usb_driver *driver;
+ struct usb_device *udev;
int m;
intf = list_entry(dev,struct usb_interface,dev);
driver = to_usb_driver(dev->driver);
+ udev = interface_to_usbdev(intf);
if (!driver) {
err("%s does not have a valid driver to work with!",
@@ -135,10 +137,10 @@
}
/* if we sleep here on an umanaged driver
- * the holder of the lock guards against
+ * the holder of the lock guards against
* module unload */
down(&driver->serialize);
-
+ down(&udev->serialize);
if (intf->driver && intf->driver->disconnect)
intf->driver->disconnect(intf);
@@ -146,6 +148,7 @@
if (intf->driver)
usb_driver_release_interface(driver, intf);
+ up(&udev->serialize);
up(&driver->serialize);
if (driver->owner)
__MOD_DEC_USE_COUNT(driver->owner);
@@ -759,6 +767,26 @@
return -1;
}
+/** usb_reap_interfaces - disconnect all interfaces of a usb device
+ * @dev: pointer to the device whose interfaces shall be disconnected
+ * Context: !in_interrupt ()
+ *
+ * Getting rid of interfaces associated with drivers.
+ * This is for physical disconnection and configuration changes
+ */
+void usb_reap_interfaces(struct usb_device *dev)
+{
+ int i;
+
+ if (dev->actconfig) {
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *interface =
+&dev->actconfig->interface[i];
+
+ /* remove this interface */
+ device_unregister(&interface->dev);
+ }
+ }
+}
/**
* usb_disconnect - disconnect a device (usbcore-internal)
* @pdev: pointer to device being disconnected
@@ -790,20 +818,13 @@
usb_disconnect(child);
}
- if (dev->actconfig) {
- for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
- struct usb_interface *interface =
&dev->actconfig->interface[i];
-
- /* remove this interface */
- device_unregister(&interface->dev);
- }
- }
+ usb_reap_interfaces(dev);
/* Free the device number and remove the /proc/bus/usb entry */
if (dev->devnum > 0) {
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
usbfs_remove_device(dev);
@@ -921,6 +942,50 @@
}
/*
+ * This registers a device's interfaces with the generic device layer
+ *
+ * for new devices or configuration changes
+ */
+int usb_register_interfaces(struct usb_device *dev)
+{
+ int i;
+
+ /* Register all of the interfaces for this device with the driver core.
+ * Remember, interfaces get bound to drivers, not devices. */
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *interface = &dev->actconfig->interface[i];
+ struct usb_interface_descriptor *desc = interface->altsetting;
+
+ interface->dev.parent = &dev->dev;
+ interface->dev.driver = NULL;
+ interface->dev.bus = &usb_bus_type;
+ sprintf (&interface->dev.bus_id[0], "%d-%s:%d",
+ dev->bus->busnum, dev->devpath,
+ interface->altsetting->bInterfaceNumber);
+ if (!desc->iInterface
+ || usb_string (dev, desc->iInterface,
+ interface->dev.name,
+ sizeof interface->dev.name) <= 0) {
+ /* typically devices won't bother with interface
+ * descriptions; this is the normal case. an
+ * interface's driver might describe it better.
+ * (also: iInterface is per-altsetting ...)
+ */
+ sprintf (&interface->dev.name[0],
+ "usb-%s-%s interface %d",
+ dev->bus->bus_name, dev->devpath,
+ interface->altsetting->bInterfaceNumber);
+ }
+ dbg ("%s - registering %s", __FUNCTION__, interface->dev.bus_id);
+ device_register (&interface->dev);
+ usb_create_driverfs_intf_files (interface);
+ }
+
+ return 0;
+}
+
+
+/*
* By the time we get here, the device has gotten a new device ID
* and is in the default state. We need to identify the thing and
* get the ball rolling..
@@ -1006,7 +1071,7 @@
}
/* we set the default configuration here */
- err = usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+ err = usb_physical_set_conf(dev, dev->config[0].bConfigurationValue);
if (err) {
err("failed to set device %d default configuration (error=%d)",
dev->devnum, err);
@@ -1042,37 +1107,11 @@
/* add the USB device specific driverfs files */
usb_create_driverfs_dev_files (dev);
-
- /* Register all of the interfaces for this device with the driver core.
- * Remember, interfaces get bound to drivers, not devices. */
- for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
- struct usb_interface *interface = &dev->actconfig->interface[i];
- struct usb_interface_descriptor *desc = interface->altsetting;
-
- interface->dev.parent = &dev->dev;
- interface->dev.driver = NULL;
- interface->dev.bus = &usb_bus_type;
- sprintf (&interface->dev.bus_id[0], "%d-%s:%d",
- dev->bus->busnum, dev->devpath,
- interface->altsetting->bInterfaceNumber);
- if (!desc->iInterface
- || usb_string (dev, desc->iInterface,
- interface->dev.name,
- sizeof interface->dev.name) <= 0) {
- /* typically devices won't bother with interface
- * descriptions; this is the normal case. an
- * interface's driver might describe it better.
- * (also: iInterface is per-altsetting ...)
- */
- sprintf (&interface->dev.name[0],
- "usb-%s-%s interface %d",
- dev->bus->bus_name, dev->devpath,
- interface->altsetting->bInterfaceNumber);
- }
- dbg ("%s - registering %s", __FUNCTION__, interface->dev.bus_id);
- device_register (&interface->dev);
- usb_create_driverfs_intf_files (interface);
- }
+
+ err = usb_register_interfaces(dev);
+
+ if (err)
+ return err;
/* add a /proc/bus/usb entry */
usbfs_add_device(dev);
--- usb-2.5/include/linux/usb.h 2002-10-12 14:49:19.000000000 +0200
+++ con25/include/linux/usb.h 2002-10-12 21:32:23.000000000 +0200
@@ -375,6 +375,7 @@
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
+ int desired_conf; /* configuration to restore on resume */
void *hcpriv; /* Host Controller private data */
@@ -1033,6 +1033,9 @@
struct scatterlist *sg, int n_hw_ents);
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
+void usb_reap_interfaces(struct usb_device *dev);
+int usb_physical_set_conf(struct usb_device *dev, int configuration);
+int usb_register_interfaces(struct usb_device *dev);
/*-------------------------------------------------------------------*
* SYNCHRONOUS CALL SUPPORT *
-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel