diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/misc |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/misc')
-rw-r--r-- | drivers/usb/misc/Kconfig | 152 | ||||
-rw-r--r-- | drivers/usb/misc/Makefile | 20 | ||||
-rw-r--r-- | drivers/usb/misc/auerswald.c | 2163 | ||||
-rw-r--r-- | drivers/usb/misc/cytherm.c | 430 | ||||
-rw-r--r-- | drivers/usb/misc/emi26.c | 255 | ||||
-rw-r--r-- | drivers/usb/misc/emi26_fw.h | 5779 | ||||
-rw-r--r-- | drivers/usb/misc/emi62.c | 298 | ||||
-rw-r--r-- | drivers/usb/misc/emi62_fw_m.h | 8853 | ||||
-rw-r--r-- | drivers/usb/misc/emi62_fw_s.h | 8837 | ||||
-rw-r--r-- | drivers/usb/misc/idmouse.c | 443 | ||||
-rw-r--r-- | drivers/usb/misc/legousbtower.c | 1088 | ||||
-rw-r--r-- | drivers/usb/misc/phidgetkit.c | 586 | ||||
-rw-r--r-- | drivers/usb/misc/phidgetservo.c | 342 | ||||
-rw-r--r-- | drivers/usb/misc/rio500.c | 563 | ||||
-rw-r--r-- | drivers/usb/misc/rio500_usb.h | 37 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/Kconfig | 14 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/Makefile | 6 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/sisusb.c | 3145 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/sisusb.h | 278 | ||||
-rw-r--r-- | drivers/usb/misc/usblcd.c | 404 | ||||
-rw-r--r-- | drivers/usb/misc/usbled.c | 181 | ||||
-rw-r--r-- | drivers/usb/misc/usbtest.c | 2140 | ||||
-rw-r--r-- | drivers/usb/misc/uss720.c | 674 |
23 files changed, 36688 insertions, 0 deletions
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig new file mode 100644 index 00000000000..3a896954b3a --- /dev/null +++ b/drivers/usb/misc/Kconfig @@ -0,0 +1,152 @@ +# +# USB Miscellaneous driver configuration +# +comment "USB Miscellaneous drivers" + depends on USB + +config USB_EMI62 + tristate "EMI 6|2m USB Audio interface support" + depends on USB + ---help--- + This driver loads firmware to Emagic EMI 6|2m low latency USB + Audio and Midi interface. + + After firmware load the device is handled with standard linux + USB Audio driver. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called audio. If you want to compile it as a + module, say M here and read <file:Documentation/kbuild/modules.txt>. + +config USB_EMI26 + tristate "EMI 2|6 USB Audio interface support" + depends on USB + ---help--- + This driver loads firmware to Emagic EMI 2|6 low latency USB + Audio interface. + + After firmware load the device is handled with standard linux + USB Audio driver. + + To compile this driver as a module, choose M here: the + module will be called emi26. + +config USB_AUERSWALD + tristate "USB Auerswald ISDN support (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + Say Y here if you want to connect an Auerswald USB ISDN Device + to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called auerswald. + +config USB_RIO500 + tristate "USB Diamond Rio500 support (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + Say Y here if you want to connect a USB Rio500 mp3 player to your + computer's USB port. Please read <file:Documentation/usb/rio.txt> + for more information. + + To compile this driver as a module, choose M here: the + module will be called rio500. + +config USB_LEGOTOWER + tristate "USB Lego Infrared Tower support (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + Say Y here if you want to connect a USB Lego Infrared Tower to your + computer's USB port. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called legousbtower. If you want to compile it as + a module, say M here and read + <file:Documentation/kbuild/modules.txt>. + +config USB_LCD + tristate "USB LCD driver support" + depends on USB + help + Say Y here if you want to connect an USBLCD to your computer's + USB port. The USBLCD is a small USB interface board for + alphanumeric LCD modules. See <http://www.usblcd.de/> for more + information. + + To compile this driver as a module, choose M here: the + module will be called usblcd. + +config USB_LED + tristate "USB LED driver support" + depends on USB + help + Say Y here if you want to connect an USBLED device to your + computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called usbled. + +config USB_CYTHERM + tristate "Cypress USB thermometer driver support" + depends on USB + help + Say Y here if you want to connect a Cypress USB thermometer + device to your computer's USB port. This device is also known + as the Cypress USB Starter kit or demo board. The Elektor + magazine published a modified version of this device in issue + #291. + + To compile this driver as a module, choose M here: the + module will be called cytherm. + +config USB_PHIDGETKIT + tristate "USB PhidgetKit support" + depends on USB + help + Say Y here if you want to connect a PhidgetKit USB device from + Phidgets Inc. + + To compile this driver as a module, choose M here: the + module will be called phidgetkit. + +config USB_PHIDGETSERVO + tristate "USB PhidgetServo support" + depends on USB + help + Say Y here if you want to connect an 1 or 4 Motor PhidgetServo + servo controller version 2.0 or 3.0. + + Phidgets Inc. has a web page at <http://www.phidgets.com/>. + + To compile this driver as a module, choose M here: the + module will be called phidgetservo. + +config USB_IDMOUSE + tristate "Siemens ID USB Mouse Fingerprint sensor support" + depends on USB + help + Say Y here if you want to use the fingerprint sensor on + the Siemens ID Mouse. There is also a Siemens ID Mouse + _Professional_, which has not been tested with this driver, + but uses the same sensor and may therefore work. + + This driver creates an entry "/dev/idmouseX" or "/dev/usb/idmouseX", + which can be used by, e.g.,"cat /dev/idmouse0 > fingerprint.pnm". + + See also <http://www.fs.tum.de/~echtler/idmouse/>. + +source "drivers/usb/misc/sisusbvga/Kconfig" + +config USB_TEST + tristate "USB testing driver (DEVELOPMENT)" + depends on USB && USB_DEVICEFS && EXPERIMENTAL + help + This driver is for testing host controller software. It is used + with specialized device firmware for regression and stress testing, + to help prevent problems from cropping up with "real" drivers. + + See <http://www.linux-usb.org/usbtest/> for more information, + including sample test device firmware and "how to use it". + diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile new file mode 100644 index 00000000000..4a3814cbd48 --- /dev/null +++ b/drivers/usb/misc/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# + +obj-$(CONFIG_USB_AUERSWALD) += auerswald.o +obj-$(CONFIG_USB_CYTHERM) += cytherm.o +obj-$(CONFIG_USB_EMI26) += emi26.o +obj-$(CONFIG_USB_EMI62) += emi62.o +obj-$(CONFIG_USB_IDMOUSE) += idmouse.o +obj-$(CONFIG_USB_LCD) += usblcd.o +obj-$(CONFIG_USB_LED) += usbled.o +obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o +obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o +obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o +obj-$(CONFIG_USB_RIO500) += rio500.o +obj-$(CONFIG_USB_TEST) += usbtest.o +obj-$(CONFIG_USB_USS720) += uss720.o + +obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
\ No newline at end of file diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c new file mode 100644 index 00000000000..a530bb976e4 --- /dev/null +++ b/drivers/usb/misc/auerswald.c @@ -0,0 +1,2163 @@ +/*****************************************************************************/ +/* + * auerswald.c -- Auerswald PBX/System Telephone usb driver. + * + * Copyright (C) 2001 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * Very much code of this driver is borrowed from dabusb.c (Deti Fliegl) + * and from the USB Skeleton driver (Greg Kroah-Hartman). Thank you. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + /*****************************************************************************/ + +/* Standard Linux module include files */ +#include <asm/uaccess.h> +#include <asm/byteorder.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/wait.h> +#undef DEBUG /* include debug macros until it's done */ +#include <linux/usb.h> + +/*-------------------------------------------------------------------*/ +/* Debug support */ +#ifdef DEBUG +#define dump( adr, len) \ +do { \ + unsigned int u; \ + printk (KERN_DEBUG); \ + for (u = 0; u < len; u++) \ + printk (" %02X", adr[u] & 0xFF); \ + printk ("\n"); \ +} while (0) +#else +#define dump( adr, len) +#endif + +/*-------------------------------------------------------------------*/ +/* Version Information */ +#define DRIVER_VERSION "0.9.11" +#define DRIVER_AUTHOR "Wolfgang Mües <wolfgang@iksw-muees.de>" +#define DRIVER_DESC "Auerswald PBX/System Telephone usb driver" + +/*-------------------------------------------------------------------*/ +/* Private declarations for Auerswald USB driver */ + +/* Auerswald Vendor ID */ +#define ID_AUERSWALD 0x09BF + +#define AUER_MINOR_BASE 112 /* auerswald driver minor number */ + +/* we can have up to this number of device plugged in at once */ +#define AUER_MAX_DEVICES 16 + + +/* Number of read buffers for each device */ +#define AU_RBUFFERS 10 + +/* Number of chain elements for each control chain */ +#define AUCH_ELEMENTS 20 + +/* Number of retries in communication */ +#define AU_RETRIES 10 + +/*-------------------------------------------------------------------*/ +/* vendor specific protocol */ +/* Header Byte */ +#define AUH_INDIRMASK 0x80 /* mask for direct/indirect bit */ +#define AUH_DIRECT 0x00 /* data is for USB device */ +#define AUH_INDIRECT 0x80 /* USB device is relay */ + +#define AUH_SPLITMASK 0x40 /* mask for split bit */ +#define AUH_UNSPLIT 0x00 /* data block is full-size */ +#define AUH_SPLIT 0x40 /* data block is part of a larger one, + split-byte follows */ + +#define AUH_TYPEMASK 0x3F /* mask for type of data transfer */ +#define AUH_TYPESIZE 0x40 /* different types */ +#define AUH_DCHANNEL 0x00 /* D channel data */ +#define AUH_B1CHANNEL 0x01 /* B1 channel transparent */ +#define AUH_B2CHANNEL 0x02 /* B2 channel transparent */ +/* 0x03..0x0F reserved for driver internal use */ +#define AUH_COMMAND 0x10 /* Command channel */ +#define AUH_BPROT 0x11 /* Configuration block protocol */ +#define AUH_DPROTANA 0x12 /* D channel protocol analyzer */ +#define AUH_TAPI 0x13 /* telephone api data (ATD) */ +/* 0x14..0x3F reserved for other protocols */ +#define AUH_UNASSIGNED 0xFF /* if char device has no assigned service */ +#define AUH_FIRSTUSERCH 0x11 /* first channel which is available for driver users */ + +#define AUH_SIZE 1 /* Size of Header Byte */ + +/* Split Byte. Only present if split bit in header byte set.*/ +#define AUS_STARTMASK 0x80 /* mask for first block of splitted frame */ +#define AUS_FIRST 0x80 /* first block */ +#define AUS_FOLLOW 0x00 /* following block */ + +#define AUS_ENDMASK 0x40 /* mask for last block of splitted frame */ +#define AUS_END 0x40 /* last block */ +#define AUS_NOEND 0x00 /* not the last block */ + +#define AUS_LENMASK 0x3F /* mask for block length information */ + +/* Request types */ +#define AUT_RREQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER) /* Read Request */ +#define AUT_WREQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER) /* Write Request */ + +/* Vendor Requests */ +#define AUV_GETINFO 0x00 /* GetDeviceInfo */ +#define AUV_WBLOCK 0x01 /* Write Block */ +#define AUV_RBLOCK 0x02 /* Read Block */ +#define AUV_CHANNELCTL 0x03 /* Channel Control */ +#define AUV_DUMMY 0x04 /* Dummy Out for retry */ + +/* Device Info Types */ +#define AUDI_NUMBCH 0x0000 /* Number of supported B channels */ +#define AUDI_OUTFSIZE 0x0001 /* Size of OUT B channel fifos */ +#define AUDI_MBCTRANS 0x0002 /* max. Blocklength of control transfer */ + +/* Interrupt endpoint definitions */ +#define AU_IRQENDP 1 /* Endpoint number */ +#define AU_IRQCMDID 16 /* Command-block ID */ +#define AU_BLOCKRDY 0 /* Command: Block data ready on ctl endpoint */ +#define AU_IRQMINSIZE 5 /* Nr. of bytes decoded in this driver */ + +/* Device String Descriptors */ +#define AUSI_VENDOR 1 /* "Auerswald GmbH & Co. KG" */ +#define AUSI_DEVICE 2 /* Name of the Device */ +#define AUSI_SERIALNR 3 /* Serial Number */ +#define AUSI_MSN 4 /* "MSN ..." (first) Multiple Subscriber Number */ + +#define AUSI_DLEN 100 /* Max. Length of Device Description */ + +#define AUV_RETRY 0x101 /* First Firmware version which can do control retries */ + +/*-------------------------------------------------------------------*/ +/* External data structures / Interface */ +typedef struct +{ + char __user *buf; /* return buffer for string contents */ + unsigned int bsize; /* size of return buffer */ +} audevinfo_t,*paudevinfo_t; + +/* IO controls */ +#define IOCTL_AU_SLEN _IOR( 'U', 0xF0, int) /* return the max. string descriptor length */ +#define IOCTL_AU_DEVINFO _IOWR('U', 0xF1, audevinfo_t) /* get name of a specific device */ +#define IOCTL_AU_SERVREQ _IOW( 'U', 0xF2, int) /* request a service channel */ +#define IOCTL_AU_BUFLEN _IOR( 'U', 0xF3, int) /* return the max. buffer length for the device */ +#define IOCTL_AU_RXAVAIL _IOR( 'U', 0xF4, int) /* return != 0 if Receive Data available */ +#define IOCTL_AU_CONNECT _IOR( 'U', 0xF5, int) /* return != 0 if connected to a service channel */ +#define IOCTL_AU_TXREADY _IOR( 'U', 0xF6, int) /* return != 0 if Transmitt channel ready to send */ +/* 'U' 0xF7..0xFF reseved */ + +/*-------------------------------------------------------------------*/ +/* Internal data structures */ + +/* ..................................................................*/ +/* urb chain element */ +struct auerchain; /* forward for circular reference */ +typedef struct +{ + struct auerchain *chain; /* pointer to the chain to which this element belongs */ + struct urb * urbp; /* pointer to attached urb */ + void *context; /* saved URB context */ + usb_complete_t complete; /* saved URB completion function */ + struct list_head list; /* to include element into a list */ +} auerchainelement_t,*pauerchainelement_t; + +/* urb chain */ +typedef struct auerchain +{ + pauerchainelement_t active; /* element which is submitted to urb */ + spinlock_t lock; /* protection agains interrupts */ + struct list_head waiting_list; /* list of waiting elements */ + struct list_head free_list; /* list of available elements */ +} auerchain_t,*pauerchain_t; + +/* urb blocking completion helper struct */ +typedef struct +{ + wait_queue_head_t wqh; /* wait for completion */ + unsigned int done; /* completion flag */ +} auerchain_chs_t,*pauerchain_chs_t; + +/* ...................................................................*/ +/* buffer element */ +struct auerbufctl; /* forward */ +typedef struct +{ + char *bufp; /* reference to allocated data buffer */ + unsigned int len; /* number of characters in data buffer */ + unsigned int retries; /* for urb retries */ + struct usb_ctrlrequest *dr; /* for setup data in control messages */ + struct urb * urbp; /* USB urb */ + struct auerbufctl *list; /* pointer to list */ + struct list_head buff_list; /* reference to next buffer in list */ +} auerbuf_t,*pauerbuf_t; + +/* buffer list control block */ +typedef struct auerbufctl +{ + spinlock_t lock; /* protection in interrupt */ + struct list_head free_buff_list;/* free buffers */ + struct list_head rec_buff_list; /* buffers with receive data */ +} auerbufctl_t,*pauerbufctl_t; + +/* ...................................................................*/ +/* service context */ +struct auerscon; /* forward */ +typedef void (*auer_dispatch_t)(struct auerscon*, pauerbuf_t); +typedef void (*auer_disconn_t) (struct auerscon*); +typedef struct auerscon +{ + unsigned int id; /* protocol service id AUH_xxxx */ + auer_dispatch_t dispatch; /* dispatch read buffer */ + auer_disconn_t disconnect; /* disconnect from device, wake up all char readers */ +} auerscon_t,*pauerscon_t; + +/* ...................................................................*/ +/* USB device context */ +typedef struct +{ + struct semaphore mutex; /* protection in user context */ + char name[20]; /* name of the /dev/usb entry */ + unsigned int dtindex; /* index in the device table */ + struct usb_device * usbdev; /* USB device handle */ + int open_count; /* count the number of open character channels */ + char dev_desc[AUSI_DLEN];/* for storing a textual description */ + unsigned int maxControlLength; /* max. Length of control paket (without header) */ + struct urb * inturbp; /* interrupt urb */ + char * intbufp; /* data buffer for interrupt urb */ + unsigned int irqsize; /* size of interrupt endpoint 1 */ + struct auerchain controlchain; /* for chaining of control messages */ + auerbufctl_t bufctl; /* Buffer control for control transfers */ + pauerscon_t services[AUH_TYPESIZE];/* context pointers for each service */ + unsigned int version; /* Version of the device */ + wait_queue_head_t bufferwait; /* wait for a control buffer */ +} auerswald_t,*pauerswald_t; + +/* ................................................................... */ +/* character device context */ +typedef struct +{ + struct semaphore mutex; /* protection in user context */ + pauerswald_t auerdev; /* context pointer of assigned device */ + auerbufctl_t bufctl; /* controls the buffer chain */ + auerscon_t scontext; /* service context */ + wait_queue_head_t readwait; /* for synchronous reading */ + struct semaphore readmutex; /* protection against multiple reads */ + pauerbuf_t readbuf; /* buffer held for partial reading */ + unsigned int readoffset; /* current offset in readbuf */ + unsigned int removed; /* is != 0 if device is removed */ +} auerchar_t,*pauerchar_t; + + +/*-------------------------------------------------------------------*/ +/* Forwards */ +static void auerswald_ctrlread_complete (struct urb * urb, struct pt_regs *regs); +static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp); +static struct usb_driver auerswald_driver; + + +/*-------------------------------------------------------------------*/ +/* USB chain helper functions */ +/* -------------------------- */ + +/* completion function for chained urbs */ +static void auerchain_complete (struct urb * urb, struct pt_regs *regs) +{ + unsigned long flags; + int result; + + /* get pointer to element and to chain */ + pauerchainelement_t acep = (pauerchainelement_t) urb->context; + pauerchain_t acp = acep->chain; + + /* restore original entries in urb */ + urb->context = acep->context; + urb->complete = acep->complete; + + dbg ("auerchain_complete called"); + + /* call original completion function + NOTE: this function may lead to more urbs submitted into the chain. + (no chain lock at calling complete()!) + acp->active != NULL is protecting us against recursion.*/ + urb->complete (urb, regs); + + /* detach element from chain data structure */ + spin_lock_irqsave (&acp->lock, flags); + if (acp->active != acep) /* paranoia debug check */ + dbg ("auerchain_complete: completion on non-active element called!"); + else + acp->active = NULL; + + /* add the used chain element to the list of free elements */ + list_add_tail (&acep->list, &acp->free_list); + acep = NULL; + + /* is there a new element waiting in the chain? */ + if (!acp->active && !list_empty (&acp->waiting_list)) { + /* yes: get the entry */ + struct list_head *tmp = acp->waiting_list.next; + list_del (tmp); + acep = list_entry (tmp, auerchainelement_t, list); + acp->active = acep; + } + spin_unlock_irqrestore (&acp->lock, flags); + + /* submit the new urb */ + if (acep) { + urb = acep->urbp; + dbg ("auerchain_complete: submitting next urb from chain"); + urb->status = 0; /* needed! */ + result = usb_submit_urb(urb, GFP_ATOMIC); + + /* check for submit errors */ + if (result) { + urb->status = result; + dbg("auerchain_complete: usb_submit_urb with error code %d", result); + /* and do error handling via *this* completion function (recursive) */ + auerchain_complete( urb, NULL); + } + } else { + /* simple return without submitting a new urb. + The empty chain is detected with acp->active == NULL. */ + }; +} + + +/* submit function for chained urbs + this function may be called from completion context or from user space! + early = 1 -> submit in front of chain +*/ +static int auerchain_submit_urb_list (pauerchain_t acp, struct urb * urb, int early) +{ + int result; + unsigned long flags; + pauerchainelement_t acep = NULL; + + dbg ("auerchain_submit_urb called"); + + /* try to get a chain element */ + spin_lock_irqsave (&acp->lock, flags); + if (!list_empty (&acp->free_list)) { + /* yes: get the entry */ + struct list_head *tmp = acp->free_list.next; + list_del (tmp); + acep = list_entry (tmp, auerchainelement_t, list); + } + spin_unlock_irqrestore (&acp->lock, flags); + + /* if no chain element available: return with error */ + if (!acep) { + return -ENOMEM; + } + + /* fill in the new chain element values */ + acep->chain = acp; + acep->context = urb->context; + acep->complete = urb->complete; + acep->urbp = urb; + INIT_LIST_HEAD (&acep->list); + + /* modify urb */ + urb->context = acep; + urb->complete = auerchain_complete; + urb->status = -EINPROGRESS; /* usb_submit_urb does this, too */ + + /* add element to chain - or start it immediately */ + spin_lock_irqsave (&acp->lock, flags); + if (acp->active) { + /* there is traffic in the chain, simple add element to chain */ + if (early) { + dbg ("adding new urb to head of chain"); + list_add (&acep->list, &acp->waiting_list); + } else { + dbg ("adding new urb to end of chain"); + list_add_tail (&acep->list, &acp->waiting_list); + } + acep = NULL; + } else { + /* the chain is empty. Prepare restart */ + acp->active = acep; + } + /* Spin has to be removed before usb_submit_urb! */ + spin_unlock_irqrestore (&acp->lock, flags); + + /* Submit urb if immediate restart */ + if (acep) { + dbg("submitting urb immediate"); + urb->status = 0; /* needed! */ + result = usb_submit_urb(urb, GFP_ATOMIC); + /* check for submit errors */ + if (result) { + urb->status = result; + dbg("auerchain_submit_urb: usb_submit_urb with error code %d", result); + /* and do error handling via completion function */ + auerchain_complete( urb, NULL); + } + } + + return 0; +} + +/* submit function for chained urbs + this function may be called from completion context or from user space! +*/ +static int auerchain_submit_urb (pauerchain_t acp, struct urb * urb) +{ + return auerchain_submit_urb_list (acp, urb, 0); +} + +/* cancel an urb which is submitted to the chain + the result is 0 if the urb is cancelled, or -EINPROGRESS if + URB_ASYNC_UNLINK is set and the function is successfully started. +*/ +static int auerchain_unlink_urb (pauerchain_t acp, struct urb * urb) +{ + unsigned long flags; + struct urb * urbp; + pauerchainelement_t acep; + struct list_head *tmp; + + dbg ("auerchain_unlink_urb called"); + + /* search the chain of waiting elements */ + spin_lock_irqsave (&acp->lock, flags); + list_for_each (tmp, &acp->waiting_list) { + acep = list_entry (tmp, auerchainelement_t, list); + if (acep->urbp == urb) { + list_del (tmp); + urb->context = acep->context; + urb->complete = acep->complete; + list_add_tail (&acep->list, &acp->free_list); + spin_unlock_irqrestore (&acp->lock, flags); + dbg ("unlink waiting urb"); + urb->status = -ENOENT; + urb->complete (urb, NULL); + return 0; + } + } + /* not found. */ + spin_unlock_irqrestore (&acp->lock, flags); + + /* get the active urb */ + acep = acp->active; + if (acep) { + urbp = acep->urbp; + + /* check if we have to cancel the active urb */ + if (urbp == urb) { + /* note that there is a race condition between the check above + and the unlink() call because of no lock. This race is harmless, + because the usb module will detect the unlink() after completion. + We can't use the acp->lock here because the completion function + wants to grab it. + */ + dbg ("unlink active urb"); + return usb_unlink_urb (urbp); + } + } + + /* not found anyway + ... is some kind of success + */ + dbg ("urb to unlink not found in chain"); + return 0; +} + +/* cancel all urbs which are in the chain. + this function must not be called from interrupt or completion handler. +*/ +static void auerchain_unlink_all (pauerchain_t acp) +{ + unsigned long flags; + struct urb * urbp; + pauerchainelement_t acep; + + dbg ("auerchain_unlink_all called"); + + /* clear the chain of waiting elements */ + spin_lock_irqsave (&acp->lock, flags); + while (!list_empty (&acp->waiting_list)) { + /* get the next entry */ + struct list_head *tmp = acp->waiting_list.next; + list_del (tmp); + acep = list_entry (tmp, auerchainelement_t, list); + urbp = acep->urbp; + urbp->context = acep->context; + urbp->complete = acep->complete; + list_add_tail (&acep->list, &acp->free_list); + spin_unlock_irqrestore (&acp->lock, flags); + dbg ("unlink waiting urb"); + urbp->status = -ENOENT; + urbp->complete (urbp, NULL); + spin_lock_irqsave (&acp->lock, flags); + } + spin_unlock_irqrestore (&acp->lock, flags); + + /* clear the active urb */ + acep = acp->active; + if (acep) { + urbp = acep->urbp; + urbp->transfer_flags &= ~URB_ASYNC_UNLINK; + dbg ("unlink active urb"); + usb_kill_urb (urbp); + } +} + + +/* free the chain. + this function must not be called from interrupt or completion handler. +*/ +static void auerchain_free (pauerchain_t acp) +{ + unsigned long flags; + pauerchainelement_t acep; + + dbg ("auerchain_free called"); + + /* first, cancel all pending urbs */ + auerchain_unlink_all (acp); + + /* free the elements */ + spin_lock_irqsave (&acp->lock, flags); + while (!list_empty (&acp->free_list)) { + /* get the next entry */ + struct list_head *tmp = acp->free_list.next; + list_del (tmp); + spin_unlock_irqrestore (&acp->lock, flags); + acep = list_entry (tmp, auerchainelement_t, list); + kfree (acep); + spin_lock_irqsave (&acp->lock, flags); + } + spin_unlock_irqrestore (&acp->lock, flags); +} + + +/* Init the chain control structure */ +static void auerchain_init (pauerchain_t acp) +{ + /* init the chain data structure */ + acp->active = NULL; + spin_lock_init (&acp->lock); + INIT_LIST_HEAD (&acp->waiting_list); + INIT_LIST_HEAD (&acp->free_list); +} + +/* setup a chain. + It is assumed that there is no concurrency while setting up the chain + requirement: auerchain_init() +*/ +static int auerchain_setup (pauerchain_t acp, unsigned int numElements) +{ + pauerchainelement_t acep; + + dbg ("auerchain_setup called with %d elements", numElements); + + /* fill the list of free elements */ + for (;numElements; numElements--) { + acep = (pauerchainelement_t) kmalloc (sizeof (auerchainelement_t), GFP_KERNEL); + if (!acep) + goto ac_fail; + memset (acep, 0, sizeof (auerchainelement_t)); + INIT_LIST_HEAD (&acep->list); + list_add_tail (&acep->list, &acp->free_list); + } + return 0; + +ac_fail:/* free the elements */ + while (!list_empty (&acp->free_list)) { + /* get the next entry */ + struct list_head *tmp = acp->free_list.next; + list_del (tmp); + acep = list_entry (tmp, auerchainelement_t, list); + kfree (acep); + } + return -ENOMEM; +} + + +/* completion handler for synchronous chained URBs */ +static void auerchain_blocking_completion (struct urb *urb, struct pt_regs *regs) +{ + pauerchain_chs_t pchs = (pauerchain_chs_t)urb->context; + pchs->done = 1; + wmb(); + wake_up (&pchs->wqh); +} + + +/* Starts chained urb and waits for completion or timeout */ +static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length) +{ + auerchain_chs_t chs; + int status; + + dbg ("auerchain_start_wait_urb called"); + init_waitqueue_head (&chs.wqh); + chs.done = 0; + + urb |