After talking to Matthew Dharm, Randy Dunlap and a couple of other people
it became apparent that we probably want different semantics for
usb_reset_device to make it more useful.
This patch resets the device, then it programs the address, programs the
active configuration previously set and then sets the alternate setting
for all of the interfaces.
The calling driver still retains control of the device and it does not
simulate a disconnect() and then probe() call on the device.
I've also added an address 0 semaphore. If we reset a device while a new
device was plugged in, we can have 2 devices on address 0 which will
most likely cause problems.
I've also added an ioctl() call to reset the device through usbdevfs.
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.
If you wish to still have the old semantics. Make a call to usb_disconnect
and then usb_connect (to get a new address) and then usb_new_device.
The patch is against 2.4.0-test1
JE
--- linux-2.4.0-test1.orig/drivers/usb/hub.c Tue Jun 13 16:56:36 2000
+++ linux-2.4.0-test1/drivers/usb/hub.c Tue Jun 13 16:37:03 2000
@@ -19,6 +19,7 @@
#endif
#include <linux/usb.h>
+#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -26,6 +27,7 @@
/* 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 containing all of the hubs (for
cleanup) */
@@ -366,19 +368,20 @@
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return;
}
- wait_ms(400);
+ wait_ms(400);
-#define MAX_TRIES 5
+ down(&usb_address0_sem);
+#define MAX_TRIES 5
/* Reset the port */
for (tries = 0; tries < MAX_TRIES ; tries++) {
usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
- wait_ms(200);
+ wait_ms(200);
ret = usb_get_port_status(hub, port + 1, &portsts);
if (ret < 0) {
err("get_port_status(%d) failed (err = %d)", port + 1, ret);
- return;
+ goto out;
}
portstatus = le16_to_cpu(portsts.wPortStatus);
@@ -388,7 +391,7 @@
if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
!(portstatus & USB_PORT_STAT_CONNECTION))
- return;
+ goto out;
if (portstatus & USB_PORT_STAT_ENABLE)
break;
@@ -399,7 +402,7 @@
if (tries >= MAX_TRIES) {
err("Cannot enable port %i after %i retries, disabling port.", port+1,
MAX_TRIES);
err("Maybe the USB cable is bad?");
- return;
+ goto out;
}
usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET);
@@ -408,7 +411,7 @@
usb = usb_alloc_dev(hub, hub->bus);
if (!usb) {
err("couldn't allocate usb_device");
- return;
+ goto out;
}
usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
@@ -435,6 +438,9 @@
USB_PORT_FEAT_ENABLE);
}
}
+
+out:
+ up(&usb_address0_sem);
}
static void usb_hub_events(void)
@@ -545,10 +551,6 @@
static int usb_hub_thread(void *__hub)
{
-/*
- MOD_INC_USE_COUNT;
-*/
-
khubd_running = 1;
lock_kernel();
@@ -569,10 +571,6 @@
interruptible_sleep_on(&khubd_wait);
} while (!signal_pending(current));
-/*
- MOD_DEC_USE_COUNT;
-*/
-
dbg("usb_hub_thread exiting");
khubd_running = 0;
@@ -643,25 +641,70 @@
int usb_reset_device(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
- int i;
+ int i, err, port = -1;
if (!parent) {
err("attempting to reset root hub!");
return -EINVAL;
}
- for (i = 0; i < parent->maxchild; i++) {
+ for (i = 0; i < parent->maxchild; i++)
if (parent->children[i] == dev) {
- usb_set_port_feature(parent, i + 1,
- USB_PORT_FEAT_RESET);
+ port = i;
+ break;
+ }
+
+ if (port < 0)
+ return -ENOENT;
+
+ down(&usb_address0_sem);
- usb_disconnect(&dev);
- usb_hub_port_connect_change(parent, i);
+ /* Send a reset to the device */
+ usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET);
- return 0;
+ wait_ms(200);
+
+ usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET);
+
+ /*
+ * Now we reprogram a couple of things:
+ * - Address
+ * - Active Configuration
+ * - Active Alternate Settings
+ */
+ err = usb_set_address(dev);
+ if (err < 0) {
+ err("USB device not accepting new address (error=%d)", err);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ up(&usb_address0_sem);
+ return err;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
+
+ up(&usb_address0_sem);
+
+ err = usb_set_configuration(dev, dev->actconfig->bConfigurationValue);
+ if (err < 0) {
+ err("failed to set active configuration (error=%d)", err);
+ return err;
+ }
+
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *intf =
+ &dev->actconfig->interface[i];
+ struct usb_interface_descriptor *as =
+ &intf->altsetting[intf->act_altsetting];
+
+ err = usb_set_interface(dev, as->bInterfaceNumber,
+ as->bAlternateSetting);
+ if (err < 0) {
+ err("failed to set active alternate setting for interface %d
+(error=%d)", as->bInterfaceNumber, err);
+ return err;
}
}
- return -ENOENT;
+ return 0;
}
--- linux-2.4.0-test1.orig/include/linux/usbdevice_fs.h Thu Jun 8 16:44:28 2000
+++ linux-2.4.0-test1/include/linux/usbdevice_fs.h Tue Jun 13 18:19:30 2000
@@ -122,6 +122,7 @@
#define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int)
#define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int)
#define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo)
+#define USBDEVFS_RESET _IO('U', 18)
/* --------------------------------------------------------------------- */
--- linux-2.4.0-test1.orig/drivers/usb/devio.c Thu Jun 8 16:44:28 2000
+++ linux-2.4.0-test1/drivers/usb/devio.c Tue Jun 13 18:20:07 2000
@@ -1040,6 +1040,10 @@
ret = proc_connectinfo(ps, (void *)arg);
break;
+ case USBDEVFS_RESET:
+ ret = usb_reset_device(ps->dev);
+ break;
+
case USBDEVFS_SETINTERFACE:
ret = proc_setintf(ps, (void *)arg);
break;
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]