This takes all of the feedback I got back about the first patch. Changes
since the first patch:
- updated to 2.4.0-test1-ac18. only significant change was the ioctl number
has changed
- Simulate disconnect() and probe() on all interfaces not claimed by the
calling process in ioctl()
- Only set_configuration() and set_interface() if the device descriptor
hasn't changed. Otherwise, it dumps the configs and rereads them
- More comments
Any other changes someone can think of?
JE
diff -ur linux-2.4.0-test1-ac18.orig/drivers/usb/devio.c
linux-2.4.0-test1-ac18/drivers/usb/devio.c
--- linux-2.4.0-test1-ac18.orig/drivers/usb/devio.c Wed Jun 14 19:23:42 2000
+++ linux-2.4.0-test1-ac18/drivers/usb/devio.c Wed Jun 14 19:25:09 2000
@@ -744,6 +744,32 @@
return 0;
}
+static int proc_resetdevice(struct dev_state *ps)
+{
+ int i, ret;
+
+ ret = usb_reset_device(ps->dev);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ps->dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *intf = &ps->dev->actconfig->interface[i];
+
+ /* Don't simulate interfaces we've claimed */
+ if (test_bit(i, &ps->ifclaimed))
+ continue;
+
+ if (intf->driver) {
+ down(&intf->driver->serialize);
+ intf->driver->disconnect(ps->dev, intf->private_data);
+ intf->driver->probe(ps->dev, i);
+ up(&intf->driver->serialize);
+ }
+ }
+
+ return 0;
+}
+
static int proc_setintf(struct dev_state *ps, void *arg)
{
struct usbdevfs_setinterface setintf;
@@ -1089,6 +1115,10 @@
ret = proc_resetep(ps, (void *)arg);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
+ break;
+
+ case USBDEVFS_RESET:
+ ret = proc_resetdevice(ps);
break;
case USBDEVFS_GETDRIVER:
--- linux-2.4.0-test1-ac18.orig/include/linux/usbdevice_fs.h Wed Jun 14 19:23:49
2000
+++ linux-2.4.0-test1-ac18/include/linux/usbdevice_fs.h Wed Jun 14 19:39:11 2000
@@ -108,6 +108,7 @@
struct usbdevfs_iso_packet_desc iso_frame_desc[0];
};
+/* ioctls for talking to drivers in the usbcore module: */
struct usbdevfs_ioctl {
int ifno; /* interface 0..N ; negative numbers reserved */
int ioctl_code; /* MUST encode size + direction of data so the
@@ -115,6 +116,13 @@
void *data; /* param buffer (in, or out) */
};
+/* You can do most things with hubs just through control messages,
+ * except find out what device connects to what port. */
+struct usbdevfs_hub_portinfo {
+ char nports; /* number of downstream ports in this hub */
+ char port [127]; /* e.g. port 3 connects to device 27 */
+};
+
#define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer)
#define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer)
#define USBDEVFS_RESETEP _IOR('U', 3, unsigned int)
@@ -130,19 +138,8 @@
#define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int)
#define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo)
#define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl)
-
-/* --------------------------------------------------------------------- */
-
-/* ioctls for talking to drivers in the usbcore module: */
-
-/* You can do most things with hubs just through control messages,
- * except find out what device connects to what port. */
-struct usbdevfs_hub_portinfo {
- char nports; /* number of downstream ports in this hub */
- char port [127]; /* e.g. port 3 connects to device 27 */
-};
-#define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo)
-
+#define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo)
+#define USBDEVFS_RESET _IO('U', 20)
/* --------------------------------------------------------------------- */
--- linux-2.4.0-test1-ac18.orig/drivers/usb/hub.c Wed Jun 14 19:23:42 2000
+++ linux-2.4.0-test1-ac18/drivers/usb/hub.c Wed Jun 14 19:49:31 2000
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
+#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -27,6 +28,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) */
@@ -398,19 +400,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);
@@ -420,7 +423,7 @@
if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
!(portstatus & USB_PORT_STAT_CONNECTION))
- return;
+ goto out;
if (portstatus & USB_PORT_STAT_ENABLE)
break;
@@ -431,7 +434,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);
@@ -440,7 +443,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;
@@ -467,6 +470,9 @@
USB_PORT_FEAT_ENABLE);
}
}
+
+out:
+ up(&usb_address0_sem);
}
static void usb_hub_events(void)
@@ -577,10 +583,6 @@
static int usb_hub_thread(void *__hub)
{
-/*
- MOD_INC_USE_COUNT;
-*/
-
khubd_running = 1;
lock_kernel();
@@ -601,10 +603,6 @@
interruptible_sleep_on(&khubd_wait);
} while (!signal_pending(current));
-/*
- MOD_DEC_USE_COUNT;
-*/
-
dbg("usb_hub_thread exiting");
khubd_running = 0;
@@ -673,28 +671,130 @@
usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */
+/*
+ * 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.
+ *
+ * Take a look at proc_resetdevice in devio.c for some sample code to
+ * do this.
+ */
int usb_reset_device(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
- int i;
+ struct usb_device_descriptor descriptor;
+ int i, ret, 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);
+
+ /* Send a reset to the device */
+ usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET);
+
+ wait_ms(200);
- usb_disconnect(&dev);
- usb_hub_port_connect_change(parent, i);
+ usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET);
- return 0;
+ /* Reprogram the Address */
+ ret = usb_set_address(dev);
+ if (ret < 0) {
+ err("USB device not accepting new address (error=%d)", ret);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ up(&usb_address0_sem);
+ return ret;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
+
+ 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)
+ 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))) {
+ usb_destroy_configuration(dev);
+
+ ret = usb_get_device_descriptor(dev);
+ if (ret < sizeof(dev->descriptor)) {
+ if (ret < 0)
+ err("unable to get device descriptor (error=%d)", ret);
+ else
+ err("USB device descriptor short read (expected %i,
+got %i)", sizeof(dev->descriptor), ret);
+
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return -EIO;
+ }
+
+ ret = usb_get_configuration(dev);
+ if (ret < 0) {
+ err("unable to get configuration (error=%d)", ret);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
}
+
+ dev->actconfig = dev->config;
+ usb_set_maxpacket(dev);
+
+ return 1;
+ } else {
+ ret = usb_set_configuration(dev,
+ dev->actconfig->bConfigurationValue);
+ if (ret < 0) {
+ err("failed to set active configuration (error=%d)",
+ ret);
+ return ret;
+ }
+
+ 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];
+
+ ret = usb_set_interface(dev, as->bInterfaceNumber,
+ as->bAlternateSetting);
+ if (ret < 0) {
+ err("failed to set active alternate setting for
+interface %d (error=%d)", i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
}
- return -ENOENT;
+ return 0;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]