!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/usb/   drwxr-xr-x
Free 318.37 GB of 458.09 GB (69.5%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     usb.c (64.59 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 * drivers/usb/usb.c
 *
 * (C) Copyright Linus Torvalds 1999
 * (C) Copyright Johannes Erdfelt 1999-2001
 * (C) Copyright Andreas Gal 1999
 * (C) Copyright Gregory P. Smith 1999
 * (C) Copyright Deti Fliegl 1999 (new USB architecture)
 * (C) Copyright Randy Dunlap 2000
 * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
 * (C) Copyright Yggdrasil Computing, Inc. 2000
 *     (usb_device_id matching changes by Adam J. Richter)
 *
 * NOTE! This is not actually a driver at all, rather this is
 * just a collection of helper routines that implement the
 * generic USB things that the real drivers can use..
 *
 * Think of this as a "USB library" rather than anything else.
 * It should be considered a slave, with no callbacks. Callbacks
 * are evil.
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/interrupt.h>  /* for in_interrupt() */
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/spinlock.h>

#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

extern int  usb_hub_init(void);
extern void usb_hub_cleanup(void);

/*
 * 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.
 */
LIST_HEAD(usb_driver_list);
LIST_HEAD(usb_bus_list);
struct semaphore usb_bus_list_lock;

devfs_handle_t usb_devfs_handle;    /* /dev/usb dir. */

static struct usb_busmap busmap;

static struct usb_driver *usb_minors[16];

/**
 *    usb_register - register a USB driver
 *    @new_driver: USB operations for the driver
 *
 *    Registers a USB driver with the USB core.  The list of unattached
 *    interfaces will be rescanned whenever a new driver is added, allowing
 *    the new driver to attach to any recognized devices.
 *    Returns a negative error code on failure and 0 on success.
 */
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_tail(&new_driver->driver_list, &usb_driver_list);

    usb_scan_devices();

    return 0;
}

/**
 *    usb_scan_devices - scans all unclaimed USB interfaces
 *
 *    Goes through all unclaimed USB interfaces, and offers them to all
 *    registered USB drivers through the 'probe' function.
 *    This will automatically be called after usb_register is called.
 *    It is called by some of the USB subsystems after one of their subdrivers
 *    are registered.
 */
void usb_scan_devices(void)
{
    struct list_head *tmp;

    down (&usb_bus_list_lock);
    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);
    }
    up (&usb_bus_list_lock);
}

/*
 * 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);
            /* if driver->disconnect didn't release the interface */
            if (interface->driver)
                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);
        }
    }
}

/**
 *    usb_deregister - unregister a USB driver
 *    @driver: USB operations of the driver to unregister
 *
 *    Unlinks the specified driver from the internal USB driver list.
 */
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);

    down (&usb_bus_list_lock);
    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);
    }
    up (&usb_bus_list_lock);
}

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;
}

struct usb_endpoint_descriptor *usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)
{
    int i, j, k;

    for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
        for (j = 0; j < dev->actconfig->interface[i].num_altsetting; j++)
            for (k = 0; k < dev->actconfig->interface[i].altsetting[j].bNumEndpoints; k++)
                if (epnum == dev->actconfig->interface[i].altsetting[j].endpoint[k].bEndpointAddress)
                    return &dev->actconfig->interface[i].altsetting[j].endpoint[k];

    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;

#ifdef USB_BANDWIDTH_MESSAGES
    dbg("bandwidth alloc increased by %d to %d for %d requesters",
        bustime,
        dev->bus->bandwidth_allocated,
        dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
#endif
}

/*
 * 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--;

#ifdef USB_BANDWIDTH_MESSAGES
    dbg("bandwidth 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);
#endif
    urb->bandwidth = 0;
}

static void usb_bus_get(struct usb_bus *bus)
{
    atomic_inc(&bus->refcnt);
}

static void usb_bus_put(struct usb_bus *bus)
{
    if (atomic_dec_and_test(&bus->refcnt))
        kfree(bus);
}

/**
 *    usb_alloc_bus - creates a new USB host controller structure
 *    @op: pointer to a struct usb_operations that this bus structure should use
 *
 *    Creates a USB host controller bus structure with the specified 
 *    usb_operations and initializes all the necessary internal objects.
 *    (For use only by USB Host Controller Drivers.)
 *
 *    If no memory is available, NULL is returned.
 *
 *    The caller should call usb_free_bus() when it is finished with the structure.
 */
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));

#ifdef DEVNUM_ROUND_ROBIN
    bus->devnum_next = 1;
#endif /* DEVNUM_ROUND_ROBIN */

    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);

    atomic_set(&bus->refcnt, 1);

    return bus;
}

/**
 *    usb_free_bus - frees the memory used by a bus structure
 *    @bus: pointer to the bus to free
 *
 *    (For use only by USB Host Controller Drivers.)
 */
void usb_free_bus(struct usb_bus *bus)
{
    if (!bus)
        return;

    usb_bus_put(bus);
}

/**
 *    usb_register_bus - registers the USB host controller with the usb core
 *    @bus: pointer to the bus to register
 *
 *    (For use only by USB Host Controller Drivers.)
 */
void usb_register_bus(struct usb_bus *bus)
{
    int busnum;

    down (&usb_bus_list_lock);
    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");

    usb_bus_get(bus);

    /* Add it to the list of buses */
    list_add(&bus->bus_list, &usb_bus_list);
    up (&usb_bus_list_lock);

    usbdevfs_add_bus(bus);

    info("new USB bus registered, assigned bus number %d", bus->busnum);
}

/**
 *    usb_deregister_bus - deregisters the USB host controller
 *    @bus: pointer to the bus to deregister
 *
 *    (For use only by USB Host Controller Drivers.)
 */
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
     */
    down (&usb_bus_list_lock);
    list_del(&bus->bus_list);
    up (&usb_bus_list_lock);

    usbdevfs_remove_bus(bus);

    clear_bit(bus->busnum, busmap.busmap);

    usb_bus_put(bus);
}

/*
 * 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;
}


/**
 * usb_match_id - find first usb_device_id matching device or interface
 * @dev: the device whose descriptors are considered when matching
 * @interface: the interface of interest
 * @id: array of usb_device_id structures, terminated by zero entry
 *
 * usb_match_id searches an array of usb_device_id's and returns
 * the first one matching the device or interface, or null.
 * This is used when binding (or rebinding) a driver to an interface.
 * Most USB device drivers will use this indirectly, through the usb core,
 * but some layered driver frameworks use it directly.
 * These device tables are exported with MODULE_DEVICE_TABLE, through
 * modutils and "modules.usbmap", to support the driver loading
 * functionality of USB hotplugging.
 *
 * What Matches:
 *
 * The "match_flags" element in a usb_device_id controls which
 * members are used.  If the corresponding bit is set, the
 * value in the device_id must match its corresponding member
 * in the device or interface descriptor, or else the device_id
 * does not match.
 *
 * "driver_info" is normally used only by device drivers,
 * but you can create a wildcard "matches anything" usb_device_id
 * as a driver's "modules.usbmap" entry if you provide an id with
 * only a nonzero "driver_info" field.  If you do this, the USB device
 * driver's probe() routine should use additional intelligence to
 * decide whether to bind to the specified interface.
 * 
 * What Makes Good usb_device_id Tables:
 *
 * The match algorithm is very simple, so that intelligence in
 * driver selection must come from smart driver id records.
 * Unless you have good reasons to use another selection policy,
 * provide match elements only in related groups, and order match
 * specifiers from specific to general.  Use the macros provided
 * for that purpose if you can.
 *
 * The most specific match specifiers use device descriptor
 * data.  These are commonly used with product-specific matches;
 * the USB_DEVICE macro lets you provide vendor and product IDs,
 * and you can also match against ranges of product revisions.
 * These are widely used for devices with application or vendor
 * specific bDeviceClass values.
 *
 * Matches based on device class/subclass/protocol specifications
 * are slightly more general; use the USB_DEVICE_INFO macro, or
 * its siblings.  These are used with single-function devices
 * where bDeviceClass doesn't specify that each interface has
 * its own class. 
 *
 * Matches based on interface class/subclass/protocol are the
 * most general; they let drivers bind to any interface on a
 * multiple-function device.  Use the USB_INTERFACE_INFO
 * macro, or its siblings, to match class-per-interface style 
 * devices (as recorded in bDeviceClass).
 *  
 * Within those groups, remember that not all combinations are
 * meaningful.  For example, don't give a product version range
 * without vendor and product IDs; or specify a protocol without
 * its associated class and subclass.
 */   
const struct usb_device_id *
usb_match_id(struct usb_device *dev, struct usb_interface *interface,
         const struct usb_device_id *id)
{
    struct usb_interface_descriptor    *intf = 0;

    /* proc_connectinfo in devio.c may call us with id == NULL. */
    if (id == NULL)
        return NULL;

    /* It is important to check that id->driver_info is nonzero,
       since an entry that is all zeroes except for a nonzero
       id->driver_info is the way to create an entry that
       indicates that the driver want to examine every
       device and interface. */
    for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
           id->driver_info; id++) {

        if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
            id->idVendor != dev->descriptor.idVendor)
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
            id->idProduct != dev->descriptor.idProduct)
            continue;

        /* No need to test id->bcdDevice_lo != 0, since 0 is never
           greater than any unsigned number. */
        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
            (id->bcdDevice_lo > dev->descriptor.bcdDevice))
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
            (id->bcdDevice_hi < dev->descriptor.bcdDevice))
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
            (id->bDeviceClass != dev->descriptor.bDeviceClass))
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
            (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
            (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
            continue;

        intf = &interface->altsetting [interface->act_altsetting];

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
            (id->bInterfaceClass != intf->bInterfaceClass))
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
            (id->bInterfaceSubClass != intf->bInterfaceSubClass))
            continue;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
            (id->bInterfaceProtocol != intf->bInterfaceProtocol))
            continue;

        return id;
    }

    return 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 interface.
 *
 * "New Style" drivers use a table describing the devices and interfaces
 * they handle.  Those tables are available to user mode tools deciding
 * whether to load driver modules for a new device.
 *
 * 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_interface_driver(struct usb_device *dev, unsigned ifnum)
{
    struct list_head *tmp;
    struct usb_interface *interface;
    void *private;
    const struct usb_device_id *id;
    struct usb_driver *driver;
    int i;
    
    if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
        err("bad find_interface_driver params");
        return -1;
    }

    down(&dev->serialize);

    interface = dev->actconfig->interface + ifnum;

    if (usb_interface_claimed(interface))
        goto out_err;

    private = NULL;
    for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) {
        driver = list_entry(tmp, struct usb_driver, driver_list);
        tmp = tmp->next;

        id = driver->id_table;
        /* new style driver? */
        if (id) {
            for (i = 0; i < interface->num_altsetting; i++) {
                  interface->act_altsetting = i;
                id = usb_match_id(dev, interface, id);
                if (id) {
                    down(&driver->serialize);
                    private = driver->probe(dev,ifnum,id);
                    up(&driver->serialize);
                    if (private != NULL)
                        break;
                }
            }

            /* if driver not bound, leave defaults unchanged */
            if (private == NULL)
                interface->act_altsetting = 0;
        } else { /* "old style" driver */
            down(&driver->serialize);
            private = driver->probe(dev, ifnum, NULL);
            up(&driver->serialize);
        }

        /* probe() may have changed the config on us */
        interface = dev->actconfig->interface + ifnum;

        if (private) {
            usb_driver_claim_interface(driver, interface, private);
            up(&dev->serialize);
            return 0;
        }
    }

out_err:
    up(&dev->serialize);
    return -1;
}


#ifdef    CONFIG_HOTPLUG

/*
 * USB hotplugging invokes what /proc/sys/kernel/hotplug says
 * (normally /sbin/hotplug) when USB devices get added or removed.
 *
 * This invokes a user mode policy agent, typically helping to load driver
 * or other modules, configure the device, and more.  Drivers can provide
 * a MODULE_DEVICE_TABLE to help with module loading subtasks.
 *
 * 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;
    }

    /* 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.
     */
    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.
     *
     * FIXME reduce hardwired intelligence here
     */
    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 common */
    envp [i++] = scratch;
    scratch += sprintf (scratch, "PRODUCT=%x/%x/%x",
        dev->descriptor.idVendor,
        dev->descriptor.idProduct,
        dev->descriptor.bcdDevice) + 1;

    /* class-based driver binding models */
    envp [i++] = scratch;
    scratch += sprintf (scratch, "TYPE=%d/%d/%d",
                dev->descriptor.bDeviceClass,
                dev->descriptor.bDeviceSubClass,
                dev->descriptor.bDeviceProtocol) + 1;
    if (dev->descriptor.bDeviceClass == 0) {
        int alt = dev->actconfig->interface [0].act_altsetting;

        /* a simple/common case: one config, one interface, one driver
         * with current altsetting being a reasonable setting.
         * everything needs a smart agent and usbdevfs; or can rely on
         * device-specific binding policies.
         */
        envp [i++] = scratch;
        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, ... ? */
    }
    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);
    if (value != 0)
        dbg ("kusbd policy returned 0x%x", value);
}

#else

static inline void
call_policy (char *verb, struct usb_device *dev)
{ } 

#endif    /* CONFIG_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 (vend/prod 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
    }
}

/*
 * 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
 */
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
{
    struct usb_device *dev;

    dev = kmalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return NULL;

    memset(dev, 0, sizeof(*dev));

    usb_bus_get(bus);

    dev->bus = bus;
    dev->parent = parent;
    atomic_set(&dev->refcnt, 1);
    INIT_LIST_HEAD(&dev->inodes);
    INIT_LIST_HEAD(&dev->filelist);

    init_MUTEX(&dev->serialize);

    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);

        usb_bus_put(dev->bus);

        kfree(dev);
    }
}

void usb_inc_dev_use(struct usb_device *dev)
{
    atomic_inc(&dev->refcnt);
}

/* ------------------------------------------------------------------------------------- 
 * New USB Core Functions
 * -------------------------------------------------------------------------------------*/

/**
 *    usb_alloc_urb - creates a new urb for a USB driver to use
 *    @iso_packets: number of iso packets for this urb
 *
 *    Creates an urb for the USB driver to use and returns a pointer to it.
 *    If no memory is available, NULL is returned.
 *
 *    If the driver want to use this urb for interrupt, control, or bulk
 *    endpoints, pass '0' as the number of iso packets.
 *
 *    The driver should call usb_free_urb() when it is finished with the urb.
 */
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;
}

/**
 *    usb_free_urb - frees the memory used by a urb
 *    @urb: pointer to the urb to free
 *
 *    If an urb is created with a call to usb_create_urb() it should be
 *    cleaned up with a call to usb_free_urb() when the driver is finished
 *    with it.
 */
void usb_free_urb(urb_t* urb)
{
    if (urb)
        kfree(urb);
}
/*-------------------------------------------------------------------*/
int usb_submit_urb(urb_t *urb)
{
    if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
        return urb->dev->bus->op->submit_urb(urb);
    else
        return -ENODEV;
}

/*-------------------------------------------------------------------*/
int usb_unlink_urb(urb_t *urb)
{
    if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
        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)
{
    struct usb_api_data *awd = (struct usb_api_data *)urb->context;

    awd->done = 1;
    wmb();
    wake_up(&awd->wqh);
}

/*-------------------------------------------------------------------*
 *                         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);
    struct usb_api_data awd;
    int status;

    init_waitqueue_head(&awd.wqh);     
    awd.done = 0;

    set_current_state(TASK_UNINTERRUPTIBLE);
    add_wait_queue(&awd.wqh, &wait);

    urb->context = &awd;
    status = usb_submit_urb(urb);
    if (status) {
        // something went wrong
        usb_free_urb(urb);
        set_current_state(TASK_RUNNING);
        remove_wait_queue(&awd.wqh, &wait);
        return status;
    }

    while (timeout && !awd.done)
    {
        timeout = schedule_timeout(timeout);
        set_current_state(TASK_UNINTERRUPTIBLE);
        rmb();
    }

    set_current_state(TASK_RUNNING);
    remove_wait_queue(&awd.wqh, &wait);

    if (!timeout && !awd.done) {
        if (urb->status != -EINPROGRESS) {    /* No callback?!! */
            printk(KERN_ERR "usb: raced timeout, "
                "pipe 0x%x status %d time left %d\n",
                urb->pipe, urb->status, timeout);
            status = urb->status;
        } else {
            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,
           usb_api_blocking_completion, 0);

    retv = usb_start_wait_urb(urb, timeout, &length);
    if (retv < 0)
        return retv;
    else
        return length;
}

/**
 *    usb_control_msg - Builds a control urb, sends it off and waits for completion
 *    @dev: pointer to the usb device to send the message to
 *    @pipe: endpoint "pipe" to send the message to
 *    @request: USB message request value
 *    @requesttype: USB message request type value
 *    @value: USB message value
 *    @index: USB message index value
 *    @data: pointer to the data to send
 *    @size: length in bytes of the data to send
 *    @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever)
 *
 *    This function sends a simple control message to a specified endpoint
 *    and waits for the message to complete, or timeout.
 *    
 *    If successful, it returns 0, othwise a negative error number.
 *
 *    Don't use this function from within an interrupt context, like a
 *    bottom half handler.  If you need a asyncronous message, or need to send
 *    a message from within interrupt context, use usb_submit_urb()
 */
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;
}


/**
 *    usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
 *    @usb_dev: pointer to the usb device to send the message to
 *    @pipe: endpoint "pipe" to send the message to
 *    @data: pointer to the data to send
 *    @len: length in bytes of the data to send
 *    @actual_length: pointer to a location to put the actual length transferred in bytes
 *    @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever)
 *
 *    This function sends a simple bulk message to a specified endpoint
 *    and waits for the message to complete, or timeout.
 *    
 *    If successful, it returns 0, othwise a negative error number.
 *    The number of actual bytes transferred will be plaed in the 
 *    actual_timeout paramater.
 *
 *    Don't use this function from within an interrupt context, like a
 *    bottom half handler.  If you need a asyncronous message, or need to
 *    send a message from within interrupt context, use usb_submit_urb()
 */
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, data, len,
            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_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;
        }

        /* If we find another "proper" descriptor 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;
    }

    memcpy(endpoint->extra, begin, len);
    endpoint->extralen = len;

    return parsed;
}

static int usb_parse_interface(struct usb_interface *interface, unsigned char *buffer, int size)
{
    int i, len, numskipped, retval, parsed = 0;
    struct usb_descriptor_header *header;
    struct usb_interface_descriptor *ifp;
    unsigned char *begin;

    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;
    }

    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);
        }

        ifp = interface->altsetting + interface->num_altsetting;
        interface->num_altsetting++;

        memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);

        /* 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 "proper" descriptor then we're done  */
            if ((header->bDescriptorType == USB_DT_INTERFACE) ||
                (header->bDescriptorType == USB_DT_ENDPOINT) ||
                (header->bDescriptorType == USB_DT_CONFIG) ||
                (header->bDescriptorType == USB_DT_DEVICE))
                break;

            numskipped++;

            buffer += header->bLength;
            parsed += header->bLength;
            size -= header->bLength;
        }

        if (numskipped)
            dbg("skipped %d class/vendor specific interface descriptors", numskipped);

        /* 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;
        }

        /* 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;

        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 < 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(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;
}

int usb_parse_configuration(struct usb_config_descriptor *config, char *buffer)
{
    int i, retval, size;
    struct usb_descriptor_header *header;

    memcpy(config, buffer, USB_DT_CONFIG_SIZE);
    le16_to_cpus(&config->wTotalLength);
    size = config->wTotalLength;

    if (config->bNumInterfaces > USB_MAXINTERFACES) {
        warn("too many interfaces");
        return -1;
    }

    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));

    buffer += config->bLength;
    size -= config->bLength;
    
    config->extra = NULL;
    config->extralen = 0;

    for (i = 0; i < config->bNumInterfaces; i++) {
        int numskipped, len;
        char *begin;

        /* 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 "proper" descriptor 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;
        }
        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) {
            if (config->extralen) {
                warn("extra config descriptor");
            } 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(config->interface + i, buffer, size);
        if (retval < 0)
            return retval;

        buffer += retval;
        size -= retval;
    }

    return size;
}

void usb_destroy_configuration(struct usb_device *dev)
{
    int c, i, j, k;
    
    if (!dev->config)
        return;

    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++) {
            struct usb_interface *ifp =
                &cf->interface[i];
                
            if (!ifp->altsetting)
                break;

            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);
}

/* 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)
{
    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;
}

/*
 * Something got disconnected. Get rid of it, and all of its children.
 */
void usb_disconnect(struct usb_device **pdev)
{
    struct usb_device * dev = *pdev;
    int i;

    if (!dev)
        return;

    *pdev = NULL;

    info("USB disconnect on device %d", dev->devnum);

    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);
                /* if driver->disconnect didn't release the interface */
                if (interface->driver)
                    usb_driver_release_interface(driver, interface);
            }
        }
    }

    /* Free up all the children.. */
    for (i = 0; i < USB_MAXCHILDREN; i++) {
        struct usb_device **child = dev->children + i;
        if (*child)
            usb_disconnect(child);
    }

    /* Let policy agent unload modules etc */
    call_policy ("remove", dev);

    /* 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
 * the USB device information and sets up the topology - it's
 * up to the low-level driver to reset the port and actually
 * do the setup (the upper levels don't know how to do that).
 */
void usb_connect(struct usb_device *dev)
{
    int devnum;
    // FIXME needs locking for SMP!!
    /* why? this is called only from the hub thread, 
     * which hopefully doesn't run on multiple CPU's simultaneously 8-)
     */
    dev->descriptor.bMaxPacketSize0 = 8;  /* Start off at 8 bytes  */
#ifndef DEVNUM_ROUND_ROBIN
    devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
#else    /* round_robin alloc of devnums */
    /* Try to allocate the next devnum beginning at bus->devnum_next. */
    devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next);
    if (devnum >= 128)
        devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);

    dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
#endif    /* round_robin alloc of devnums */

    if (devnum < 128) {
        set_bit(devnum, dev->bus->devmap.devicemap);
        dev->devnum = devnum;
    }
}

/*
 * These are the actual routines to send
 * and receive control messages.
 */
#ifdef CONFIG_USB_LONG_TIMEOUT
#define GET_TIMEOUT 4
#else
#define GET_TIMEOUT 3
#endif
#define SET_TIMEOUT 3

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)
{
    int i = 5;
    int result;
    
    memset(buf,0,size);    // Make sure we parse really received data

    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;    /* retry if the returned length was 0; flaky device */
    }
    return result;
}

int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
        unsigned char type, unsigned char id, void *buf, int size)
{
    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_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int 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_get_device_descriptor(struct usb_device *dev)
{
    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;
}

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_get_protocol(struct usb_device *dev, int ifnum)
{
    unsigned char type;
    int ret;

    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 type;
}

int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol)
{
    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);
}

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);
}

void usb_set_maxpacket(struct usb_device *dev)
{
    int i, b;

    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;

        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;
            }
        }
    }
}

/*
 * 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)
{
    int result;
    __u16 status;
    unsigned char *buffer;
    int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);

/*
    if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp)))
        return 0;
*/

    result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
        USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT);

    /* don't clear if failed */
    if (result < 0)
        return result;

    buffer = kmalloc(sizeof(status), GFP_KERNEL);
    if (!buffer) {
        err("unable to allocate memory for configuration descriptors");
        return -ENOMEM;
    }

    result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp,
        buffer, sizeof(status), HZ * SET_TIMEOUT);

    memcpy(&status, buffer, sizeof(status));
    kfree(buffer);

    if (result < 0)
        return result;

    if (le16_to_cpu(status) & 1)
        return -EPIPE;        /* still halted */

    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;
}

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;
    }

    /* 9.4.10 says devices don't need this, if the interface
       only has one alternate setting */
    if (iface->num_altsetting == 1) {
        warn("ignoring set_interface for dev %d, iface %d, alt %d",
            dev->devnum, interface, alternate);
        return 0;
    }

    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)
{
    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 ((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 ifnum, unsigned char type, unsigned char id, void *buf, int size)
{
    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);
}

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)
{
    int result;
    unsigned int cfgno, length;
    unsigned char *buffer;
    unsigned char *bigbuffer;
     struct usb_config_descriptor *desc;

    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;
    }

    buffer = kmalloc(8, GFP_KERNEL);
    if (!buffer) {
        err("unable to allocate memory for configuration descriptors");
        return -ENOMEM;
    }
    desc = (struct usb_config_descriptor *)buffer;

    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 */
        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->config[cfgno], bigbuffer);
        if (result > 0)
            dbg("descriptor data left");
        else if (result < 0) {
            result = -EINVAL;
            goto err;
        }
    }

    kfree(buffer);
    return 0;
err:
    kfree(buffer);
    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);
        }
    }

    /*
     * 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.
 */
int usb_new_device(struct usb_device *dev)
{
    int err;

    /* 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=%d (error=%d)",
            dev->devnum, err);
        clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
        dev->devnum = -1;
        return 1;
    }

    wait_ms(10);    /* Let the SET_ADDRESS settle */

    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 < (signed)sizeof(dev->descriptor)) {
        if (err < 0)
            err("unable to get device descriptor (error=%d)", err);
        else
            err("USB device descriptor short read (expected %Zi, 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 device %d configuration (error=%d)",
            dev->devnum, err);
        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 device %d default configuration (error=%d)",
            dev->devnum, 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

    /* now that the basic setup is over, add a /proc/bus/usb entry */
    usbdevfs_add_device(dev);

    /* find drivers willing to handle this device */
    usb_find_drivers(dev);

    /* userspace may load modules and/or configure further */
    call_policy ("add", dev);

    return 0;
}

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;
    struct file_operations *old_fops, *new_fops = NULL;

    /*
     * No load-on-demand? Randy, could you ACK that it's really not
     * supposed to be done?                    -- AV
     */
    if (!c || !(new_fops = fops_get(c->fops)))
        return err;
    old_fops = file->f_op;
    file->f_op = new_fops;
    /* Curiouser and curiouser... NULL ->open() as "no device" ? */
    if (file->f_op->open)
        err = file->f_op->open(inode,file);
    if (err) {
        fops_put(file->f_op);
        file->f_op = fops_get(old_fops);
    }
    fops_put(old_fops);
    return err;
}

static struct file_operations usb_fops = {
    owner:        THIS_MODULE,
    open:        usb_open,
};

int usb_major_init(void)
{
    if (devfs_register_chrdev(USB_MAJOR, "usb", &usb_fops)) {
        err("unable to get major %d for usb devices", USB_MAJOR);
        return -EBUSY;
    }

    usb_devfs_handle = devfs_mk_dir(NULL, "usb", NULL);

    return 0;
}

void usb_major_cleanup(void)
{
    devfs_unregister(usb_devfs_handle);
    devfs_unregister_chrdev(USB_MAJOR, "usb");
}


#ifdef CONFIG_PROC_FS
struct list_head *usb_driver_get_list(void)
{
    return &usb_driver_list;
}

struct list_head *usb_bus_get_list(void)
{
    return &usb_bus_list;
}
#endif


/*
 * Init
 */
static int __init usb_init(void)
{
    init_MUTEX(&usb_bus_list_lock);
    usb_major_init();
    usbdevfs_init();
    usb_hub_init();

    return 0;
}

/*
 * Cleanup
 */
static void __exit usb_exit(void)
{
    usb_major_cleanup();
    usbdevfs_cleanup();
    usb_hub_cleanup();
}

module_init(usb_init);
module_exit(usb_exit);

/*
 * 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_epnum_to_ep_desc);

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_match_id);

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_status);

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);

EXPORT_SYMBOL(usb_devfs_handle);
MODULE_LICENSE("GPL");

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0396 ]--