patch-2.3.25 linux/drivers/usb/usb.c
Next file: linux/drivers/usb/usb.h
Previous file: linux/drivers/usb/usb-serial.c
Back to the patch index
Back to the overall index
- Lines: 427
- Date:
Thu Oct 28 12:53:26 1999
- Orig file:
v2.3.24/linux/drivers/usb/usb.c
- Orig date:
Fri Oct 22 13:21:51 1999
diff -u --recursive --new-file v2.3.24/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
@@ -3,6 +3,8 @@
*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
@@ -27,9 +29,12 @@
#include "usb.h"
-static int usb_find_driver(struct usb_device *);
+/*
+ * Prototypes for the device driver probing/loading functions
+ */
+static void usb_find_drivers(struct usb_device *);
+static int usb_find_interface_driver(struct usb_device *, unsigned int);
static void usb_check_support(struct usb_device *);
-static void usb_driver_purge(struct usb_driver *, struct usb_device *);
/*
* We have a per-interface "registered driver" list.
@@ -37,6 +42,8 @@
static LIST_HEAD(usb_driver_list);
static LIST_HEAD(usb_bus_list);
+static struct usb_busmap busmap;
+
static struct usb_driver *usb_minors[16];
int usb_register(struct usb_driver *new_driver)
@@ -70,6 +77,44 @@
return 0;
}
+/*
+ * This function is part of a depth-first search down the device tree,
+ * removing any instances of a device driver.
+ */
+static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
+{
+ int i;
+
+ if (!dev) {
+ printk(KERN_ERR "usbcore: null device being purged!!!\n");
+ return;
+ }
+
+ for (i=0; i<USB_MAXCHILDREN; i++)
+ if (dev->children[i])
+ usb_drivers_purge(driver, dev->children[i]);
+
+ if (!dev->actconfig)
+ return;
+
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *interface = &dev->actconfig->interface[i];
+
+ if (interface->driver == driver) {
+ driver->disconnect(dev, interface->private_data);
+ usb_driver_release_interface(driver, interface);
+ /*
+ * This will go through the list looking for another
+ * driver that can handle the device
+ */
+ usb_find_interface_driver(dev, i);
+ }
+ }
+}
+
+/*
+ * Unlink a driver from the driver list when it is unloaded
+ */
void usb_deregister(struct usb_driver *driver)
{
struct list_head *tmp;
@@ -89,45 +134,10 @@
struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list);
tmp = tmp->next;
- usb_driver_purge(driver, bus->root_hub);
+ usb_drivers_purge(driver, bus->root_hub);
}
}
-/*
- * This function is part of a depth-first search down the device tree,
- * removing any instances of a device driver.
- */
-static void usb_driver_purge(struct usb_driver *driver,struct usb_device *dev)
-{
- int i;
-
- if (!dev) {
- printk(KERN_ERR "usbcore: null device being purged!!!\n");
- return;
- }
-
- for (i=0; i<USB_MAXCHILDREN; i++)
- if (dev->children[i])
- usb_driver_purge(driver, dev->children[i]);
-
- /* now we check this device */
- if (dev->driver == driver) {
- /*
- * Note: this is not the correct way to do this, this
- * uninitializes and reinitializes EVERY driver
- */
- printk(KERN_INFO "disconnect driverless device %d\n",
- dev->devnum);
- dev->driver->disconnect(dev);
- dev->driver = NULL;
-
- /*
- * This will go back through the list looking for a driver
- * that can handle the device
- */
- usb_find_driver(dev);
- }
-}
/*
* calc_bus_time:
@@ -222,6 +232,7 @@
bus->op = op;
bus->root_hub = NULL;
bus->hcpriv = NULL;
+ bus->busnum = -1;
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
@@ -241,16 +252,27 @@
void usb_register_bus(struct usb_bus *bus)
{
+ int busnum;
+
+ busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1);
+ if (busnum < USB_MAXBUS) {
+ set_bit(busnum, busmap.busmap);
+ bus->busnum = busnum;
+ } else
+ printk(KERN_INFO "usb: too many bus'\n");
+
proc_usb_add_bus(bus);
/* Add it to the list of buses */
list_add(&bus->bus_list, &usb_bus_list);
- printk("New USB bus registered\n");
+ printk("New USB bus registered, assigned bus number %d\n", bus->busnum);
}
void usb_deregister_bus(struct usb_bus *bus)
{
+ printk("usbcore: USB bus %d deregistered\n", bus->busnum);
+
/*
* NOTE: make sure that all the devices are removed by the
* controller code, as well as having it call this when cleaning
@@ -259,6 +281,8 @@
list_del(&bus->bus_list);
proc_usb_remove_bus(bus);
+
+ clear_bit(bus->busnum, busmap.busmap);
}
/*
@@ -278,37 +302,123 @@
if (dev->children[i])
usb_check_support(dev->children[i]);
+ if (!dev->actconfig)
+ return;
+
/* now we check this device */
- if (!dev->driver && dev->devnum > 0)
- usb_find_driver(dev);
+ if (dev->devnum > 0)
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+ usb_find_interface_driver(dev, i);
+}
+
+
+/*
+ * This is intended to be used by usb device drivers that need to
+ * claim more than one interface on a device at once when probing
+ * (audio and acm are good examples). No device driver should have
+ * to mess with the internal usb_interface or usb_device structure
+ * members.
+ */
+void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+{
+ if (!iface || !driver)
+ return;
+
+ printk(KERN_DEBUG "usbcore: %s driver claimed interface %p\n", driver->name, iface);
+
+ iface->driver = driver;
+ iface->private_data = priv;
+} /* usb_driver_claim_interface() */
+
+/*
+ * This should be used by drivers to check other interfaces to see if
+ * they are available or not.
+ */
+int usb_interface_claimed(struct usb_interface *iface)
+{
+ if (!iface)
+ return 0;
+
+ return (iface->driver != NULL);
+} /* usb_interface_claimed() */
+
+/*
+ * This should be used by drivers to release their claimed interfaces
+ */
+void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
+{
+ /* this should never happen, don't release something that's not ours */
+ if (iface->driver != driver || !iface)
+ return;
+
+ iface->driver = NULL;
+ iface->private_data = NULL;
}
/*
* This entrypoint gets called for each new device.
*
* We now walk the list of registered USB drivers,
- * looking for one that will accept this device as
- * his..
+ * looking for one that will accept this interface.
+ *
+ * The probe return value is changed to be a private pointer. This way
+ * the drivers don't have to dig around in our structures to set the
+ * private pointer if they only need one interface.
+ *
+ * Returns: 0 if a driver accepted the interface, -1 otherwise
*/
-static int usb_find_driver(struct usb_device *dev)
+static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
{
struct list_head *tmp = usb_driver_list.next;
-
- while (tmp != &usb_driver_list) {
- struct usb_driver *driver = list_entry(tmp, struct usb_driver,
- driver_list);
- tmp = tmp->next;
- if (driver->probe(dev))
- continue;
- dev->driver = driver;
- return 1;
+ struct usb_interface *interface;
+
+ if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
+ printk(KERN_ERR "usb-core: bad find_interface_driver params\n");
+ return -1;
}
- /*
- * Ok, no driver accepted the device, so show the info
- * for debugging..
- */
- return 0;
+ interface = &dev->actconfig->interface[ifnum];
+
+ if (usb_interface_claimed(interface))
+ return -1;
+
+ while (tmp != &usb_driver_list) {
+ void *private;
+ struct usb_driver *driver = list_entry(tmp, struct usb_driver,
+ driver_list);
+
+ tmp = tmp->next;
+ if (!(private = driver->probe(dev, ifnum)))
+ continue;
+ usb_driver_claim_interface(driver, interface, private);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * This entrypoint gets called for each new device.
+ *
+ * All interfaces are scanned for matching drivers.
+ */
+static void usb_find_drivers(struct usb_device *dev)
+{
+ unsigned ifnum;
+ unsigned rejected = 0;
+
+ for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
+ /* if this interface hasn't already been claimed */
+ if (!usb_interface_claimed(dev->actconfig->interface)) {
+ if (usb_find_interface_driver(dev, ifnum))
+ rejected++;
+ }
+ }
+
+ if (rejected) {
+ printk(KERN_DEBUG "usbcore: unhandled interfaces on device.\n");
+ }
}
/*
@@ -631,6 +741,7 @@
{
dev->devnum = -1;
dev->slow = 0;
+ dev->actconfig = NULL;
}
/*
@@ -646,10 +757,18 @@
*pdev = NULL;
- printk("USB disconnect on device %d\n", dev->devnum);
+ printk("usbcore: USB disconnect on device %d\n", dev->devnum);
- if (dev->driver)
- dev->driver->disconnect(dev);
+ if (dev->actconfig) {
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *interface = &dev->actconfig->interface[i];
+ struct usb_driver *driver = interface->driver;
+ if (driver) {
+ driver->disconnect(dev, interface->private_data);
+ usb_driver_release_interface(driver, interface);
+ }
+ }
+ }
/* Free up all the children.. */
for (i = 0; i < USB_MAXCHILDREN; i++) {
@@ -894,7 +1013,6 @@
if (err)
return err;
- dev->ifnum = interface;
dev->actconfig->interface[interface].act_altsetting = alternate;
usb_set_maxpacket(dev);
return 0;
@@ -1113,9 +1231,14 @@
}
dev->actconfig = dev->config;
- dev->ifnum = 0;
usb_set_maxpacket(dev);
+ /* we set the default configuration here */
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk(KERN_ERR "usbcore: failed to set default configuration\n");
+ return -1;
+ }
+
usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
usb_show_string(dev, "Product", dev->descriptor.iProduct);
usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
@@ -1123,14 +1246,8 @@
/* now that the basic setup is over, add a /proc/bus/usb entry */
proc_usb_add_device(dev);
- if (!usb_find_driver(dev)) {
- /*
- * Ok, no driver accepted the device, so show the info for
- * debugging
- */
- printk(KERN_DEBUG "Unknown new USB device:\n");
- usb_show_device(dev);
- }
+ /* find drivers willing to handle this device */
+ usb_find_drivers(dev);
return 0;
}
@@ -1226,12 +1343,12 @@
* returns the current frame number for the parent USB bus/controller
* of the given USB device.
*/
-int usb_get_current_frame_number (struct usb_device *usb_dev)
+int usb_get_current_frame_number(struct usb_device *usb_dev)
{
return usb_dev->bus->op->get_frame_number (usb_dev);
}
-int usb_init_isoc (struct usb_device *usb_dev,
+int usb_init_isoc(struct usb_device *usb_dev,
unsigned int pipe,
int frame_count,
void *context,
@@ -1302,7 +1419,7 @@
struct usb_driver *c = usb_minors[minor/16];
file->f_op = NULL;
- if ((file->f_op = c->fops) && file->f_op->open)
+ if (c && (file->f_op = c->fops) && file->f_op->open)
return file->f_op->open(inode,file);
else
return -ENODEV;
@@ -1323,10 +1440,15 @@
void usb_major_init(void)
{
- if (register_chrdev(180,"usb",&usb_fops)) {
+ if (register_chrdev(USB_MAJOR,"usb",&usb_fops)) {
printk("unable to get major %d for usb devices\n",
- MISC_MAJOR);
+ USB_MAJOR);
}
+}
+
+void usb_major_cleanup(void)
+{
+ unregister_chrdev(USB_MAJOR, "usb");
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)