patch-2.4.0-test2 linux/drivers/usb/hub.c
Next file: linux/drivers/usb/hub.h
Previous file: linux/drivers/usb/hid.h
Back to the patch index
Back to the overall index
- Lines: 429
- Date:
Mon Jun 19 13:42:41 2000
- Orig file:
v2.4.0-test1/linux/drivers/usb/hub.c
- Orig date:
Tue May 23 15:31:35 2000
diff -u --recursive --new-file v2.4.0-test1/linux/drivers/usb/hub.c linux/drivers/usb/hub.c
@@ -18,7 +18,9 @@
#undef DEBUG
#endif
#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -26,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) */
@@ -116,26 +119,29 @@
static int usb_hub_configure(struct usb_hub *hub)
{
struct usb_device *dev = hub->dev;
- unsigned char buffer[4], *bitmap;
+ unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap;
struct usb_hub_descriptor *descriptor;
struct usb_descriptor_header *header;
struct usb_hub_status *hubsts;
- int i;
-
- /* Get the length first */
- if (usb_get_hub_descriptor(dev, buffer, 4) < 0)
- return -1;
+ int i, ret;
+ /* Request the entire hub descriptor. */
header = (struct usb_descriptor_header *)buffer;
- bitmap = kmalloc(header->bLength, GFP_KERNEL);
- if (!bitmap)
+ ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer));
+ /* <buffer> is large enough for a hub with 127 ports;
+ * the hub can/will return fewer bytes here. */
+ if (ret < 0) {
+ err("Unable to get hub descriptor (err = %d)", ret);
return -1;
+ }
- if (usb_get_hub_descriptor(dev, bitmap, header->bLength) < 0) {
- kfree(bitmap);
+ bitmap = kmalloc(header->bLength, GFP_KERNEL);
+ if (!bitmap) {
+ err("Unable to kmalloc %d bytes for bitmap", header->bLength);
return -1;
}
+ memcpy (bitmap, buffer, header->bLength);
descriptor = (struct usb_hub_descriptor *)bitmap;
hub->nports = dev->maxchild = descriptor->bNbrPorts;
@@ -182,8 +188,11 @@
kfree(bitmap);
- if (usb_get_hub_status(dev, buffer) < 0)
+ ret = usb_get_hub_status(dev, buffer);
+ if (ret < 0) {
+ err("Unable to get hub status (err = %d)", ret);
return -1;
+ }
hubsts = (struct usb_hub_status *)buffer;
dbg("local power source is %s",
@@ -225,12 +234,16 @@
endpoint = &interface->endpoint[0];
/* Output endpoint? Curiousier and curiousier.. */
- if (!(endpoint->bEndpointAddress & USB_DIR_IN))
+ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) {
+ err("Device is hub class, but has output endpoint?");
return NULL;
+ }
/* If it's not an interrupt endpoint, we'd better punt! */
- if ((endpoint->bmAttributes & 3) != 3)
+ if ((endpoint->bmAttributes & 3) != 3) {
+ err("Device is hub class, but has endpoint other than interrupt?");
return NULL;
+ }
/* We found a hub */
info("USB hub found");
@@ -321,17 +334,49 @@
kfree(hub);
}
+static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data)
+{
+ /* assert ifno == 0 (part of hub spec) */
+ switch (code) {
+ case USBDEVFS_HUB_PORTINFO: {
+ struct usbdevfs_hub_portinfo *info = user_data;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave (&hub_event_lock, flags);
+ if (hub->devnum <= 0)
+ info->nports = 0;
+ else {
+ info->nports = hub->maxchild;
+ for (i = 0; i < info->nports; i++) {
+ if (hub->children [i] == NULL)
+ info->port [i] = 0;
+ else
+ info->port [i] = hub->children [i]->devnum;
+ }
+ }
+ spin_unlock_irqrestore (&hub_event_lock, flags);
+
+ return info->nports + 1;
+ }
+
+ default:
+ return -ENOSYS;
+ }
+}
+
static void usb_hub_port_connect_change(struct usb_device *hub, int port)
{
struct usb_device *usb;
struct usb_port_status portsts;
unsigned short portstatus, portchange;
- int tries;
+ int ret, tries;
wait_ms(100);
- /* Check status */
- if (usb_get_port_status(hub, port + 1, &portsts)<0) {
- err("get_port_status failed");
+
+ ret = usb_get_port_status(hub, port + 1, &portsts);
+ if (ret < 0) {
+ err("get_port_status(%d) failed (err = %d)", port + 1, ret);
return;
}
@@ -351,21 +396,22 @@
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return;
}
- wait_ms(400);
+ wait_ms(400);
- /* Reset the port */
+ down(&usb_address0_sem);
#define MAX_TRIES 5
-
- for(tries=0;tries<MAX_TRIES;tries++) {
-
+ /* Reset the port */
+ for (tries = 0; tries < MAX_TRIES ; tries++) {
usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
- wait_ms(200);
-
- if (usb_get_port_status(hub, port + 1, &portsts)<0) {
- err("get_port_status failed");
- return;
+ 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);
+ goto out;
}
+
portstatus = le16_to_cpu(portsts.wPortStatus);
portchange = le16_to_cpu(portsts.wPortChange);
dbg("portstatus %x, change %x, %s", portstatus ,portchange,
@@ -373,7 +419,7 @@
if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
!(portstatus & USB_PORT_STAT_CONNECTION))
- return;
+ goto out;
if (portstatus & USB_PORT_STAT_ENABLE)
break;
@@ -381,20 +427,19 @@
wait_ms(200);
}
- if (tries==MAX_TRIES) {
+ 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);
/* Allocate a new device struct for it */
-
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;
@@ -405,12 +450,25 @@
usb_connect(usb);
/* Run it through the hoops (find a driver, etc) */
- if (usb_new_device(usb)) {
- usb_disconnect(&hub->children[port]);
- /* Woops, disable the port */
- dbg("hub: disabling port %d", port + 1);
- usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);
+ ret = usb_new_device(usb);
+ if (ret) {
+ /* Try resetting the device. Windows does this and it */
+ /* gets some devices working correctly */
+ usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+
+ ret = usb_new_device(usb);
+ if (ret) {
+ usb_disconnect(&hub->children[port]);
+
+ /* Woops, disable the port */
+ dbg("hub: disabling port %d", port + 1);
+ usb_clear_port_feature(hub, port + 1,
+ USB_PORT_FEAT_ENABLE);
+ }
}
+
+out:
+ up(&usb_address0_sem);
}
static void usb_hub_events(void)
@@ -521,10 +579,6 @@
static int usb_hub_thread(void *__hub)
{
-/*
- MOD_INC_USE_COUNT;
-*/
-
khubd_running = 1;
lock_kernel();
@@ -545,10 +599,6 @@
interruptible_sleep_on(&khubd_wait);
} while (!signal_pending(current));
-/*
- MOD_DEC_USE_COUNT;
-*/
-
dbg("usb_hub_thread exiting");
khubd_running = 0;
@@ -556,10 +606,10 @@
}
static struct usb_driver hub_driver = {
- "hub",
- hub_probe,
- hub_disconnect,
- { NULL, NULL }
+ name: "hub",
+ probe: hub_probe,
+ ioctl: hub_ioctl,
+ disconnect: hub_disconnect
};
/*
@@ -569,8 +619,10 @@
{
int pid;
- if (usb_register(&hub_driver) < 0)
+ if (usb_register(&hub_driver) < 0) {
+ err("Unable to register USB hub driver");
return -1;
+ }
pid = kernel_thread(usb_hub_thread, NULL,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
@@ -615,28 +667,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;
- usb_disconnect(&dev);
- usb_hub_port_connect_change(parent, i);
+ down(&usb_address0_sem);
- return 0;
+ /* Send a reset to the device */
+ usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET);
+
+ wait_ms(200);
+
+ usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET);
+
+ /* 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;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)