aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/misc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /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/Kconfig152
-rw-r--r--drivers/usb/misc/Makefile20
-rw-r--r--drivers/usb/misc/auerswald.c2163
-rw-r--r--drivers/usb/misc/cytherm.c430
-rw-r--r--drivers/usb/misc/emi26.c255
-rw-r--r--drivers/usb/misc/emi26_fw.h5779
-rw-r--r--drivers/usb/misc/emi62.c298
-rw-r--r--drivers/usb/misc/emi62_fw_m.h8853
-rw-r--r--drivers/usb/misc/emi62_fw_s.h8837
-rw-r--r--drivers/usb/misc/idmouse.c443
-rw-r--r--drivers/usb/misc/legousbtower.c1088
-rw-r--r--drivers/usb/misc/phidgetkit.c586
-rw-r--r--drivers/usb/misc/phidgetservo.c342
-rw-r--r--drivers/usb/misc/rio500.c563
-rw-r--r--drivers/usb/misc/rio500_usb.h37
-rw-r--r--drivers/usb/misc/sisusbvga/Kconfig14
-rw-r--r--drivers/usb/misc/sisusbvga/Makefile6
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c3145
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.h278
-rw-r--r--drivers/usb/misc/usblcd.c404
-rw-r--r--drivers/usb/misc/usbled.c181
-rw-r--r--drivers/usb/misc/usbtest.c2140
-rw-r--r--drivers/usb/misc/uss720.c674
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