📄 auerswald.c
字号:
/*****************************************************************************//* * 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>#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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -