patch-2.2.18 linux/drivers/usb/usb.c
Next file: linux/drivers/usb/usb.h
Previous file: linux/drivers/usb/usb-uhci.h
Back to the patch index
Back to the overall index
- Lines: 2511
- Date:
Sat Oct 14 00:51:40 2000
- Orig file:
v2.2.17/drivers/usb/usb.c
- Orig date:
Fri Apr 21 12:46:37 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/usb/usb.c linux/drivers/usb/usb.c
@@ -1,7 +1,12 @@
/*
- * driver/usb/usb.c
+ * drivers/usb/usb.c
*
* (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
@@ -10,328 +15,1431 @@
* Think of this as a "USB library" rather than anything else.
* It should be considered a slave, with no callbacks. Callbacks
* are evil.
- */
-
-/*
- * Table 9-2
*
- * Offset Field Size Value Desc
- * 0 bmRequestType 1 Bitmap D7: Direction
- * 0 = Host-to-device
- * 1 = Device-to-host
- * D6..5: Type
- * 0 = Standard
- * 1 = Class
- * 2 = Vendor
- * 3 = Reserved
- * D4..0: Recipient
- * 0 = Device
- * 1 = Interface
- * 2 = Endpoint
- * 3 = Other
- * 4..31 = Reserved
- * 1 bRequest 1 Value Specific request (9-3)
- * 2 wValue 2 Value Varies
- * 4 wIndex 2 Index/Offset Varies
- * 6 wLength 2 Count Bytes for data
+ * $Id: usb.c,v 1.53 2000/01/14 16:19:09 acher Exp $
*/
+#include <linux/config.h>
+#include <linux/module.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/malloc.h>
+#include <linux/interrupt.h> /* for in_interrupt() */
-#include "usb.h"
+
+#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* waitpid() call glue uses this */
+static int errno;
+#endif
+
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+static const int usb_bandwidth_option =
+#ifdef CONFIG_USB_BANDWIDTH
+ 1;
+#else
+ 0;
+#endif
+
+/*
+ * 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 *);
/*
* We have a per-interface "registered driver" list.
*/
-static LIST_HEAD(usb_driver_list);
+LIST_HEAD(usb_driver_list);
+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)
{
+ if (new_driver->fops != NULL) {
+ if (usb_minors[new_driver->minor/16]) {
+ err("error registering %s driver", new_driver->name);
+ return -EINVAL;
+ }
+ usb_minors[new_driver->minor/16] = new_driver;
+ }
+
+ info("registered new driver %s", new_driver->name);
+
+ init_MUTEX(&new_driver->serialize);
+
/* Add it to the list of known drivers */
list_add(&new_driver->driver_list, &usb_driver_list);
- /*
- * We should go through all existing devices, and see if any of
- * them would be acceptable to the new driver.. Let's do that
- * in version 2.0.
- */
+ usb_scan_devices();
+
return 0;
}
+/*
+ * We go through all existing devices, and see if any of them would
+ * be acceptable to the new driver.. This is done using a depth-first
+ * search for devices without a registered driver already, then
+ * running 'probe' with each of the drivers registered on every one
+ * of these.
+ */
+void usb_scan_devices(void)
+{
+ struct list_head *tmp;
+
+ tmp = usb_bus_list.next;
+ while (tmp != &usb_bus_list) {
+ struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list);
+
+ tmp = tmp->next;
+ usb_check_support(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_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
+{
+ int i;
+
+ if (!dev) {
+ err("null device being purged!!!");
+ 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) {
+ down(&driver->serialize);
+ driver->disconnect(dev, interface->private_data);
+ up(&driver->serialize);
+ 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;
+
+ info("deregistering driver %s", driver->name);
+ if (driver->fops != NULL)
+ usb_minors[driver->minor/16] = NULL;
+
+ /*
+ * first we remove the driver, to be sure it doesn't get used by
+ * another thread while we are stepping through removing entries
+ */
list_del(&driver->driver_list);
+
+ tmp = usb_bus_list.next;
+ while (tmp != &usb_bus_list) {
+ struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list);
+
+ tmp = tmp->next;
+ usb_drivers_purge(driver, bus->root_hub);
+ }
+}
+
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+{
+ int i;
+
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+ if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum)
+ return &dev->actconfig->interface[i];
+
+ return NULL;
+}
+
+/*
+ * usb_calc_bus_time:
+ *
+ * returns (approximate) USB bus time in nanoseconds for a USB transaction.
+ */
+static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
+{
+ unsigned long tmp;
+
+ if (low_speed) /* no isoc. here */
+ {
+ if (input_dir)
+ {
+ tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ }
+ else
+ {
+ tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ }
+ }
+
+ /* for full-speed: */
+
+ if (!isoc) /* Input or Output */
+ {
+ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (9107L + BW_HOST_DELAY + tmp);
+ } /* end not Isoc */
+
+ /* for isoc: */
+
+ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+}
+
+/*
+ * usb_check_bandwidth():
+ *
+ * old_alloc is from host_controller->bandwidth_allocated in microseconds;
+ * bustime is from calc_bus_time(), but converted to microseconds.
+ *
+ * returns <bustime in us> if successful,
+ * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails.
+ *
+ * FIXME:
+ * This initial implementation does not use Endpoint.bInterval
+ * in managing bandwidth allocation.
+ * It probably needs to be expanded to use Endpoint.bInterval.
+ * This can be done as a later enhancement (correction).
+ * This will also probably require some kind of
+ * frame allocation tracking...meaning, for example,
+ * that if multiple drivers request interrupts every 10 USB frames,
+ * they don't all have to be allocated at
+ * frame numbers N, N+10, N+20, etc. Some of them could be at
+ * N+11, N+21, N+31, etc., and others at
+ * N+12, N+22, N+32, etc.
+ * However, this first cut at USB bandwidth allocation does not
+ * contain any frame allocation tracking.
+ */
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
+{
+ int new_alloc;
+ int old_alloc = dev->bus->bandwidth_allocated;
+ unsigned int pipe = urb->pipe;
+ long bustime;
+
+ bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe),
+ usb_pipeisoc(pipe), usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
+ if (usb_pipeisoc(pipe))
+ bustime = NS_TO_US(bustime) / urb->number_of_packets;
+ else
+ bustime = NS_TO_US(bustime);
+
+ new_alloc = old_alloc + (int)bustime;
+ /* what new total allocated bus time would be */
+
+ if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC)
+ dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us",
+ usb_bandwidth_option ? "" : "would have ",
+ old_alloc, new_alloc, bustime);
+
+ if (!usb_bandwidth_option) /* don't enforce it */
+ return (bustime);
+ return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : USB_ST_BANDWIDTH_ERROR;
+}
+
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+ dev->bus->bandwidth_allocated += bustime;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs++;
+ else
+ dev->bus->bandwidth_int_reqs++;
+ urb->bandwidth = bustime;
+
+ dbg("bw_alloc increased by %d to %d for %d requesters",
+ bustime,
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+}
+
+/*
+ * usb_release_bandwidth():
+ *
+ * called to release a pipe's bandwidth (in microseconds)
+ */
+void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
+{
+ dev->bus->bandwidth_allocated -= urb->bandwidth;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs--;
+ else
+ dev->bus->bandwidth_int_reqs--;
+
+ dbg("bw_alloc reduced by %d to %d for %d requesters",
+ urb->bandwidth,
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+ urb->bandwidth = 0;
+}
+
+/*
+ * New functions for (de)registering a controller
+ */
+struct usb_bus *usb_alloc_bus(struct usb_operations *op)
+{
+ struct usb_bus *bus;
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ memset(&bus->devmap, 0, sizeof(struct usb_devmap));
+
+ 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;
+
+ INIT_LIST_HEAD(&bus->bus_list);
+ INIT_LIST_HEAD(&bus->inodes);
+
+ return bus;
+}
+
+void usb_free_bus(struct usb_bus *bus)
+{
+ if (!bus)
+ return;
+
+ kfree(bus);
+}
+
+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
+ warn("too many buses");
+
+ /* Add it to the list of buses */
+ list_add(&bus->bus_list, &usb_bus_list);
+
+ usbdevfs_add_bus(bus);
+
+ info("new USB bus registered, assigned bus number %d", bus->busnum);
+}
+
+void usb_deregister_bus(struct usb_bus *bus)
+{
+ info("USB bus %d deregistered", bus->busnum);
+
+ /*
+ * NOTE: make sure that all the devices are removed by the
+ * controller code, as well as having it call this when cleaning
+ * itself up
+ */
+ list_del(&bus->bus_list);
+
+ usbdevfs_remove_bus(bus);
+
+ clear_bit(bus->busnum, busmap.busmap);
+}
+
+/*
+ * This function is for doing a depth-first search for devices which
+ * have support, for dynamic loading of driver modules.
+ */
+static void usb_check_support(struct usb_device *dev)
+{
+ int i;
+
+ if (!dev) {
+ err("null device being checked!!!");
+ return;
+ }
+
+ for (i=0; i<USB_MAXCHILDREN; i++)
+ if (dev->children[i])
+ usb_check_support(dev->children[i]);
+
+ if (!dev->actconfig)
+ return;
+
+ /* now we check this device */
+ 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;
+
+ dbg("%s driver claimed interface %p", 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 || iface->driver != driver)
+ 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
*/
-void usb_device_descriptor(struct usb_device *dev)
+static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
{
struct list_head *tmp = usb_driver_list.next;
+ struct usb_interface *interface;
+
+ if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
+ err("bad find_interface_driver params");
+ return -1;
+ }
+
+ interface = dev->actconfig->interface + ifnum;
+
+ if (usb_interface_claimed(interface))
+ return -1;
while (tmp != &usb_driver_list) {
- struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+ void *private;
+ struct usb_driver *driver = list_entry(tmp, struct usb_driver,
+ driver_list);
+
tmp = tmp->next;
- if (driver->probe(dev))
+ down(&driver->serialize);
+ private = driver->probe(dev, ifnum);
+ up(&driver->serialize);
+ if (!private)
continue;
- dev->driver = driver;
+ usb_driver_claim_interface(driver, interface, private);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+
+/*
+ * USB hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when USB devices get added or removed.
+ */
+
+static int exec_helper (void *arg)
+{
+ void **params = (void **) arg;
+ char *path = (char *) params [0];
+ char **argv = (char **) params [1];
+ char **envp = (char **) params [2];
+ return exec_usermodehelper (path, argv, envp);
+}
+
+int call_usermodehelper (char *path, char **argv, char **envp)
+{
+ void *params [3] = { path, argv, envp };
+ int pid, pid2, retval;
+ mm_segment_t fs;
+
+ if ((pid = kernel_thread (exec_helper, (void *) params, 0)) < 0) {
+ err ("failed fork of %s, errno = %d", argv [0], -pid);
+ return -1;
+ }
+
+ /* set signal mask? */
+ fs = get_fs ();
+ set_fs (KERNEL_DS); /* retval is in kernel space. */
+ pid2 = waitpid (pid, &retval, __WCLONE); /* "errno" gets assigned */
+ set_fs (fs);
+ /* restore signal mask? */
+
+ if (pid2 != pid) {
+ err ("waitpid(%d) failed, returned %d\n", pid, pid2);
+ return -1;
+ }
+ return retval;
+}
+
+static int to_bcd (char *buf, __u16 *bcdValue)
+{
+ int retval = 0;
+ char *value = (char *) bcdValue;
+ int temp;
+
+ /* digits are 0-9 then ":;<=>?" for devices using
+ * non-bcd (non-standard!) values here ... */
+
+ /* No leading (or later, trailing) zeroes since scripts do
+ * literal matches, and that's how they're doing them. */
+ if ((temp = value [1] & 0xf0) != 0) {
+ temp >>= 4;
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+ }
+
+ temp = value [1] & 0x0f;
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+
+ *buf++ = '.';
+ retval++;
+
+ temp = value [0] & 0xf0;
+ temp >>= 4;
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+
+ if ((temp = value [0] & 0x0f) != 0) {
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+ }
+ *buf++ = 0;
+
+ return retval;
+}
+
+/*
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, or both.
+ *
+ * Some synchronization is important: removes can't start processing
+ * before the add-device processing completes, and vice versa. That keeps
+ * a stack of USB-related identifiers stable while they're in use. If we
+ * know that agents won't complete after they return (such as by forking
+ * a process that completes later), it's enough to just waitpid() for the
+ * agent -- as is currently done.
+ *
+ * The reason: we know we're called either from khubd (the typical case)
+ * or from root hub initialization (init, kapmd, modprobe, etc). In both
+ * cases, we know no other thread can recycle our address, since we must
+ * already have been serialized enough to prevent that.
+ */
+static void call_policy (char *verb, struct usb_device *dev)
+{
+ char *argv [3], **envp, *buf, *scratch;
+ int i = 0, value;
+
+ if (!hotplug_path [0])
+ return;
+ if (in_interrupt ()) {
+ dbg ("In_interrupt");
+ return;
+ }
+ if (!current->fs->root) {
+ /* statically linked USB is initted rather early */
+ dbg ("call_policy %s, num %d -- no FS yet", verb, dev->devnum);
+ return;
+ }
+ if (dev->devnum < 0) {
+ dbg ("device already deleted ??");
+ return;
+ }
+ if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) {
+ dbg ("enomem");
+ return;
+ }
+ if (!(buf = kmalloc (256, GFP_KERNEL))) {
+ kfree (envp);
+ dbg ("enomem2");
return;
}
- /*
- * Ok, no driver accepted the device, so show the info
- * for debugging..
+ /* only one standardized param to hotplug command: type */
+ argv [0] = hotplug_path;
+ argv [1] = "usb";
+ argv [2] = 0;
+
+ /* minimal command environment */
+ envp [i++] = "HOME=/";
+ envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef DEBUG
+ /* hint that policy agent should enter no-stdout debug mode */
+ envp [i++] = "DEBUG=kernel";
+#endif
+ /* extensible set of named bus-specific parameters,
+ * supporting multiple driver selection algorithms.
*/
- printk("Unknown new USB device:\n");
- usb_show_device(dev);
+ scratch = buf;
+
+ /* action: add, remove */
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "ACTION=%s", verb) + 1;
+
+#ifdef CONFIG_USB_DEVICEFS
+ /* If this is available, userspace programs can directly read
+ * all the device descriptors we don't tell them about. Or
+ * even act as usermode drivers.
+ *
+ * XXX how little intelligence can we hardwire?
+ * (a) mount point: /devfs, /dev, /proc/bus/usb etc.
+ * (b) naming convention: bus1/device3, 001/003 etc.
+ */
+ envp [i++] = "DEVFS=/proc/bus/usb";
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d",
+ dev->bus->busnum, dev->devnum) + 1;
+#endif
+
+ /* per-device configuration hacks are often necessary */
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct);
+ scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+
+ /* otherwise, use a simple (so far) generic driver binding model */
+ envp [i++] = scratch;
+ if (dev->descriptor.bDeviceClass == 0) {
+ int alt = dev->actconfig->interface [0].act_altsetting;
+
+ /* simple/common case: one config, one interface, one driver
+ * unsimple cases: everything else
+ */
+ scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
+ dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
+ dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
+ dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+ + 1;
+ /* INTERFACE-0, INTERFACE-1, ... ? */
+ } else {
+ /* simple/common case: generic device, handled generically */
+ scratch += sprintf (scratch, "TYPE=%d/%d/%d",
+ dev->descriptor.bDeviceClass,
+ dev->descriptor.bDeviceSubClass,
+ dev->descriptor.bDeviceProtocol) + 1;
+ }
+ envp [i++] = 0;
+ /* assert: (scratch - buf) < sizeof buf */
+
+ /* NOTE: user mode daemons can call the agents too */
+
+ dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum);
+ value = call_usermodehelper (argv [0], argv, envp);
+ kfree (buf);
+ kfree (envp);
+ dbg ("kusbd policy returned 0x%x", value);
+}
+
+#else
+
+static inline void
+call_policy (char *verb, struct usb_device *dev)
+{ }
+
+#endif /* KMOD && HOTPLUG */
+
+
+/*
+ * 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;
+ unsigned claimed = 0;
+
+ for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
+ /* if this interface hasn't already been claimed */
+ if (!usb_interface_claimed(dev->actconfig->interface + ifnum)) {
+ if (usb_find_interface_driver(dev, ifnum))
+ rejected++;
+ else
+ claimed++;
+ }
+ }
+
+ if (rejected)
+ dbg("unhandled interfaces on device");
+
+ if (!claimed) {
+ warn("USB device %d (prod/vend 0x%x/0x%x) is not claimed by any active driver.",
+ dev->devnum,
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct);
+#ifdef DEBUG
+ usb_show_device(dev);
+#endif
+ }
}
/*
- * Parse the fairly incomprehensible output of
- * the USB configuration data, and build up the
- * USB device database.
+ * Only HC's should call usb_alloc_dev and usb_free_dev directly
+ * Anybody may use usb_inc_dev_use or usb_dec_dev_use
*/
-static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex)
+struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
{
- int parsed = 0;
- int n_len;
- unsigned short n_desc;
+ struct usb_device *dev;
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ memset(dev, 0, sizeof(*dev));
- for (;;) {
- int i;
+ dev->bus = bus;
+ dev->parent = parent;
+ atomic_set(&dev->refcnt, 1);
+ INIT_LIST_HEAD(&dev->inodes);
+ INIT_LIST_HEAD(&dev->filelist);
- if (len < descindex)
+ dev->bus->op->allocate(dev);
+
+ return dev;
+}
+
+void usb_free_dev(struct usb_device *dev)
+{
+ if (atomic_dec_and_test(&dev->refcnt)) {
+ dev->bus->op->deallocate(dev);
+ usb_destroy_configuration(dev);
+ kfree(dev);
+ }
+}
+
+void usb_inc_dev_use(struct usb_device *dev)
+{
+ atomic_inc(&dev->refcnt);
+}
+/* -------------------------------------------------------------------------------------
+ * New USB Core Functions
+ * -------------------------------------------------------------------------------------*/
+
+urb_t *usb_alloc_urb(int iso_packets)
+{
+ urb_t *urb;
+
+ urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!urb) {
+ err("alloc_urb: kmalloc failed");
+ return NULL;
+ }
+
+ memset(urb, 0, sizeof(*urb));
+
+ spin_lock_init(&urb->lock);
+
+ return urb;
+}
+
+/*-------------------------------------------------------------------*/
+void usb_free_urb(urb_t* urb)
+{
+ if (urb)
+ kfree(urb);
+}
+/*-------------------------------------------------------------------*/
+int usb_submit_urb(urb_t *urb)
+{
+ if (urb && urb->dev)
+ return urb->dev->bus->op->submit_urb(urb);
+ else
+ return -ENODEV;
+}
+
+/*-------------------------------------------------------------------*/
+int usb_unlink_urb(urb_t *urb)
+{
+ if (urb && urb->dev)
+ return urb->dev->bus->op->unlink_urb(urb);
+ else
+ return -ENODEV;
+}
+/*-------------------------------------------------------------------*
+ * COMPLETION HANDLERS *
+ *-------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------*
+ * completion handler for compatibility wrappers (sync control/bulk) *
+ *-------------------------------------------------------------------*/
+static void usb_api_blocking_completion(urb_t *urb)
+{
+ api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+
+ if (waitqueue_active(awd->wakeup))
+ wake_up(awd->wakeup);
+#if 0
+ else
+ dbg("(blocking_completion): waitqueue empty!");
+ // even occurs if urb was unlinked by timeout...
+#endif
+}
+
+/*-------------------------------------------------------------------*
+ * COMPATIBILITY STUFF *
+ *-------------------------------------------------------------------*/
+
+// Starts urb and waits for completion or timeout
+static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ DECLARE_WAIT_QUEUE_HEAD(wqh);
+ api_wrapper_data awd;
+ int status;
+
+ awd.wakeup = &wqh;
+ awd.handler = 0;
+ init_waitqueue_head(&wqh);
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&wqh, &wait);
+ urb->context = &awd;
+ status = usb_submit_urb(urb);
+ if (status) {
+ // something went wrong
+ usb_free_urb(urb);
+ remove_wait_queue(&wqh, &wait);
+ return status;
+ }
+
+ if (urb->status == -EINPROGRESS) {
+ while (timeout && urb->status == -EINPROGRESS)
+ status = timeout = schedule_timeout(timeout);
+ } else
+ status = 1;
+
+ remove_wait_queue(&wqh, &wait);
+
+ if (!status) {
+ // timeout
+ printk("usb_control/bulk_msg: timeout\n");
+ usb_unlink_urb(urb); // remove urb safely
+ status = -ETIMEDOUT;
+ } else
+ status = urb->status;
+
+ if (actual_length)
+ *actual_length = urb->actual_length;
+
+ usb_free_urb(urb);
+ return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) or length (positive)
+int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
+ devrequest *cmd, void *data, int len, int timeout)
+{
+ urb_t *urb;
+ int retv;
+ int length;
+
+ urb = usb_alloc_urb(0);
+ if (!urb)
+ return -ENOMEM;
+
+ FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, /* build urb */
+ (usb_complete_t)usb_api_blocking_completion,0);
+
+ retv = usb_start_wait_urb(urb, timeout, &length);
+ if (retv < 0)
+ return retv;
+ else
+ return length;
+
+}
+
+/*-------------------------------------------------------------------*/
+int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
+ __u16 value, __u16 index, void *data, __u16 size, int timeout)
+{
+ devrequest *dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+ int ret;
+
+ if (!dr)
+ return -ENOMEM;
+
+ dr->requesttype = requesttype;
+ dr->request = request;
+ dr->value = cpu_to_le16p(&value);
+ dr->index = cpu_to_le16p(&index);
+ dr->length = cpu_to_le16p(&size);
+
+ //dbg("usb_control_msg");
+
+ ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+
+ kfree(dr);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------*/
+/* compatibility wrapper, builds bulk urb, and waits for completion */
+/* synchronous behavior */
+
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout)
+{
+ urb_t *urb;
+
+ if (len < 0)
+ return -EINVAL;
+
+ urb=usb_alloc_urb(0);
+ if (!urb)
+ return -ENOMEM;
+
+ FILL_BULK_URB(urb,usb_dev,pipe,(unsigned char*)data,len, /* build urb */
+ (usb_complete_t)usb_api_blocking_completion,0);
+
+ return usb_start_wait_urb(urb,timeout,actual_length);
+}
+
+/*
+ * usb_get_current_frame_number()
+ *
+ * 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)
+{
+ return usb_dev->bus->op->get_frame_number (usb_dev);
+}
+/*-------------------------------------------------------------------*/
+
+static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size)
+{
+ struct usb_descriptor_header *header;
+ unsigned char *begin;
+ int parsed = 0, len, numskipped;
+
+ header = (struct usb_descriptor_header *)buffer;
+
+ /* Everything should be fine being passed into here, but we sanity */
+ /* check JIC */
+ if (header->bLength > size) {
+ err("ran out of descriptors parsing");
+ return -1;
+ }
+
+ if (header->bDescriptorType != USB_DT_ENDPOINT) {
+ warn("unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X",
+ endpoint->bDescriptorType, USB_DT_ENDPOINT);
+ return parsed;
+ }
+
+ if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE)
+ memcpy(endpoint, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
+ else
+ memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE);
+
+ le16_to_cpus(&endpoint->wMaxPacketSize);
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ parsed += header->bLength;
+
+ /* Skip over the rest of the Class Specific or Vendor Specific */
+ /* descriptors */
+ begin = buffer;
+ numskipped = 0;
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength < 2) {
+ err("invalid descriptor length of %d", header->bLength);
return -1;
- n_desc = *(unsigned short *)ptr;
- n_len = n_desc & 0xff;
+ }
+
+ /* If we find another descriptor which is at or below us */
+ /* in the descriptor heirarchy then we're done */
+ if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+ (header->bDescriptorType == USB_DT_INTERFACE) ||
+ (header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE))
+ break;
+
+ dbg("skipping descriptor 0x%X",
+ header->bDescriptorType);
+ numskipped++;
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ parsed += header->bLength;
+ }
+ if (numskipped)
+ dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+ /* Copy any unknown descriptors into a storage area for drivers */
+ /* to later parse */
+ len = (int)(buffer - begin);
+ if (!len) {
+ endpoint->extra = NULL;
+ endpoint->extralen = 0;
+ return parsed;
+ }
+
+ endpoint->extra = kmalloc(len, GFP_KERNEL);
+
+ if (!endpoint->extra) {
+ err("couldn't allocate memory for endpoint extra descriptors");
+ endpoint->extralen = 0;
+ return parsed;
+ }
- if (n_desc == ((desctype << 8) + descindex))
- break;
+ memcpy(endpoint->extra, begin, len);
+ endpoint->extralen = len;
- if (((n_desc >> 8)&0xFF) == desctype &&
- n_len > descindex)
- {
- printk("bug: oversized descriptor.\n");
- break;
- }
-
- if (n_len < 2 || n_len > len)
- {
- printk("Short descriptor.\n");
- return -1;
- }
- printk(
- "Expected descriptor %02X/%02X, got %02X/%02X - skipping\n",
- desctype, descindex,
- (n_desc >> 8) & 0xFF, n_desc & 0xFF);
- for (i = 0 ; i < n_len; i++)
- printk(" %d %02x\n", i, ptr[i]);
- len -= n_len;
- ptr += n_len;
- parsed += n_len;
- }
-
- printk("Found %02X:%02X\n",
- desctype, descindex);
return parsed;
}
-/*
- * Parse the even more incomprehensible mess made of the USB spec
- * by USB audio having private magic to go with it.
- */
-
-static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desctype)
+static int usb_parse_interface(struct usb_device *dev, struct usb_interface *interface, unsigned char *buffer, int size)
{
- int n_len = ptr[0];
+ int i, len, numskipped, retval, parsed = 0;
+ struct usb_descriptor_header *header;
+ struct usb_interface_descriptor *ifp;
+ unsigned char *begin;
- if (n_len < 2 || n_len > len)
- {
- printk("Short descriptor.\n");
+ interface->act_altsetting = 0;
+ interface->num_altsetting = 0;
+ interface->max_altsetting = USB_ALTSETTINGALLOC;
+
+ interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+
+ if (!interface->altsetting) {
+ err("couldn't kmalloc interface->altsetting");
return -1;
}
- if (ptr[1] == desctype)
- return 0;
-
- return -1;
-}
+ while (size > 0) {
+ if (interface->num_altsetting >= interface->max_altsetting) {
+ void *ptr;
+ int oldmas;
+
+ oldmas = interface->max_altsetting;
+ interface->max_altsetting += USB_ALTSETTINGALLOC;
+ if (interface->max_altsetting > USB_MAXALTSETTING) {
+ warn("too many alternate settings (max %d)",
+ USB_MAXALTSETTING);
+ return -1;
+ }
+
+ ptr = interface->altsetting;
+ interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+ if (!interface->altsetting) {
+ err("couldn't kmalloc interface->altsetting");
+ interface->altsetting = ptr;
+ return -1;
+ }
+ memcpy(interface->altsetting, ptr, sizeof(struct usb_interface_descriptor) * oldmas);
+ kfree(ptr);
+ }
-static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len)
-{
- int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7);
- int i;
+ ifp = interface->altsetting + interface->num_altsetting;
+ interface->num_altsetting++;
- if (parsed < 0)
- return parsed;
- memcpy(endpoint, ptr + parsed, ptr[parsed]);
+ memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);
- parsed += ptr[parsed];
- len -= ptr[parsed];
+ /* Skip over the interface */
+ buffer += ifp->bLength;
+ parsed += ifp->bLength;
+ size -= ifp->bLength;
+
+ begin = buffer;
+ numskipped = 0;
+
+ /* Skip over any interface, class or vendor descriptors */
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength < 2) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ /* If we find another descriptor which is at or below */
+ /* us in the descriptor heirarchy then return */
+ if ((header->bDescriptorType == USB_DT_INTERFACE) ||
+ (header->bDescriptorType == USB_DT_ENDPOINT) ||
+ (header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE))
+ break;
- while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0)
- {
- usb_audio_endpoint(endpoint, ptr+parsed+i);
- len -= ptr[parsed+i];
- parsed += ptr[parsed+i];
- }
-
- return parsed;// + ptr[parsed];
-}
+ numskipped++;
-static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len)
-{
- int i;
- int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9);
- int retval;
+ buffer += header->bLength;
+ parsed += header->bLength;
+ size -= header->bLength;
+ }
- if (parsed < 0)
- return parsed;
+ if (numskipped)
+ dbg("skipped %d class/vendor specific interface descriptors", numskipped);
- memcpy(interface, ptr + parsed, *ptr);
- len -= ptr[parsed];
- parsed += ptr[parsed];
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (!len) {
+ ifp->extra = NULL;
+ ifp->extralen = 0;
+ } else {
+ ifp->extra = kmalloc(len, GFP_KERNEL);
+
+ if (!ifp->extra) {
+ err("couldn't allocate memory for interface extra descriptors");
+ ifp->extralen = 0;
+ return -1;
+ }
+ memcpy(ifp->extra, begin, len);
+ ifp->extralen = len;
+ }
- while((i=usb_check_descriptor(ptr+parsed, len, 0x24))>=0)
- {
- usb_audio_interface(interface, ptr+parsed+i);
- len -= ptr[parsed+i];
- parsed += ptr[parsed+i];
- }
-
- if (interface->bNumEndpoints > USB_MAXENDPOINTS)
- {
- printk(KERN_WARNING "usb: too many endpoints.\n");
- return -1;
- }
+ /* Did we hit an unexpected descriptor? */
+ header = (struct usb_descriptor_header *)buffer;
+ if ((size >= sizeof(struct usb_descriptor_header)) &&
+ ((header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE)))
+ return parsed;
- interface->endpoint = (struct usb_endpoint_descriptor *)
- kmalloc(interface->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
- if(interface->endpoint==NULL)
- {
- printk(KERN_WARNING "usb: out of memory.\n");
- return -1;
- }
- memset(interface->endpoint, 0, interface->bNumEndpoints*sizeof(struct usb_endpoint_descriptor));
+ if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+ warn("too many endpoints");
+ return -1;
+ }
+
+ ifp->endpoint = (struct usb_endpoint_descriptor *)
+ kmalloc(ifp->bNumEndpoints *
+ sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
+ if (!ifp->endpoint) {
+ err("out of memory");
+ return -1;
+ }
+
+ memset(ifp->endpoint, 0, ifp->bNumEndpoints *
+ sizeof(struct usb_endpoint_descriptor));
- for (i = 0; i < interface->bNumEndpoints; i++) {
-// if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) {
-// parsed += 9; /* skip over the HID descriptor for now */
-// len -= 9;
-// }
- retval = usb_parse_endpoint(dev, interface->endpoint + i, ptr + parsed, len);
- if (retval < 0) return retval;
- parsed += retval;
- len -= retval;
+ for (i = 0; i < ifp->bNumEndpoints; i++) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength > size) {
+ err("ran out of descriptors parsing");
+ return -1;
+ }
+
+ retval = usb_parse_endpoint(dev, ifp->endpoint + i, buffer, size);
+ if (retval < 0)
+ return retval;
+
+ buffer += retval;
+ parsed += retval;
+ size -= retval;
+ }
+
+ /* We check to see if it's an alternate to this one */
+ ifp = (struct usb_interface_descriptor *)buffer;
+ if (size < USB_DT_INTERFACE_SIZE ||
+ ifp->bDescriptorType != USB_DT_INTERFACE ||
+ !ifp->bAlternateSetting)
+ return parsed;
}
+
return parsed;
}
-static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len)
+int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer)
{
int i;
- int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);
-
- if (parsed < 0)
- return parsed;
+ int retval;
+ int size;
+ struct usb_descriptor_header *header;
- memcpy(config, ptr + parsed, *ptr);
- len -= *ptr;
- parsed += *ptr;
+ memcpy(config, buffer, USB_DT_CONFIG_SIZE);
+ le16_to_cpus(&config->wTotalLength);
+ size = config->wTotalLength;
- if (config->bNumInterfaces > USB_MAXINTERFACES)
- {
- printk(KERN_WARNING "usb: too many interfaces.\n");
+ if (config->bNumInterfaces > USB_MAXINTERFACES) {
+ warn("too many interfaces");
return -1;
-
}
- config->interface = (struct usb_interface_descriptor *)
- kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL);
- if(config->interface==NULL)
- {
- printk(KERN_WARNING "usb: out of memory.\n");
+ config->interface = (struct usb_interface *)
+ kmalloc(config->bNumInterfaces *
+ sizeof(struct usb_interface), GFP_KERNEL);
+ dbg("kmalloc IF %p, numif %i",config->interface,config->bNumInterfaces);
+ if (!config->interface) {
+ err("out of memory");
return -1;
}
- memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor));
+
+ memset(config->interface, 0,
+ config->bNumInterfaces * sizeof(struct usb_interface));
+
+ buffer += config->bLength;
+ size -= config->bLength;
for (i = 0; i < config->bNumInterfaces; i++) {
- int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len);
- if (retval < 0)
- return parsed; // HACK
-// return retval;
- parsed += retval;
- len -= retval;
- }
- return parsed;
-}
+ int numskipped, len;
+ char *begin;
-int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes)
-{
- int i;
- unsigned char *ptr = __buf;
+ /* Skip over the rest of the Class Specific or Vendor */
+ /* Specific descriptors */
+ begin = buffer;
+ numskipped = 0;
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if ((header->bLength > size) || (header->bLength < 2)) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ /* If we find another descriptor which is at or below */
+ /* us in the descriptor heirarchy then we're done */
+ if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+ (header->bDescriptorType == USB_DT_INTERFACE) ||
+ (header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE))
+ break;
- if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
- {
- printk(KERN_WARNING "usb: too many configurations.\n");
- return -1;
- }
+ dbg("skipping descriptor 0x%X", header->bDescriptorType);
+ numskipped++;
- dev->config = (struct usb_config_descriptor *)
- kmalloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor), GFP_KERNEL);
- if(dev->config==NULL)
- {
- printk(KERN_WARNING "usb: out of memory.\n");
- return -1;
- }
- memset(dev->config, 0, dev->descriptor.bNumConfigurations*sizeof(struct usb_config_descriptor));
- for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
- int retval = usb_parse_config(dev, dev->config + i, ptr, bytes);
+ buffer += header->bLength;
+ size -= header->bLength;
+ }
+ if (numskipped)
+ dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (!len) {
+ config->extra = NULL;
+ config->extralen = 0;
+ } else {
+ config->extra = kmalloc(len, GFP_KERNEL);
+ if (!config->extra) {
+ err("couldn't allocate memory for config extra descriptors");
+ config->extralen = 0;
+ return -1;
+ }
+
+ memcpy(config->extra, begin, len);
+ config->extralen = len;
+ }
+
+ retval = usb_parse_interface(dev, config->interface + i, buffer, size);
if (retval < 0)
return retval;
- ptr += retval;
- bytes += retval;
+
+ buffer += retval;
+ size -= retval;
}
- return 0;
+
+ return size;
}
void usb_destroy_configuration(struct usb_device *dev)
{
- int c, i;
- struct usb_config_descriptor *cf;
- struct usb_interface_descriptor *ifp;
+ int c, i, j, k;
- if(dev->config==NULL)
+ if (!dev->config)
return;
- for(c=0;c<dev->descriptor.bNumConfigurations;c++)
- {
- cf=&dev->config[c];
- if(cf->interface==NULL)
+
+ if (dev->rawdescriptors) {
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+ kfree(dev->rawdescriptors[i]);
+
+ kfree(dev->rawdescriptors);
+ }
+
+ for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+ struct usb_config_descriptor *cf = &dev->config[c];
+
+ if (!cf->interface)
break;
- for(i=0;i<cf->bNumInterfaces;i++)
- {
- ifp=&cf->interface[i];
- if(ifp->endpoint==NULL)
+
+ for (i = 0; i < cf->bNumInterfaces; i++) {
+ struct usb_interface *ifp =
+ &cf->interface[i];
+
+ if (!ifp->altsetting)
break;
- kfree(ifp->endpoint);
+
+ for (j = 0; j < ifp->num_altsetting; j++) {
+ struct usb_interface_descriptor *as =
+ &ifp->altsetting[j];
+
+ if(as->extra) {
+ kfree(as->extra);
+ }
+
+ if (!as->endpoint)
+ break;
+
+ for(k = 0; k < as->bNumEndpoints; k++) {
+ if(as->endpoint[k].extra) {
+ kfree(as->endpoint[k].extra);
+ }
+ }
+ kfree(as->endpoint);
+ }
+
+ kfree(ifp->altsetting);
}
kfree(cf->interface);
}
kfree(dev->config);
}
-
-void usb_init_root_hub(struct usb_device *dev)
+
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+ int retval;
+
+ for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+ *utf++ = *ascii++ & 0x7f;
+ *utf++ = 0;
+ }
+ return retval;
+}
+
+/*
+ * root_hub_string is used by each host controller's root hub code,
+ * so that they're identified consistently throughout the system.
+ */
+int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+ char buf [30];
+
+ // assert (len > (2 * (sizeof (buf) + 1)));
+ // assert (strlen (type) <= 8);
+
+ // language ids
+ if (id == 0) {
+ *data++ = 4; *data++ = 3; /* 4 bytes data */
+ *data++ = 0; *data++ = 0; /* some language id */
+ return 4;
+
+ // serial number
+ } else if (id == 1) {
+ sprintf (buf, "%x", serial);
+
+ // product description
+ } else if (id == 2) {
+ sprintf (buf, "USB %s Root Hub", type);
+
+ // id 3 == vendor description
+
+ // unsupported IDs --> "stall"
+ } else
+ return 0;
+
+ data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+ data [1] = 3;
+ return data [0];
+}
+
+/*
+ * __usb_get_extra_descriptor() finds a descriptor of specific type in the
+ * extra field of the interface and endpoint descriptor structs.
+ */
+
+int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)
{
- dev->devnum = -1;
- dev->slow = 0;
+ struct usb_descriptor_header *header;
+
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength < 2) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ if (header->bDescriptorType == type) {
+ *ptr = header;
+ return 0;
+ }
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ }
+ return -1;
}
/*
@@ -340,29 +1448,46 @@
void usb_disconnect(struct usb_device **pdev)
{
struct usb_device * dev = *pdev;
+ int i;
- if (dev) {
- int i;
+ if (!dev)
+ return;
- *pdev = NULL;
+ *pdev = NULL;
- printk("USB disconnect on device %d\n", dev->devnum);
+ info("USB disconnect on device %d", dev->devnum);
- if(dev->driver) dev->driver->disconnect(dev);
+ call_policy ("remove", dev);
- /* Free up all the children.. */
- for (i = 0; i < USB_MAXCHILDREN; i++) {
- struct usb_device **child = dev->children + i;
- usb_disconnect(child);
+ 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) {
+ down(&driver->serialize);
+ driver->disconnect(dev, interface->private_data);
+ up(&driver->serialize);
+ usb_driver_release_interface(driver, interface);
+ }
}
+ }
- /* Free up the device itself, including its device number */
- if (dev->devnum > 0)
- clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
- dev->bus->op->deallocate(dev);
+ /* Free up all the children.. */
+ for (i = 0; i < USB_MAXCHILDREN; i++) {
+ struct usb_device **child = dev->children + i;
+ if (*child)
+ usb_disconnect(child);
+ }
+
+ /* Free the device number and remove the /proc/bus/usb entry */
+ if (dev->devnum > 0) {
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ usbdevfs_remove_device(dev);
}
-}
+ /* Free up the device itself */
+ usb_free_dev(dev);
+}
/*
* Connect a new USB device. This basically just initializes
@@ -373,9 +1498,11 @@
void usb_connect(struct usb_device *dev)
{
int devnum;
-
- dev->descriptor.bMaxPacketSize0 = 8; /* XXX fixed 8 bytes for now */
-
+ // FIXME needs locking for SMP!!
+ /* why? this is called only from the hub thread,
+ * which hopefully doesn't run on multiple CPU's simulatenously 8-)
+ */
+ dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
if (devnum < 128) {
set_bit(devnum, dev->bus->devmap.devicemap);
@@ -387,287 +1514,578 @@
* These are the actual routines to send
* and receive control messages.
*/
-int usb_set_address(struct usb_device *dev)
-{
- devrequest dr;
- dr.requesttype = 0;
- dr.request = USB_REQ_SET_ADDRESS;
- dr.value = dev->devnum;
- dr.index = 0;
- dr.length = 0;
+#define GET_TIMEOUT 3
+#define SET_TIMEOUT 3
- return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0);
+int usb_set_address(struct usb_device *dev)
+{
+ return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
+ 0, dev->devnum, 0, NULL, 0, HZ * GET_TIMEOUT);
}
int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
{
- devrequest dr;
-
- dr.requesttype = 0x80;
- dr.request = USB_REQ_GET_DESCRIPTOR;
- dr.value = (type << 8) + index;
- dr.index = 0;
- dr.length = size;
+ int i = 5;
+ int result;
+
+ memset(buf,0,size); // Make sure we parse really received data
- return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+ while (i--) {
+ if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+ (type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 ||
+ result == -EPIPE)
+ break;
+ }
+ return result;
}
-int usb_get_device_descriptor(struct usb_device *dev)
+int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
+ unsigned char type, unsigned char id, void *buf, int size)
{
- return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+ (type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
}
-int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
{
- devrequest dr;
-
- dr.requesttype = USB_RT_HUB | 0x80;
- dr.request = USB_REQ_GET_DESCRIPTOR;
- dr.value = (USB_DT_HUB << 8);
- dr.index = 0;
- dr.length = size;
-
- return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size);
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+ (USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT);
}
-int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+int usb_get_device_descriptor(struct usb_device *dev)
{
- devrequest dr;
-
- dr.requesttype = USB_RT_PORT;
- dr.request = USB_REQ_CLEAR_FEATURE;
- dr.value = feature;
- dr.index = port;
- dr.length = 0;
+ int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor,
+ sizeof(dev->descriptor));
+ if (ret >= 0) {
+ le16_to_cpus(&dev->descriptor.bcdUSB);
+ le16_to_cpus(&dev->descriptor.idVendor);
+ le16_to_cpus(&dev->descriptor.idProduct);
+ le16_to_cpus(&dev->descriptor.bcdDevice);
+ }
+ return ret;
+}
- return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+int usb_get_status(struct usb_device *dev, int type, int target, void *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ * GET_TIMEOUT);
}
-int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+int usb_get_protocol(struct usb_device *dev, int ifnum)
{
- devrequest dr;
+ unsigned char type;
+ int ret;
- dr.requesttype = USB_RT_PORT;
- dr.request = USB_REQ_SET_FEATURE;
- dr.value = feature;
- dr.index = port;
- dr.length = 0;
+ if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_PROTOCOL, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, ifnum, &type, 1, HZ * GET_TIMEOUT)) < 0)
+ return ret;
- return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+ return type;
}
-int usb_get_hub_status(struct usb_device *dev, void *data)
+int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol)
{
- devrequest dr;
-
- dr.requesttype = USB_RT_HUB | 0x80;
- dr.request = USB_REQ_GET_STATUS;
- dr.value = 0;
- dr.index = 0;
- dr.length = 4;
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ protocol, ifnum, NULL, 0, HZ * SET_TIMEOUT);
+}
- return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (duration << 8) | report_id, ifnum, NULL, 0, HZ * SET_TIMEOUT);
}
-int usb_get_port_status(struct usb_device *dev, int port, void *data)
+void usb_set_maxpacket(struct usb_device *dev)
{
- devrequest dr;
+ int i, b;
- dr.requesttype = USB_RT_PORT | 0x80;
- dr.request = USB_REQ_GET_STATUS;
- dr.value = 0;
- dr.index = port;
- dr.length = 4;
+ for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *ifp = dev->actconfig->interface + i;
+ struct usb_interface_descriptor *as = ifp->altsetting + ifp->act_altsetting;
+ struct usb_endpoint_descriptor *ep = as->endpoint;
+ int e;
- return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+ for (e=0; e<as->bNumEndpoints; e++) {
+ b = ep[e].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ if ((ep[e].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_CONTROL) { /* Control => bidirectional */
+ dev->epmaxpacketout[b] = ep[e].wMaxPacketSize;
+ dev->epmaxpacketin [b] = ep[e].wMaxPacketSize;
+ }
+ else if (usb_endpoint_out(ep[e].bEndpointAddress)) {
+ if (ep[e].wMaxPacketSize > dev->epmaxpacketout[b])
+ dev->epmaxpacketout[b] = ep[e].wMaxPacketSize;
+ }
+ else {
+ if (ep[e].wMaxPacketSize > dev->epmaxpacketin [b])
+ dev->epmaxpacketin [b] = ep[e].wMaxPacketSize;
+ }
+ }
+ }
}
-int usb_get_protocol(struct usb_device *dev)
+/*
+ * endp: endpoint number in bits 0-3;
+ * direction flag in bit 7 (1 = IN, 0 = OUT)
+ */
+int usb_clear_halt(struct usb_device *dev, int pipe)
{
- unsigned char buf[8];
- devrequest dr;
+ int result;
+ __u16 status;
+ int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
- dr.requesttype = USB_RT_HIDD | 0x80;
- dr.request = USB_REQ_GET_PROTOCOL;
- dr.value = 0;
- dr.index = 1;
- dr.length = 1;
+/*
+ if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp)))
+ return 0;
+*/
- if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1))
- return -1;
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT);
- return buf[0];
-}
+ /* don't clear if failed */
+ if (result < 0)
+ return result;
-int usb_set_protocol(struct usb_device *dev, int protocol)
-{
- devrequest dr;
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp,
+ &status, sizeof(status), HZ * SET_TIMEOUT);
+ if (result < 0)
+ return result;
- dr.requesttype = USB_RT_HIDD;
- dr.request = USB_REQ_SET_PROTOCOL;
- dr.value = protocol;
- dr.index = 1;
- dr.length = 0;
+ if (le16_to_cpu(status) & 1)
+ return -EPIPE; /* still halted */
- if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
- return -1;
+ usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
+ /* toggle is reset on clear */
+
+ usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
return 0;
}
-/* keyboards want a nonzero duration according to HID spec, but
- mice should use infinity (0) -keryan */
-int usb_set_idle(struct usb_device *dev, int duration, int report_id)
-{
- devrequest dr;
-
- dr.requesttype = USB_RT_HIDD;
- dr.request = USB_REQ_SET_IDLE;
- dr.value = (duration << 8) | report_id;
- dr.index = 1;
- dr.length = 0;
-
- if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
- return -1;
+int usb_set_interface(struct usb_device *dev, int interface, int alternate)
+{
+ struct usb_interface *iface;
+ int ret;
+ iface = usb_ifnum_to_if(dev, interface);
+ if (!iface) {
+ warn("selecting invalid interface %d", interface);
+ return -EINVAL;
+ }
+
+ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate,
+ interface, NULL, 0, HZ * 5)) < 0)
+ return ret;
+
+ iface->act_altsetting = alternate;
+ dev->toggle[0] = 0; /* 9.1.1.5 says to do this */
+ dev->toggle[1] = 0;
+ usb_set_maxpacket(dev);
return 0;
}
int usb_set_configuration(struct usb_device *dev, int configuration)
{
- devrequest dr;
-
- dr.requesttype = 0;
- dr.request = USB_REQ_SET_CONFIGURATION;
- dr.value = configuration;
- dr.index = 0;
- dr.length = 0;
+ 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];
+ break;
+ }
+ }
+ if (!cp) {
+ warn("selecting invalid configuration %d", configuration);
+ return -EINVAL;
+ }
- if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
- return -1;
+ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * SET_TIMEOUT)) < 0)
+ return ret;
+
+ dev->actconfig = cp;
+ dev->toggle[0] = 0;
+ dev->toggle[1] = 0;
+ usb_set_maxpacket(dev);
return 0;
}
-int usb_get_report(struct usb_device *dev)
+int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
{
- unsigned char buf[8];
- devrequest dr;
-
- dr.requesttype = USB_RT_HIDD | 0x80;
- dr.request = USB_REQ_GET_REPORT;
- dr.value = 0x100;
- dr.index = 1;
- dr.length = 3;
-
- if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3))
- return -1;
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
+}
- return buf[0];
+int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, ifnum, buf, size, HZ);
}
int usb_get_configuration(struct usb_device *dev)
{
- unsigned int cfgno,size;
- unsigned char buffer[400];
- unsigned char * bufptr;
-
- bufptr=buffer;
- for (cfgno=0;cfgno<dev->descriptor.bNumConfigurations;cfgno++) {
- /* Get the first 8 bytes - guaranteed */
- if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8))
- return -1;
+ int result;
+ unsigned int cfgno, length;
+ unsigned char buffer[8];
+ unsigned char *bigbuffer;
+ struct usb_config_descriptor *desc =
+ (struct usb_config_descriptor *)buffer;
+
+ if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {
+ warn("too many configurations");
+ return -EINVAL;
+ }
+
+ if (dev->descriptor.bNumConfigurations < 1) {
+ warn("not enough configurations");
+ return -EINVAL;
+ }
+
+ dev->config = (struct usb_config_descriptor *)
+ kmalloc(dev->descriptor.bNumConfigurations *
+ sizeof(struct usb_config_descriptor), GFP_KERNEL);
+ if (!dev->config) {
+ err("out of memory");
+ return -ENOMEM;
+ }
+ memset(dev->config, 0, dev->descriptor.bNumConfigurations *
+ sizeof(struct usb_config_descriptor));
+
+ dev->rawdescriptors = (char **)kmalloc(sizeof(char *) *
+ dev->descriptor.bNumConfigurations, GFP_KERNEL);
+ if (!dev->rawdescriptors) {
+ err("out of memory");
+ return -ENOMEM;
+ }
+
+ for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
+ /* We grab the first 8 bytes so we know how long the whole */
+ /* configuration is */
+ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
+ if (result < 8) {
+ if (result < 0)
+ err("unable to get descriptor");
+ else {
+ err("config descriptor too short (expected %i, got %i)", 8, result);
+ result = -EINVAL;
+ }
+ goto err;
+ }
/* Get the full buffer */
- size = *(unsigned short *)(bufptr+2);
- if (bufptr+size > buffer+sizeof(buffer))
- {
- printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
- size = buffer+sizeof(buffer)-bufptr;
+ length = le16_to_cpu(desc->wTotalLength);
+
+ bigbuffer = kmalloc(length, GFP_KERNEL);
+ if (!bigbuffer) {
+ err("unable to allocate memory for configuration descriptors");
+ result = -ENOMEM;
+ goto err;
+ }
+
+ /* Now that we know the length, get the whole thing */
+ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
+ if (result < 0) {
+ err("couldn't get all of config descriptors");
+ kfree(bigbuffer);
+ goto err;
+ }
+
+ if (result < length) {
+ err("config descriptor too short (expected %i, got %i)", length, result);
+ result = -EINVAL;
+ kfree(bigbuffer);
+ goto err;
+ }
+
+ dev->rawdescriptors[cfgno] = bigbuffer;
+
+ result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
+ if (result > 0)
+ dbg("descriptor data left");
+ else if (result < 0) {
+ result = -EINVAL;
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ dev->descriptor.bNumConfigurations = cfgno;
+ return result;
+}
+
+/*
+ * usb_string:
+ * returns string length (> 0) or error (< 0)
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+ unsigned char *tbuf;
+ int err;
+ unsigned int u, idx;
+
+ if (size <= 0 || !buf || !index)
+ return -EINVAL;
+ buf[0] = 0;
+ tbuf = kmalloc(256, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
+
+ /* get langid for strings if it's not yet known */
+ if (!dev->have_langid) {
+ err = usb_get_string(dev, 0, 0, tbuf, 4);
+ if (err < 0) {
+ err("error getting string descriptor 0 (error=%d)", err);
+ goto errout;
+ } else if (tbuf[0] < 4) {
+ err("string descriptor 0 too short");
+ err = -EINVAL;
+ goto errout;
+ } else {
+ dev->have_langid = -1;
+ dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+ /* always use the first langid listed */
+ dbg("USB device number %d default language ID 0x%x",
+ dev->devnum, dev->string_langid);
}
- if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size))
- return -1;
-
- /* Prepare for next configuration */
- bufptr+=size;
}
- return usb_parse_configuration(dev, buffer, size);
+
+ /*
+ * Just ask for a maximum length string and then take the length
+ * that was returned.
+ */
+ err = usb_get_string(dev, dev->string_langid, index, tbuf, 255);
+ if (err < 0)
+ goto errout;
+
+ size--; /* leave room for trailing NULL char in output buffer */
+ for (idx = 0, u = 2; u < err; u += 2) {
+ if (idx >= size)
+ break;
+ if (tbuf[u+1]) /* high byte */
+ buf[idx++] = '?'; /* non-ASCII character */
+ else
+ buf[idx++] = tbuf[u];
+ }
+ buf[idx] = 0;
+ err = idx;
+
+ errout:
+ kfree(tbuf);
+ return err;
}
/*
* 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..
+ *
+ * Returns 0 for success, != 0 for error.
*/
-void usb_new_device(struct usb_device *dev)
+int usb_new_device(struct usb_device *dev)
{
- int addr, i;
+ int err;
- printk("USB new device connect, assigned device number %d\n",
- dev->devnum);
+ info("USB new device connect, assigned device number %d", dev->devnum);
+
+ /* USB v1.1 5.5.3 */
+ /* We read the first 8 bytes from the device descriptor to get to */
+ /* the bMaxPacketSize0 field. Then we set the maximum packet size */
+ /* for the control pipe, and retrieve the rest */
+ dev->epmaxpacketin [0] = 8;
+ dev->epmaxpacketout[0] = 8;
+
+ 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;
+ return 1;
+ }
- dev->maxpacketsize = 0; /* Default to 8 byte max packet size */
+ wait_ms(10); /* Let the SET_ADDRESS settle */
- addr = dev->devnum;
- dev->devnum = 0;
+ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
+ if (err < 8) {
+ if (err < 0)
+ err("USB device not responding, giving up (error=%d)", err);
+ else
+ err("USB device descriptor short read (expected %i, got %i)",8,err);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+ dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
+ dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
+
+ err = usb_get_device_descriptor(dev);
+ if (err < sizeof(dev->descriptor)) {
+ if (err < 0)
+ err("unable to get device descriptor (error=%d)", err);
+ else
+ err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err);
+
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ err = usb_get_configuration(dev);
+ if (err < 0) {
+ err("unable to get configuration (error=%d)", err);
+ usb_destroy_configuration(dev);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ /* we set the default configuration here */
+ err = usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+ if (err) {
+ err("failed to set default configuration (error=%d)", err);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
+ dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+#ifdef DEBUG
+ if (dev->descriptor.iManufacturer)
+ usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
+ if (dev->descriptor.iProduct)
+ usb_show_string(dev, "Product", dev->descriptor.iProduct);
+ if (dev->descriptor.iSerialNumber)
+ usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+#endif
- /* Slow devices */
- for (i = 0; i < 5; i++) {
- if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
- break;
+ /* now that the basic setup is over, add a /proc/bus/usb entry */
+ usbdevfs_add_device(dev);
- printk("get_descriptor failed, waiting\n");
- wait_ms(200);
- }
- if (i == 5) {
- printk("giving up\n");
- return;
- }
+ /* find drivers willing to handle this device */
+ usb_find_drivers(dev);
-#if 0
- printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
-#endif
- switch (dev->descriptor.bMaxPacketSize0) {
- case 8: dev->maxpacketsize = 0; break;
- case 16: dev->maxpacketsize = 1; break;
- case 32: dev->maxpacketsize = 2; break;
- case 64: dev->maxpacketsize = 3; break;
- }
-#if 0
- printk("dev->mps: %d\n", dev->maxpacketsize);
-#endif
+ /* userspace may load modules and/or configure further */
+ call_policy ("add", dev);
- dev->devnum = addr;
+ return 0;
+}
- if (usb_set_address(dev)) {
- printk("Unable to set address\n");
- /* FIXME: We should disable the port */
- return;
- }
+static int usb_open(struct inode * inode, struct file * file)
+{
+ int minor = MINOR(inode->i_rdev);
+ struct usb_driver *c = usb_minors[minor/16];
+ int err = -ENODEV;
- wait_ms(10); /* Let the SET_ADDRESS settle */
+ if (!c || !c->fops)
+ return err;
- if (usb_get_device_descriptor(dev)) {
- printk("Unable to get device descriptor\n");
- return;
- }
+ file->f_op = c->fops;
- if (usb_get_configuration(dev)) {
- printk("Unable to get configuration\n");
- return;
+ return file->f_op->open(inode,file);
+}
+
+static struct file_operations usb_fops = {
+ open: usb_open,
+};
+
+int usb_major_init(void)
+{
+ if (register_chrdev(USB_MAJOR,"usb",&usb_fops)) {
+ err("unable to get major %d for usb devices", USB_MAJOR);
+ return -EBUSY;
}
+ return 0;
+}
+
+void usb_major_cleanup(void)
+{
+ unregister_chrdev(USB_MAJOR, "usb");
+}
-#if 0
- printk("Vendor: %X\n", dev->descriptor.idVendor);
- printk("Product: %X\n", dev->descriptor.idProduct);
-#endif
- usb_device_descriptor(dev);
+#ifdef CONFIG_PROC_FS
+struct list_head *usb_driver_get_list(void)
+{
+ return &usb_driver_list;
}
-int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+struct list_head *usb_bus_get_list(void)
{
- return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id);
+ return &usb_bus_list;
}
+#endif
+
+/*
+ * USB may be built into the kernel or be built as modules.
+ * If the USB core [and maybe a host controller driver] is built
+ * into the kernel, and other device drivers are built as modules,
+ * then these symbols need to be exported for the modules to use.
+ */
+EXPORT_SYMBOL(usb_ifnum_to_if);
+
+EXPORT_SYMBOL(usb_register);
+EXPORT_SYMBOL(usb_deregister);
+EXPORT_SYMBOL(usb_scan_devices);
+EXPORT_SYMBOL(usb_alloc_bus);
+EXPORT_SYMBOL(usb_free_bus);
+EXPORT_SYMBOL(usb_register_bus);
+EXPORT_SYMBOL(usb_deregister_bus);
+EXPORT_SYMBOL(usb_alloc_dev);
+EXPORT_SYMBOL(usb_free_dev);
+EXPORT_SYMBOL(usb_inc_dev_use);
+
+EXPORT_SYMBOL(usb_driver_claim_interface);
+EXPORT_SYMBOL(usb_interface_claimed);
+EXPORT_SYMBOL(usb_driver_release_interface);
+
+EXPORT_SYMBOL(usb_root_hub_string);
+EXPORT_SYMBOL(usb_new_device);
+EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL(usb_connect);
+EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(usb_check_bandwidth);
+EXPORT_SYMBOL(usb_claim_bandwidth);
+EXPORT_SYMBOL(usb_release_bandwidth);
+
+EXPORT_SYMBOL(usb_set_address);
+EXPORT_SYMBOL(usb_get_descriptor);
+EXPORT_SYMBOL(usb_get_class_descriptor);
+EXPORT_SYMBOL(__usb_get_extra_descriptor);
+EXPORT_SYMBOL(usb_get_device_descriptor);
+EXPORT_SYMBOL(usb_get_string);
+EXPORT_SYMBOL(usb_string);
+EXPORT_SYMBOL(usb_get_protocol);
+EXPORT_SYMBOL(usb_set_protocol);
+EXPORT_SYMBOL(usb_get_report);
+EXPORT_SYMBOL(usb_set_report);
+EXPORT_SYMBOL(usb_set_idle);
+EXPORT_SYMBOL(usb_clear_halt);
+EXPORT_SYMBOL(usb_set_interface);
+EXPORT_SYMBOL(usb_get_configuration);
+EXPORT_SYMBOL(usb_set_configuration);
+
+EXPORT_SYMBOL(usb_get_current_frame_number);
+
+EXPORT_SYMBOL(usb_alloc_urb);
+EXPORT_SYMBOL(usb_free_urb);
+EXPORT_SYMBOL(usb_submit_urb);
+EXPORT_SYMBOL(usb_unlink_urb);
+EXPORT_SYMBOL(usb_control_msg);
+EXPORT_SYMBOL(usb_bulk_msg);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)