ChangeSet 1.992, 2003/02/20 11:10:54-08:00, wolfgang@iksw-muees.de [PATCH] USB: updated Auerswald driver diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help Thu Feb 20 12:07:15 2003 +++ b/Documentation/Configure.help Thu Feb 20 12:07:16 2003 @@ -14723,7 +14723,7 @@ The module will be called rio500.o. If you want to compile it as a module, say M here and read . -USB Auerswald ISDN device support +Auerswald device support CONFIG_USB_AUERSWALD Say Y here if you want to connect an Auerswald USB ISDN Device to your computer's USB port. diff -Nru a/Documentation/usb/auerswald.txt b/Documentation/usb/auerswald.txt --- a/Documentation/usb/auerswald.txt Thu Feb 20 12:07:15 2003 +++ b/Documentation/usb/auerswald.txt Thu Feb 20 12:07:15 2003 @@ -23,8 +23,15 @@ ... mknod -m 666 /dev/usb/auer15 c 180 127 -Future plans +ISDN support ============ -- Connection to ISDN4LINUX (the hisax interface) +If you enable CONFIG_USB_AUERISDN, you will get full +ISDN modem support via the HISAX ISDN4LINUX driver. + +Of course, as for every USB-based ISDN adaptor, you +will have to modify your hotplug scripts to load +the isdn subsystem and configure the network interfaces. +Same procedure as for the ST5481 driver. + The maintainer of this driver is wolfgang@iksw-muees.de diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Thu Feb 20 12:07:15 2003 +++ b/drivers/usb/Config.in Thu Feb 20 12:07:15 2003 @@ -110,7 +110,7 @@ comment 'USB Miscellaneous drivers' dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL - dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' Auerswald device support' CONFIG_USB_AUERSWALD $CONFIG_USB dep_tristate ' Texas Instruments Graph Link USB (aka SilverLink) cable support' CONFIG_USB_TIGL $CONFIG_USB dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB LCD device support' CONFIG_USB_LCD $CONFIG_USB diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Thu Feb 20 12:07:15 2003 +++ b/drivers/usb/Makefile Thu Feb 20 12:07:15 2003 @@ -18,7 +18,7 @@ usbcore-objs := usb.o usb-debug.o hub.o hid-objs := hid-core.o pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o - +auerswald-objs := auerbuf.o auerchain.o auerchar.o auermain.o # Optional parts of multipart objects. @@ -34,6 +34,12 @@ hid-objs += hid-input.o endif +ifdef CONFIG_USB_AUERISDN +ifneq ($(CONFIG_USB_AUERISDN),n) + auerswald-objs += auerisdn.o auerisdn_b.o +endif +endif + # Object file lists. obj-y := @@ -125,3 +131,6 @@ pwc.o: $(pwc-objs) $(LD) -r -o $@ $(pwc-objs) + +auerswald.o: $(auerswald-objs) + $(LD) -r -o $@ $(auerswald-objs) diff -Nru a/drivers/usb/auerbuf.c b/drivers/usb/auerbuf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerbuf.c Thu Feb 20 12:07:15 2003 @@ -0,0 +1,148 @@ +/*****************************************************************************/ +/* + * auerbuf.c -- Auerswald PBX/System Telephone urb list storage. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#undef DEBUG /* include debug macros until it's done */ +#include +#include "auerbuf.h" +#include + +/* free a single auerbuf */ +void auerbuf_free(struct auerbuf *bp) +{ + kfree(bp->bufp); + kfree(bp->dr); + if (bp->urbp) { + usb_free_urb(bp->urbp); + } + kfree(bp); +} + +/* free the buffers from an auerbuf list */ +void auerbuf_free_list(struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + struct auerbuf *bp; + + dbg("auerbuf_free_list"); + for (p = q->next; p != q;) { + bp = list_entry(p, struct auerbuf, buff_list); + tmp = p->next; + list_del(p); + p = tmp; + auerbuf_free(bp); + } +} + +/* free all buffers from an auerbuf chain */ +void auerbuf_free_buffers(struct auerbufctl *bcp) +{ + unsigned long flags; + dbg("auerbuf_free_buffers"); + + spin_lock_irqsave(&bcp->lock, flags); + + auerbuf_free_list(&bcp->free_buff_list); + auerbuf_free_list(&bcp->rec_buff_list); + + spin_unlock_irqrestore(&bcp->lock, flags); +} + +/* init the members of a list control block */ +void auerbuf_init(struct auerbufctl *bcp) +{ + dbg("auerbuf_init"); + spin_lock_init(&bcp->lock); + INIT_LIST_HEAD(&bcp->free_buff_list); + INIT_LIST_HEAD(&bcp->rec_buff_list); +} + +/* setup a list of buffers */ +/* requirement: auerbuf_init() */ +int auerbuf_setup(struct auerbufctl *bcp, unsigned int numElements, + unsigned int bufsize) +{ + struct auerbuf *bep; + + dbg("auerbuf_setup called with %d elements of %d bytes", + numElements, bufsize); + + /* fill the list of free elements */ + for (; numElements; numElements--) { + bep = + (struct auerbuf *) kmalloc(sizeof(struct auerbuf), + GFP_KERNEL); + if (!bep) + goto bl_fail; + memset(bep, 0, sizeof(struct auerbuf)); + bep->list = bcp; + INIT_LIST_HEAD(&bep->buff_list); + bep->bufp = (char *) kmalloc(bufsize, GFP_KERNEL); + if (!bep->bufp) + goto bl_fail; + bep->dr = + (struct usb_ctrlrequest *) + kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!bep->dr) + goto bl_fail; + bep->urbp = usb_alloc_urb(0); + if (!bep->urbp) + goto bl_fail; + list_add_tail(&bep->buff_list, &bcp->free_buff_list); + } + return 0; + + bl_fail: /* not enought memory. Free allocated elements */ + dbg("auerbuf_setup: no more memory"); + auerbuf_free_buffers(bcp); + return -ENOMEM; +} + +/* alloc a free buffer from the list. Returns NULL if no buffer available */ +struct auerbuf *auerbuf_getbuf(struct auerbufctl *bcp) +{ + unsigned long flags; + struct auerbuf *bp = NULL; + + spin_lock_irqsave(&bcp->lock, flags); + if (!list_empty(&bcp->free_buff_list)) { + /* yes: get the entry */ + struct list_head *tmp = bcp->free_buff_list.next; + list_del(tmp); + bp = list_entry(tmp, struct auerbuf, buff_list); + } + spin_unlock_irqrestore(&bcp->lock, flags); + return bp; +} + +/* insert a used buffer into the free list */ +void auerbuf_releasebuf(struct auerbuf *bp) +{ + unsigned long flags; + struct auerbufctl *bcp = bp->list; + bp->retries = 0; + + dbg("auerbuf_releasebuf called"); + spin_lock_irqsave(&bcp->lock, flags); + list_add_tail(&bp->buff_list, &bcp->free_buff_list); + spin_unlock_irqrestore(&bcp->lock, flags); +} diff -Nru a/drivers/usb/auerbuf.h b/drivers/usb/auerbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerbuf.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,69 @@ +/*****************************************************************************/ +/* + * auerbuf.h -- Auerswald PBX/System Telephone urb list storage. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +/* This module assembles together an URB, an usb_ctrlrequest struct for sending of + * control messages, and a data buffer. + * These items (auerbuf) are collected in a list (auerbufctl) and are used + * for serialized usb data transfer. + */ + +#ifndef AUERBUF_H +#define AUERBUF_H + +#include + +/* buffer element */ +struct auerbufctl; /* forward */ +struct auerbuf { + unsigned 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 */ +}; + +/* buffer list control block */ +struct auerbufctl { + spinlock_t lock; /* protection in interrupt */ + struct list_head free_buff_list;/* free buffers */ + struct list_head rec_buff_list; /* buffers with received data */ +}; + +/* Function prototypes */ +void auerbuf_free(struct auerbuf *bp); + +void auerbuf_free_list(struct list_head *q); + +void auerbuf_init(struct auerbufctl *bcp); + +void auerbuf_free_buffers(struct auerbufctl *bcp); + +int auerbuf_setup(struct auerbufctl *bcp, unsigned int numElements, + unsigned int bufsize); + +struct auerbuf *auerbuf_getbuf(struct auerbufctl *bcp); + +void auerbuf_releasebuf(struct auerbuf *bp); + +#endif /* AUERBUF_H */ diff -Nru a/drivers/usb/auerchain.c b/drivers/usb/auerchain.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerchain.c Thu Feb 20 12:07:16 2003 @@ -0,0 +1,468 @@ +/*****************************************************************************/ +/* + * auerchain.c -- Auerswald PBX/System Telephone chained urb support. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#undef DEBUG /* include debug macros until it's done */ +#include +#include "auerchain.h" +#include + +/* completion function for chained urbs */ +static void auerchain_complete(struct urb *urb) +{ + unsigned long flags; + int result; + + /* get pointer to element and to chain */ + struct auerchainelement *acep = + (struct auerchainelement *) urb->context; + struct auerchain *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); + + /* 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, struct auerchainelement, 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); + + /* 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); + } + } 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 +*/ +int auerchain_submit_urb_list(struct auerchain *acp, struct urb *urb, + int early) +{ + int result; + unsigned long flags; + struct auerchainelement *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, struct auerchainelement, 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); + /* 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); + } + } + + return 0; +} + +/* submit function for chained urbs + this function may be called from completion context or from user space! +*/ +int auerchain_submit_urb(struct auerchain *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 + USB_ASYNC_UNLINK is set and the function is successfully started. +*/ +int auerchain_unlink_urb(struct auerchain *acp, struct urb *urb) +{ + unsigned long flags; + struct urb *urbp; + struct auerchainelement *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, struct auerchainelement, 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); + 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. +*/ +void auerchain_unlink_all(struct auerchain *acp) +{ + unsigned long flags; + struct urb *urbp; + struct auerchainelement *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, struct auerchainelement, 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); + 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 &= ~USB_ASYNC_UNLINK; + dbg("unlink active urb"); + usb_unlink_urb(urbp); + } +} + + +/* free the chain. + this function must not be called from interrupt or completion handler. +*/ +void auerchain_free(struct auerchain *acp) +{ + unsigned long flags; + struct auerchainelement *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, struct auerchainelement, list); + kfree(acep); + spin_lock_irqsave(&acp->lock, flags); + } + spin_unlock_irqrestore(&acp->lock, flags); +} + + +/* Init the chain control structure */ +void auerchain_init(struct auerchain *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() +*/ +int auerchain_setup(struct auerchain *acp, unsigned int numElements) +{ + struct auerchainelement *acep; + + dbg("auerchain_setup called with %d elements", numElements); + + /* fill the list of free elements */ + for (; numElements; numElements--) { + acep = + (struct auerchainelement *) + kmalloc(sizeof(struct auerchainelement), GFP_KERNEL); + if (!acep) + goto ac_fail; + memset(acep, 0, sizeof(struct auerchainelement)); + 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, struct auerchainelement, list); + kfree(acep); + } + return -ENOMEM; +} + + +/* completion handler for synchronous chained URBs */ +static void auerchain_blocking_completion(struct urb *urb) +{ + struct auerchain_chs *pchs = (struct auerchain_chs *) 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(struct auerchain *acp, struct urb *urb, + int timeout, int *actual_length) +{ + DECLARE_WAITQUEUE(wait, current); + struct auerchain_chs chs; + int status; + + dbg("auerchain_start_wait_urb called"); + init_waitqueue_head(&chs.wqh); + chs.done = 0; + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chs.wqh, &wait); + urb->context = &chs; + status = auerchain_submit_urb(acp, urb); + if (status) { + /* something went wrong */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&chs.wqh, &wait); + return status; + } + + while (timeout && !chs.done) { + timeout = schedule_timeout(timeout); + set_current_state(TASK_UNINTERRUPTIBLE); + rmb(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&chs.wqh, &wait); + + if (!timeout && !chs.done) { + if (urb->status != -EINPROGRESS) { /* No callback?!! */ + dbg("auerchain_start_wait_urb: raced timeout"); + status = urb->status; + } else { + dbg("auerchain_start_wait_urb: timeout"); + auerchain_unlink_urb(acp, urb); /* remove urb safely */ + status = -ETIMEDOUT; + } + } else + status = urb->status; + + if (actual_length) + *actual_length = urb->actual_length; + + return status; +} + + +/* auerchain_control_msg - Builds a control urb, sends it off and waits for completion + acp: pointer to the auerchain + 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 the transfered length, 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 auerchain_submit_urb() +*/ +int auerchain_control_msg(struct auerchain *acp, struct usb_device *dev, + unsigned int pipe, __u8 request, + __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout) +{ + int ret; + struct usb_ctrlrequest *dr; + struct urb *urb; + int length; + + dbg("auerchain_control_msg"); + dr = (struct usb_ctrlrequest *) + kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!dr) + return -ENOMEM; + urb = usb_alloc_urb(0); + if (!urb) { + kfree(dr); + return -ENOMEM; + } + + dr->bRequestType = requesttype; + dr->bRequest = request; + dr->wValue = cpu_to_le16(value); + dr->wIndex = cpu_to_le16(index); + dr->wLength = cpu_to_le16(size); + + FILL_CONTROL_URB(urb, dev, pipe, (unsigned char *) dr, data, size, /* build urb */ + (usb_complete_t) auerchain_blocking_completion, + 0); + ret = auerchain_start_wait_urb(acp, urb, timeout, &length); + + usb_free_urb(urb); + kfree(dr); + + if (ret < 0) + return ret; + else + return length; +} diff -Nru a/drivers/usb/auerchain.h b/drivers/usb/auerchain.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerchain.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,79 @@ +/*****************************************************************************/ +/* + * auerchain.h -- Auerswald PBX/System Telephone chained urb support. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +/* This module is used to make a FIFO of URBs, to serialize the submit. + * This may be used to serialize control messages, which is not supported + * by the Linux USB subsystem. + */ + +#ifndef AUERCHAIN_H +#define AUERCHAIN_H + +#include + +/* urb chain element */ +struct auerchain; /* forward for circular reference */ +struct auerchainelement { + 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 */ +}; + +/* urb chain */ +struct auerchain { + struct auerchainelement *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 */ +}; + +/* urb blocking completion helper struct */ +struct auerchain_chs { + wait_queue_head_t wqh; /* wait for completion */ + unsigned int done; /* completion flag */ +}; + + +/* Function prototypes */ +int auerchain_submit_urb_list(struct auerchain *acp, struct urb *urb, + int early); + +int auerchain_submit_urb(struct auerchain *acp, struct urb *urb); + +int auerchain_unlink_urb(struct auerchain *acp, struct urb *urb); + +void auerchain_unlink_all(struct auerchain *acp); + +void auerchain_free(struct auerchain *acp); + +void auerchain_init(struct auerchain *acp); + +int auerchain_setup(struct auerchain *acp, unsigned int numElements); + +int auerchain_control_msg(struct auerchain *acp, struct usb_device *dev, + unsigned int pipe, __u8 request, + __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout); + +#endif /* AUERCHAIN_H */ diff -Nru a/drivers/usb/auerchar.c b/drivers/usb/auerchar.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerchar.c Thu Feb 20 12:07:16 2003 @@ -0,0 +1,615 @@ +/*****************************************************************************/ +/* + * auerchar.c -- Auerswald PBX/System Telephone character interface. + * + * Copyright (C) 2002 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. + */ + /*****************************************************************************/ + +#undef DEBUG /* include debug macros until it's done */ +#include +#include "auerchar.h" +#include "auermain.h" +#include +#include /* user area access functions */ + +/*-------------------------------------------------------------------*/ + +/* wake up waiting readers */ +static void auerchar_disconnect(struct auerscon *scp) +{ + struct auerchar *ccp =((struct auerchar *) ((char *) (scp) - (unsigned long) (&((struct auerchar *) 0)->scontext))); + dbg("auerchar_disconnect called"); + ccp->removed = 1; + wake_up(&ccp->readwait); +} + + +/* dispatch a read paket to a waiting character device */ +static void auerchar_ctrlread_dispatch(struct auerscon *scp, + struct auerbuf *bp) +{ + unsigned long flags; + struct auerchar *ccp; + struct auerbuf *newbp = NULL; + char *charp; + dbg("auerchar_ctrlread_dispatch called"); + ccp =((struct auerchar *) ((char *) (scp) - (unsigned long)(&((struct auerchar *) 0)->scontext))); + + /* get a read buffer from character device context */ + newbp = auerbuf_getbuf(&ccp->bufctl); + if (!newbp) { + dbg("No read buffer available, discard paket!"); + return; /* no buffer, no dispatch */ + } + + /* copy information to new buffer element + (all buffers have the same length) */ + charp = newbp->bufp; + newbp->bufp = bp->bufp; + bp->bufp = charp; + newbp->len = bp->len; + + /* insert new buffer in read list */ + spin_lock_irqsave(&ccp->bufctl.lock, flags); + list_add_tail(&newbp->buff_list, &ccp->bufctl.rec_buff_list); + spin_unlock_irqrestore(&ccp->bufctl.lock, flags); + dbg("read buffer appended to rec_list"); + + /* wake up pending synchronous reads */ + wake_up(&ccp->readwait); +} + + +/* Delete an auerswald character context */ +void auerchar_delete(struct auerchar *ccp) +{ + dbg("auerchar_delete"); + if (ccp == NULL) + return; + + /* wake up pending synchronous reads */ + ccp->removed = 1; + wake_up(&ccp->readwait); + + /* remove the read buffer */ + if (ccp->readbuf) { + auerbuf_releasebuf(ccp->readbuf); + ccp->readbuf = NULL; + } + + /* remove the character buffers */ + auerbuf_free_buffers(&ccp->bufctl); + + /* release the memory */ + kfree(ccp); +} + + +/* --------------------------------------------------------------------- */ +/* Char device functions */ + +/* Open a new character device */ +int auerchar_open(struct inode *inode, struct file *file) +{ + int dtindex = MINOR(inode->i_rdev) - AUER_MINOR_BASE; + struct auerswald *cp = NULL; + struct auerchar *ccp = NULL; + int ret; + + /* minor number in range? */ + if ((dtindex < 0) || (dtindex >= AUER_MAX_DEVICES)) { + return -ENODEV; + } + /* usb device available? */ + if (down_interruptible(&auerdev_table_mutex)) { + return -ERESTARTSYS; + } + cp = auerdev_table[dtindex]; + if (cp == NULL) { + up(&auerdev_table_mutex); + return -ENODEV; + } + if (down_interruptible(&cp->mutex)) { + up(&auerdev_table_mutex); + return -ERESTARTSYS; + } + up(&auerdev_table_mutex); + + /* we have access to the device. Now lets allocate memory */ + ccp = (struct auerchar *) kmalloc(sizeof(struct auerchar), GFP_KERNEL); + if (ccp == NULL) { + err("out of memory"); + ret = -ENOMEM; + goto ofail; + } + + /* Initialize device descriptor */ + memset(ccp, 0, sizeof(struct auerchar)); + init_MUTEX(&ccp->mutex); + init_MUTEX(&ccp->readmutex); + auerbuf_init(&ccp->bufctl); + ccp->scontext.id = AUH_UNASSIGNED; + ccp->scontext.dispatch = auerchar_ctrlread_dispatch; + ccp->scontext.disconnect = auerchar_disconnect; + init_waitqueue_head(&ccp->readwait); + + ret = + auerbuf_setup(&ccp->bufctl, AU_RBUFFERS, + cp->maxControlLength + AUH_SIZE); + if (ret) { + goto ofail; + } + + cp->open_count++; + ccp->auerdev = cp; + dbg("open %s as /dev/usb/%s", cp->dev_desc, cp->name); + up(&cp->mutex); + + /* file IO stuff */ + file->f_pos = 0; + file->private_data = ccp; + return 0; + + /* Error exit */ + ofail:up(&cp->mutex); + auerchar_delete(ccp); + return ret; +} + + +/* IOCTL functions */ +int auerchar_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct auerchar *ccp = (struct auerchar *) file->private_data; + int ret = 0; + struct audevinfo devinfo; + struct auerswald *cp = NULL; + unsigned int u; + dbg("ioctl"); + + /* get the mutexes */ + if (down_interruptible(&ccp->mutex)) { + return -ERESTARTSYS; + } + cp = ccp->auerdev; + if (!cp) { + up(&ccp->mutex); + return -ENODEV; + } + if (down_interruptible(&cp->mutex)) { + up(&ccp->mutex); + return -ERESTARTSYS; + } + + /* Check for removal */ + if (!cp->usbdev) { + up(&cp->mutex); + up(&ccp->mutex); + return -ENODEV; + } + + switch (cmd) { + + /* return != 0 if Transmitt channel ready to send */ + case IOCTL_AU_TXREADY: + dbg("IOCTL_AU_TXREADY"); + u = ccp->auerdev && (ccp->scontext.id != AUH_UNASSIGNED) + && !list_empty(&cp->bufctl.free_buff_list); + ret = put_user(u, (unsigned int *) arg); + break; + + /* return != 0 if connected to a service channel */ + case IOCTL_AU_CONNECT: + dbg("IOCTL_AU_CONNECT"); + u = (ccp->scontext.id != AUH_UNASSIGNED); + ret = put_user(u, (unsigned int *) arg); + break; + + /* return != 0 if Receive Data available */ + case IOCTL_AU_RXAVAIL: + dbg("IOCTL_AU_RXAVAIL"); + if (ccp->scontext.id == AUH_UNASSIGNED) { + ret = -EIO; + break; + } + u = 0; /* no data */ + if (ccp->readbuf) { + int restlen = ccp->readbuf->len - ccp->readoffset; + if (restlen > 0) + u = 1; + } + if (!u) { + if (!list_empty(&ccp->bufctl.rec_buff_list)) { + u = 1; + } + } + ret = put_user(u, (unsigned int *) arg); + break; + + /* return the max. buffer length for the device */ + case IOCTL_AU_BUFLEN: + dbg("IOCTL_AU_BUFLEN"); + u = cp->maxControlLength; + ret = put_user(u, (unsigned int *) arg); + break; + + /* requesting a service channel */ + case IOCTL_AU_SERVREQ: + dbg("IOCTL_AU_SERVREQ"); + /* requesting a service means: release the previous one first */ + auerswald_removeservice(cp, &ccp->scontext); + /* get the channel number */ + ret = get_user(u, (unsigned int *) arg); + if (ret) { + break; + } + if ((u < AUH_FIRSTUSERCH) || (u >= AUH_TYPESIZE)) { + ret = -EIO; + break; + } + dbg("auerchar service request parameters are ok"); + ccp->scontext.id = u; + + /* request the service now */ + ret = auerswald_addservice(cp, &ccp->scontext); + if (ret) { + /* no: revert service entry */ + ccp->scontext.id = AUH_UNASSIGNED; + } + break; + + /* get a string descriptor for the device */ + case IOCTL_AU_DEVINFO: + dbg("IOCTL_AU_DEVINFO"); + if (copy_from_user + (&devinfo, (void *) arg, sizeof(struct audevinfo))) { + ret = -EFAULT; + break; + } + u = strlen(cp->dev_desc) + 1; + if (u > devinfo.bsize) { + u = devinfo.bsize; + } + ret = copy_to_user(devinfo.buf, cp->dev_desc, u); + break; + + /* get the max. string descriptor length */ + case IOCTL_AU_SLEN: + dbg("IOCTL_AU_SLEN"); + u = AUSI_DLEN; + ret = put_user(u, (unsigned int *) arg); + break; + + default: + dbg("IOCTL_AU_UNKNOWN"); + ret = -ENOIOCTLCMD; + break; + } + /* release the mutexes */ + up(&cp->mutex); + up(&ccp->mutex); + return ret; +} + + +/* Seek is not supported */ +loff_t auerchar_llseek(struct file * file, loff_t offset, int origin) +{ + dbg("auerchar_seek"); + return -ESPIPE; +} + + +/* Read data from the device */ +ssize_t auerchar_read(struct file * file, char *buf, size_t count, + loff_t * ppos) +{ + unsigned long flags; + struct auerchar *ccp = (struct auerchar *) file->private_data; + struct auerbuf *bp = NULL; + wait_queue_t wait; + + dbg("auerchar_read"); + + /* Error checking */ + if (!ccp) + return -EIO; + if (*ppos) + return -ESPIPE; + if (count == 0) + return 0; + + /* get the mutex */ + if (down_interruptible(&ccp->mutex)) + return -ERESTARTSYS; + + /* Can we expect to read something? */ + if (ccp->scontext.id == AUH_UNASSIGNED) { + up(&ccp->mutex); + return -EIO; + } + + /* only one reader per device allowed */ + if (down_interruptible(&ccp->readmutex)) { + up(&ccp->mutex); + return -ERESTARTSYS; + } + + /* read data from readbuf, if available */ + doreadbuf: + bp = ccp->readbuf; + if (bp) { + /* read the maximum bytes */ + int restlen = bp->len - ccp->readoffset; + if (restlen < 0) + restlen = 0; + if (count > restlen) + count = restlen; + if (count) { + if (copy_to_user + (buf, bp->bufp + ccp->readoffset, count)) { + dbg("auerswald_read: copy_to_user failed"); + up(&ccp->readmutex); + up(&ccp->mutex); + return -EFAULT; + } + } + /* advance the read offset */ + ccp->readoffset += count; + restlen -= count; + // reuse the read buffer + if (restlen <= 0) { + auerbuf_releasebuf(bp); + ccp->readbuf = NULL; + } + /* return with number of bytes read */ + if (count) { + up(&ccp->readmutex); + up(&ccp->mutex); + return count; + } + } + + /* a read buffer is not available. Try to get the next data block. */ + doreadlist: + /* Preparing for sleep */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&ccp->readwait, &wait); + + bp = NULL; + spin_lock_irqsave(&ccp->bufctl.lock, flags); + if (!list_empty(&ccp->bufctl.rec_buff_list)) { + /* yes: get the entry */ + struct list_head *tmp = ccp->bufctl.rec_buff_list.next; + list_del(tmp); + bp = list_entry(tmp, struct auerbuf, buff_list); + } + spin_unlock_irqrestore(&ccp->bufctl.lock, flags); + + /* have we got data? */ + if (bp) { + ccp->readbuf = bp; + ccp->readoffset = AUH_SIZE; /* for headerbyte */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&ccp->readwait, &wait); + goto doreadbuf; /* now we can read! */ + } + + /* no data available. Should we wait? */ + if (file->f_flags & O_NONBLOCK) { + dbg("No read buffer available, returning -EAGAIN"); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ccp->readwait, &wait); + up(&ccp->readmutex); + up(&ccp->mutex); + return -EAGAIN; /* nonblocking, no data available */ + } + + /* yes, we should wait! */ + up(&ccp->mutex); /* allow other operations while we wait */ + schedule(); + remove_wait_queue(&ccp->readwait, &wait); + if (signal_pending(current)) { + /* waked up by a signal */ + up(&ccp->readmutex); + return -ERESTARTSYS; + } + + /* Anything left to read? */ + if ((ccp->scontext.id == AUH_UNASSIGNED) || ccp->removed) { + up(&ccp->readmutex); + return -EIO; + } + + if (down_interruptible(&ccp->mutex)) { + up(&ccp->readmutex); + return -ERESTARTSYS; + } + + /* try to read the incomming data again */ + goto doreadlist; +} + + +/* Write a data block into the right service channel of the device */ +ssize_t auerchar_write(struct file *file, const char *buf, size_t len, + loff_t * ppos) +{ + struct auerchar *ccp = (struct auerchar *) file->private_data; + struct auerswald *cp = NULL; + struct auerbuf *bp; + int ret; + wait_queue_t wait; + + dbg("auerchar_write %d bytes", len); + + /* Error checking */ + if (!ccp) + return -EIO; + if (*ppos) + return -ESPIPE; + if (len == 0) + return 0; + + write_again: + /* get the mutex */ + if (down_interruptible(&ccp->mutex)) + return -ERESTARTSYS; + + /* Can we expect to write something? */ + if (ccp->scontext.id == AUH_UNASSIGNED) { + up(&ccp->mutex); + return -EIO; + } + + cp = ccp->auerdev; + if (!cp) { + up(&ccp->mutex); + return -ERESTARTSYS; + } + if (down_interruptible(&cp->mutex)) { + up(&ccp->mutex); + return -ERESTARTSYS; + } + if (!cp->usbdev) { + up(&cp->mutex); + up(&ccp->mutex); + return -EIO; + } + /* Prepare for sleep */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&cp->bufferwait, &wait); + + /* Try to get a buffer from the device pool. + We can't use a buffer from ccp->bufctl because the write + command will last beond a release() */ + bp = auerbuf_getbuf(&cp->bufctl); + /* are there any buffers left? */ + if (!bp) { + up(&cp->mutex); + up(&ccp->mutex); + + /* NONBLOCK: don't wait */ + if (file->f_flags & O_NONBLOCK) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&cp->bufferwait, &wait); + return -EAGAIN; + } + + /* BLOCKING: wait */ + schedule(); + remove_wait_queue(&cp->bufferwait, &wait); + if (signal_pending(current)) { + /* waked up by a signal */ + return -ERESTARTSYS; + } + goto write_again; + } else { + set_current_state(TASK_RUNNING); + remove_wait_queue(&cp->bufferwait, &wait); + } + + /* protect against too big write requests */ + if (len > cp->maxControlLength) + len = cp->maxControlLength; + + /* Fill the buffer */ + if (copy_from_user(bp->bufp + AUH_SIZE, buf, len)) { + dbg("copy_from_user failed"); + auerbuf_releasebuf(bp); + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); + up(&cp->mutex); + up(&ccp->mutex); + return -EIO; + } + + /* set the header byte */ + *(bp->bufp) = ccp->scontext.id | AUH_DIRECT | AUH_UNSPLIT; + + /* Set the transfer Parameters */ + bp->len = len + AUH_SIZE; + bp->dr->bRequestType = AUT_WREQ; + bp->dr->bRequest = AUV_WBLOCK; + bp->dr->wValue = cpu_to_le16(0); + bp->dr->wIndex = + cpu_to_le16(ccp->scontext.id | AUH_DIRECT | AUH_UNSPLIT); + bp->dr->wLength = cpu_to_le16(len + AUH_SIZE); + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_sndctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, bp->bufp, + len + AUH_SIZE, auerchar_ctrlwrite_complete, bp); + /* up we go */ + ret = auerchain_submit_urb(&cp->controlchain, bp->urbp); + up(&cp->mutex); + if (ret) { + dbg("auerchar_write: nonzero result of auerchain_submit_urb %d", ret); + auerbuf_releasebuf(bp); + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); + up(&ccp->mutex); + return -EIO; + } else { + dbg("auerchar_write: Write OK"); + up(&ccp->mutex); + return len; + } +} + + +/* Close a character device */ +int auerchar_release(struct inode *inode, struct file *file) +{ + struct auerchar *ccp = (struct auerchar *) file->private_data; + struct auerswald *cp; + dbg("release"); + + /* get the mutexes */ + if (down_interruptible(&ccp->mutex)) { + return -ERESTARTSYS; + } + cp = ccp->auerdev; + if (cp) { + if (down_interruptible(&cp->mutex)) { + up(&ccp->mutex); + return -ERESTARTSYS; + } + /* remove an open service */ + auerswald_removeservice(cp, &ccp->scontext); + /* detach from device */ + if ((--cp->open_count <= 0) && (cp->usbdev == NULL)) { + /* usb device waits for removal */ + up(&cp->mutex); + auerswald_delete(cp); + } else { + up(&cp->mutex); + } + cp = NULL; + ccp->auerdev = NULL; + } + up(&ccp->mutex); + auerchar_delete(ccp); + + return 0; +} diff -Nru a/drivers/usb/auerchar.h b/drivers/usb/auerchar.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerchar.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,79 @@ +/*****************************************************************************/ +/* + * auerchar.h -- Auerswald PBX/System Telephone character interface. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#ifndef AUERCHAR_H +#define AUERCHAR_H + +#include "auerchain.h" +#include "auerbuf.h" +#include "auerserv.h" + +/* External data structures / Interface */ +struct audevinfo { + char *buf; /* return buffer for string contents */ + unsigned int bsize; /* size of return buffer */ +}; + +/* IO controls */ +#define IOCTL_AU_SLEN _IOR( 'U', 0xF0, int) /* return the max. string descriptor length */ +#define IOCTL_AU_DEVINFO _IOWR('U', 0xF1, struct audevinfo) /* 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 reserved */ + +/* character device context */ +struct auerswald; +struct auerchar { + struct semaphore mutex; /* protection in user context */ + struct auerswald *auerdev; /* context pointer of assigned device */ + struct auerbufctl bufctl; /* controls the buffer chain */ + struct auerscon scontext; /* service context */ + wait_queue_head_t readwait; /* for synchronous reading */ + struct semaphore readmutex; /* protection against multiple reads */ + struct auerbuf *readbuf; /* buffer held for partial reading */ + unsigned int readoffset; /* current offset in readbuf */ + unsigned int removed; /* is != 0 if device is removed */ +}; + +/* Function prototypes */ +void auerchar_delete(struct auerchar *ccp); + +int auerchar_open(struct inode *inode, struct file *file); + +int auerchar_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +loff_t auerchar_llseek(struct file *file, loff_t offset, int origin); + +ssize_t auerchar_read(struct file *file, char *buf, size_t count, + loff_t * ppos); + +ssize_t auerchar_write(struct file *file, const char *buf, size_t len, + loff_t * ppos); + +int auerchar_release(struct inode *inode, struct file *file); + + +#endif /* AUERCHAR_H */ diff -Nru a/drivers/usb/auerisdn.c b/drivers/usb/auerisdn.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerisdn.c Thu Feb 20 12:07:16 2003 @@ -0,0 +1,1076 @@ +/*****************************************************************************/ +/* + * auerisdn.c -- Auerswald PBX/System Telephone ISDN interface. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#include +#include +#include + +#undef DEBUG /* include debug macros until it's done */ +#include + +#include "auerisdn.h" +#include "auermain.h" + +/*-------------------------------------------------------------------*/ +/* ISDN support defines */ +#define AUISDN_TEI 64 /* use a constant TEI */ + +/*-------------------------------------------------------------------*/ +/* Debug support */ +#ifdef DEBUG +#define dump( desc, adr, len) \ +do { \ + unsigned int u; \ + printk (KERN_DEBUG); \ + printk (desc); \ + for (u = 0; u < len; u++) \ + printk (" %02X", adr[u] & 0xFF); \ + printk ("\n"); \ +} while (0) +#else +#define dump( desc, adr, len) +#endif + +/*-------------------------------------------------------------------*/ +/* Hisax Interface. */ + +/* The interface to hisax is long-lasting because hisax_unregister() + don't work well in Linux 2.4.x. So we have to hold each registered + hisax interface until driver removal. */ +static struct auerhisax auerhisax_table[AUER_MAX_DEVICES]; + + +/*-------------------------------------------------------------------*/ + +/* Callback to L2 for HISAX */ +/* This callback can be called from 3 sources: + a) from hisax context (answer from a l2l1 function) + b) from interrupt context (a D channel paket arrived) + c) from kernel daemon context (probe/disconnecting) +*/ +static void auerisdn_d_l1l2(struct auerisdn *ip, int pr, void *arg) +{ + struct sk_buff *skb; + struct auerhisax *ahp; + + /* do the callback */ + ahp = ip->ahp; + if (ahp) { + ahp->hisax_d_if.ifc.l1l2(&ahp->hisax_d_if.ifc, pr, arg); + } else { + dbg("auerisdn_d_l1l2 with ahp == NULL"); + if (pr == (PH_DATA | INDICATION)) { + skb = (struct sk_buff *) arg; + if (skb) { + skb_pull(skb, skb->len); + dev_kfree_skb_any(skb); + } + } + } +} + + +/* D-Channel sending completion function */ +static void auerisdn_dcw_complete(struct urb *urb) +{ + struct auerbuf *bp = (struct auerbuf *) urb->context; + struct auerswald *cp = + ((struct auerswald *) ((char *) (bp->list) - + (unsigned + long) (&((struct auerswald *) 0)-> + bufctl))); + + dbg("auerisdn_dcw_complete with status %d", urb->status); + + /* reuse the buffer */ + auerbuf_releasebuf(bp); + + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); +} + + +/* Translate non-ETSI ISDN messages from the device */ +static void auerisdn_translate_incoming(struct auerswald *cp, + unsigned char *msg, + unsigned int len) +{ + struct auerbuf *bp; + int ret; + + /* Translate incomming CONNECT -> CONNECT_ACK */ + /* Format: 0 1 2 3 4 5 6 7 */ + /* SAPI TEI TXSEQ RXSEQ PID=08 CREFLEN=01 CREF MSG=7 ...*/ + /* CREF.7 == 0 -> Incoming Call */ + + /* Check for minimum length */ + if (len < 8) + return; + + /* Check for a CONNECT, call originated from device */ + if (((msg[6] & 0x80) == 0) && (msg[7] == 0x07)) { + dbg("false CONNECT from device found"); + /* change into CONNECT_ACK */ + msg[7] = 0x0F; + + /* Send a CONNECT_ACK back to the device */ + + /* get a new data buffer */ + bp = auerbuf_getbuf(&cp->bufctl); + if (!bp) { + warn("no auerbuf free"); + return; + } + + /* Form a CONNECT ACK */ + bp->bufp[0] = + cp->isdn.dchannelservice.id | AUH_DIRECT | AUH_UNSPLIT; + bp->bufp[1] = 0x08; + bp->bufp[2] = 0x01; + bp->bufp[3] = msg[6] | 0x80; + bp->bufp[4] = 0x0F; + + /* Set the transfer Parameters */ + bp->len = 5; + bp->dr->bRequestType = AUT_WREQ; + bp->dr->bRequest = AUV_WBLOCK; + bp->dr->wValue = cpu_to_le16(0); + bp->dr->wIndex = + cpu_to_le16(cp->isdn.dchannelservice. + id | AUH_DIRECT | AUH_UNSPLIT); + bp->dr->wLength = cpu_to_le16(5); + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_sndctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, bp->bufp, 5, + auerisdn_dcw_complete, bp); + /* up we go */ + ret = auerchain_submit_urb(&cp->controlchain, bp->urbp); + if (ret) + auerisdn_dcw_complete(bp->urbp); + else + dbg("auerisdn_translate: Write OK"); + } + /* Check for a DISCONNECT and change to RELEASE */ + if (msg[7] == 0x45) { + dbg("DISCONNECT changed to RELEASE"); + msg[7] = 0x4D; + return; + } +} + + +/* a D-channel paket arrived from the device */ +static void auerisdn_dispatch_dc(struct auerscon *scp, struct auerbuf *bp) +{ + struct sk_buff *skb; + struct auerhisax *ahp; + struct auerswald *cp = + ((struct auerswald *) ((char *) (scp) - + (unsigned + long) (&((struct auerswald *) 0)->isdn. + dchannelservice))); + unsigned char *sp; + unsigned int l2_index; + unsigned char c; + unsigned char l2_header[10]; + unsigned long flags; + + dump("D-Channel paket arrived:", bp->bufp, bp->len); + if (cp->disconnecting) + return; + + /* add a self-generated L2 message header */ + l2_index = 0; + l2_header[l2_index++] = 0x02; /* SAPI 0, C/R = 1 */ + + /* Parse the L3 message */ + sp = bp->bufp + AUH_SIZE; + + c = *sp++; /* Protocol discriminator */ + if (c != 0x08) { + warn("D channel paket is not ETSI"); + return; + } + c = *sp++; /* Call Reference length byte */ + sp += c; /* Skip Call Reference */ + + /* translate charge IEs */ + /* Format of Auerswald Header: + 0x32 len=0x0B 0xFF 0xFF 0x73 len=0x07 0x27 */ + /* Format of IE2_UNIT: + 0x49 len=0x04 uu1 uu2 uu3 uu4 */ + /* Translate into: (?? Bytes) + 0x1C Facility + 0x?? restlen + 0x91 Sup. Services + 0xA1 Invoke + 0x?? restlen + 0x02 Invoke ID Tag + 0x02 Invoke ID len + 0x12 Invoke ID = 0x1234 + 0x34 + 0x02 OP Value Tag + 0x01 Length of OPvalue + 0x24 OpValue = AOCE + 0x30 Universal Constructor Sequence + 0x?? restlen + 0x30 Universal Constructor Sequence + 0x?? restlen + 0xA1 Context Specific Constructor Recorded Units List + 0x?? restlen + 0x30 Universal Constructor Sequence + 0x?? restlen + 0x02 Universal Primitive Integer + 0x?? len from IE2_UNIT + uu1 Recorded Units List + uu2 + uu3 + uu4 + */ + { + unsigned char *ucp = sp; // pointer to start of msg + int l = bp->len; // length until EOP + unsigned char alen; // length of auerswald msg + l -= (int) (ucp - bp->bufp); + // scan for Auerswald Header + for (; l >= 9; l--, ucp++) { // 9 = minimal length of auerswald msg + if (ucp[0] != 0x32) + continue; + if (ucp[2] != 0xFF) + continue; + if (ucp[3] != 0xFF) + continue; + if (ucp[4] != 0x73) + continue; + if (ucp[6] != 0x27) + continue; + // Auerswald Header found. Is it units? + dbg("Auerswald msg header found"); + alen = ucp[1] + 2; + if (ucp[7] == 0x49) { + // yes + unsigned char ul = ucp[8] + 1; // length of charge integer + unsigned char charge[32]; + // Copy charge info into new buffer + unsigned char *xp = &ucp[8]; + int count; + for (count = 0; count < ul; count++) + charge[count] = *xp++; + // Erase auerswald msg + count = l - alen; + xp = ucp; + for (; count; count--, xp++) + xp[0] = xp[alen]; + l -= alen; + bp->len -= alen; + // make room for new message + count = l; + xp = &ucp[l - 1]; + for (; count; count--, xp--); + xp[21 + ul] = xp[0]; + l += (21 + ul); + bp->len += (21 + ul); + // insert IE header + ucp[0] = 0x1C; + ucp[1] = 19 + ul; + ucp[2] = 0x91; + ucp[3] = 0xA1; + ucp[4] = 16 + ul; + ucp[5] = 0x02; + ucp[6] = 0x02; + ucp[7] = 0x12; + ucp[8] = 0x34; + ucp[9] = 0x02; + ucp[10] = 0x01; + ucp[11] = 0x24; + ucp[12] = 0x30; + ucp[13] = 7 + ul; + ucp[14] = 0x30; + ucp[15] = 5 + ul; + ucp[16] = 0xA1; + ucp[17] = 3 + ul; + ucp[18] = 0x30; + ucp[19] = 1 + ul; + ucp[20] = 0x02; + // Insert charge units + xp = &ucp[21]; + for (count = 0; count < ul; count++) + *xp++ = charge[count]; + dump("Rearranged message:", bp->bufp, + bp->len); + break; + } else { + // we can't handle something else, erase it + int count = l - alen; + unsigned char *xp = ucp; + for (; count; count--, xp++) + xp[0] = xp[alen]; + l -= alen; + bp->len -= alen; + dump("Shortened message:", bp->bufp, + bp->len); + } + } + } + + + c = *sp; /* Message type */ + if (c == 0x05) { + /* SETUP. Use an UI frame */ + dbg("SETUP"); + l2_header[l2_index++] = 0xFF; /* TEI 127 */ + l2_header[l2_index++] = 0x03; /* UI control field */ + skb = dev_alloc_skb(bp->len - AUH_SIZE + l2_index); + } else { + /* use an I frame */ + dbg("I Frame"); + l2_header[l2_index++] = (AUISDN_TEI << 1) | 0x01; /* TEI byte */ + skb = dev_alloc_skb(bp->len - AUH_SIZE + l2_index + 2); + if (skb) { + ahp = cp->isdn.ahp; + if (!ahp) { + err("ahp == NULL"); + return; + } + spin_lock_irqsave(&ahp->seq_lock, flags); + l2_header[l2_index++] = ahp->txseq; /* transmitt sequence number */ + l2_header[l2_index++] = ahp->rxseq; /* receive sequence number */ + ahp->txseq += 2; /* next paket gets next number */ + spin_unlock_irqrestore(&ahp->seq_lock, flags); + } + } + if (!skb) { + err("no memory - skipped"); + return; + } + sp = skb_put(skb, bp->len - AUH_SIZE + l2_index); + /* Add L2 header */ + memcpy(sp, l2_header, l2_index); + memcpy(sp + l2_index, bp->bufp + AUH_SIZE, bp->len - AUH_SIZE); + /* Translate false messages */ + auerisdn_translate_incoming(cp, sp, bp->len - AUH_SIZE + l2_index); + /* Send message to L2 */ + auerisdn_d_l1l2(&cp->isdn, PH_DATA | INDICATION, skb); +} + +/* D-channel is closed because the device is removed */ +/* This is a no-op because ISDN close is handled different */ +static void auerisdn_disconnect_dc(struct auerscon *scp) +{ +} + + +/* confirmation helper function. */ +static void auerisdn_d_confirmskb(struct auerswald *cp, + struct sk_buff *skb) +{ + /* free the skb */ + if (skb) { + skb_pull(skb, skb->len); + dev_kfree_skb_any(skb); + } + + /* confirm the sending of data */ + dbg("Confirm PH_DATA"); + auerisdn_d_l1l2(&cp->isdn, PH_DATA | CONFIRM, NULL); +} + +/* D-channel transfer function L2->L1 */ +static void auerisdn_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) +{ + struct auerhisax *ahp; + struct sk_buff *skb; + unsigned int len; + int ret; + struct auerbuf *bp; + struct auerswald *cp; + unsigned long flags; + unsigned int l2_index; + unsigned char c; + unsigned char l2_header[32]; + unsigned char *sp; + + dbg("hisax D-Channel l2l1 called"); + + /* Get reference to auerhisax struct */ + cp = NULL; + ahp = hisax_d_if->priv; + if (ahp) + cp = ahp->cp; + if (cp && !cp->disconnecting) { + /* normal usage */ + switch (pr) { + case PH_ACTIVATE | REQUEST: /* activation request */ + dbg("Activation Request"); + cp->isdn.dc_activated = 1; + /* send activation back to layer 2 */ + auerisdn_d_l1l2(&cp->isdn, + PH_ACTIVATE | INDICATION, NULL); + break; + case PH_DEACTIVATE | REQUEST: /* deactivation request */ + dbg("Deactivation Request"); + cp->isdn.dc_activated = 0; + /* send deactivation back to layer 2 */ + auerisdn_d_l1l2(&cp->isdn, + PH_DEACTIVATE | INDICATION, NULL); + break; + case PH_DATA | REQUEST: /* Transmit data request */ + skb = (struct sk_buff *) arg; + len = skb->len; + l2_index = 0; + sp = skb->data; + dump("Data Request:", sp, len); + + /* Parse the L2 header */ + if (!len) + goto phd_free; + c = *sp++; /* SAPI */ + l2_header[l2_index++] = c; + len--; + if (!len) + goto phd_free; + c = *sp++; /* TEI */ + l2_header[l2_index++] = c; + len--; + if (!len) + goto phd_free; + c = *sp++; /* Control Field, Byte 1 */ + len--; + if (!(c & 0x01)) { + /* I FRAME */ + dbg("I Frame"); + if (!len) + goto phd_free; + spin_lock_irqsave(&ahp->seq_lock, flags); + ahp->rxseq = c + 2; /* store new sequence info */ + spin_unlock_irqrestore(&ahp->seq_lock, + flags); + sp++; /* skip Control Field, Byte 2 */ + len--; + /* Check for RELEASE command */ + /* and change to RELEASE_COMPLETE */ + if (sp[3] == 0x4D) + sp[3] = 0x5A; + goto phd_send; + } + /* check the frame type */ + switch (c) { + case 0x03: /* UI frame */ + dbg("UI Frame"); + if (l2_header[0] == 0xFC) { + dbg("TEI Managment"); + l2_header[0] = 0xFE; /* set C/R bit in answer */ + l2_header[l2_index++] = c; /* Answer is UI frame */ + if (!len) + break; + c = *sp++; /* Managment ID */ + len--; + if (c != 0x0F) + break; + l2_header[l2_index++] = c; + /* Read Reference Number */ + if (!len) + break; + l2_header[l2_index++] = *sp++; + len--; + if (!len) + break; + l2_header[l2_index++] = *sp++; + len--; + if (!len) + break; + c = *sp++; /* Message Type */ + len--; + switch (c) { + case 0x01: /* Identity Request */ + dbg("Identity Request"); + l2_header[l2_index++] = 0x02; /* Identity Assign */ + l2_header[l2_index++] = + (AUISDN_TEI << 1) | + 0x01; + goto phd_answer; + default: + dbg("Unhandled TEI Managment %X", (int) c); + break; + } + // throw away + goto phd_free; + } + /* else send UI frame out */ + goto phd_send; + case 0x01: /* RR frame */ + case 0x05: /* RNR frame */ + dbg("RR/RNR Frame"); + if (!len) + break; + c = *sp++; /* Control Field, Byte 2 */ + len--; + if (!(c & 0x01)) + break; /* P/F = 1 in commands */ + if (l2_header[0] & 0x02) + break; /* C/R = 0 from TE */ + dbg("Send RR as answer"); + l2_header[l2_index++] = 0x01; /* send an RR as Answer */ + spin_lock_irqsave(&ahp->seq_lock, flags); + l2_header[l2_index++] = ahp->rxseq | 0x01; + spin_unlock_irqrestore(&ahp->seq_lock, + flags); + goto phd_answer; + case 0x7F: /* SABME */ + dbg("SABME"); + spin_lock_irqsave(&ahp->seq_lock, flags); + ahp->txseq = 0; + ahp->rxseq = 0; + spin_unlock_irqrestore(&ahp->seq_lock, + flags); + l2_header[l2_index++] = 0x73; /* UA */ + goto phd_answer; + case 0x53: /* DISC */ + dbg("DISC"); + /* Send back a UA */ + l2_header[l2_index++] = 0x73; /* UA */ + goto phd_answer; + default: + dbg("Unhandled L2 Message %X", (int) c); + break; + } + /* all done */ + goto phd_free; + + /* we have to generate a local answer */ + /* first, confirm old message, free old skb */ + phd_answer:auerisdn_d_confirmskb(cp, + skb); + + /* allocate a new skbuff */ + skb = dev_alloc_skb(l2_index); + if (!skb) { + err("no memory for new skb"); + break; + } + dump("local answer to L2 is:", l2_header, + l2_index); + memcpy(skb_put(skb, l2_index), l2_header, + l2_index); + auerisdn_d_l1l2(&cp->isdn, PH_DATA | INDICATION, + skb); + break; + + /* we have to send the L3 message out */ + phd_send:if (!len) + goto phd_free; /* no message left */ + + /* get a new data buffer */ + bp = auerbuf_getbuf(&cp->bufctl); + if (!bp) { + warn("no auerbuf free"); + goto phd_free; + } + /* protect against too big write requests */ + /* Should not happen */ + if (len > cp->maxControlLength) { + err("too long D-channel paket truncated"); + len = cp->maxControlLength; + } + + /* Copy the data */ + memcpy(bp->bufp + AUH_SIZE, sp, len); + + /* set the header byte */ + *(bp->bufp) = + cp->isdn.dchannelservice. + id | AUH_DIRECT | AUH_UNSPLIT; + + /* Set the transfer Parameters */ + bp->len = len + AUH_SIZE; + bp->dr->bRequestType = AUT_WREQ; + bp->dr->bRequest = AUV_WBLOCK; + bp->dr->wValue = cpu_to_le16(0); + bp->dr->wIndex = + cpu_to_le16(cp->isdn.dchannelservice. + id | AUH_DIRECT | AUH_UNSPLIT); + bp->dr->wLength = cpu_to_le16(len + AUH_SIZE); + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_sndctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, + bp->bufp, len + AUH_SIZE, + auerisdn_dcw_complete, bp); + /* up we go */ + ret = + auerchain_submit_urb(&cp->controlchain, + bp->urbp); + if (ret) + auerisdn_dcw_complete(bp->urbp); + else + dbg("auerisdn_dwrite: Write OK"); + /* confirm message, free skb */ + phd_free:auerisdn_d_confirmskb(cp, + skb); + break; + + default: + warn("pr %#x\n", pr); + break; + } + } else { + /* hisax interface is down */ + switch (pr) { + case PH_ACTIVATE | REQUEST: /* activation request */ + dbg("D channel PH_ACTIVATE | REQUEST with interface down"); + /* don't answer this request! Endless... */ + break; + case PH_DEACTIVATE | REQUEST: /* deactivation request */ + dbg("D channel PH_DEACTIVATE | REQUEST with interface down"); + hisax_d_if->l1l2(hisax_d_if, + PH_DEACTIVATE | INDICATION, NULL); + break; + case PH_DATA | REQUEST: /* Transmit data request */ + dbg("D channel PH_DATA | REQUEST with interface down"); + skb = (struct sk_buff *) arg; + /* free data buffer */ + if (skb) { + skb_pull(skb, skb->len); + dev_kfree_skb_any(skb); + } + /* send confirmation back to layer 2 */ + hisax_d_if->l1l2(hisax_d_if, PH_DATA | CONFIRM, + NULL); + break; + default: + warn("pr %#x\n", pr); + break; + } + } +} + + +/* Completion function for D channel open */ +static void auerisdn_dcopen_complete(struct urb *urbp) +{ + struct auerbuf *bp = (struct auerbuf *) urbp->context; + struct auerswald *cp = + ((struct auerswald *) ((char *) (bp->list) - + (unsigned + long) (&((struct auerswald *) 0)-> + bufctl))); + dbg("auerisdn_dcopen_complete called"); + + auerbuf_releasebuf(bp); + + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); +} + + +/* Open the D-channel once more */ +static void auerisdn_dcopen(unsigned long data) +{ + struct auerswald *cp = (struct auerswald *) data; + struct auerbuf *bp; + int ret; + + if (cp->disconnecting) + return; + dbg("auerisdn_dcopen running"); + + /* get a buffer for the command */ + bp = auerbuf_getbuf(&cp->bufctl); + /* if no buffer available: can't change the mode */ + if (!bp) { + err("auerisdn_dcopen: no data buffer available"); + return; + } + + /* fill the control message */ + bp->dr->bRequestType = AUT_WREQ; + bp->dr->bRequest = AUV_CHANNELCTL; + bp->dr->wValue = cpu_to_le16(1); + bp->dr->wIndex = cpu_to_le16(0); + bp->dr->wLength = cpu_to_le16(0); + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_sndctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, bp->bufp, 0, + (usb_complete_t) auerisdn_dcopen_complete, bp); + + /* submit the control msg */ + ret = auerchain_submit_urb(&cp->controlchain, bp->urbp); + dbg("dcopen submitted"); + if (ret) { + bp->urbp->status = ret; + auerisdn_dcopen_complete(bp->urbp); + } + return; +} + + +/* Initialize the isdn related items in struct auerswald */ +void auerisdn_init_dev(struct auerswald *cp) +{ + unsigned int u; + cp->isdn.dchannelservice.id = AUH_UNASSIGNED; + cp->isdn.dchannelservice.dispatch = auerisdn_dispatch_dc; + cp->isdn.dchannelservice.disconnect = auerisdn_disconnect_dc; + init_timer(&cp->isdn.dcopen_timer); + cp->isdn.dcopen_timer.data = (unsigned long) cp; + cp->isdn.dcopen_timer.function = auerisdn_dcopen; + for (u = 0; u < AUISDN_BCHANNELS; u++) { + cp->isdn.bc[u].cp = cp; + cp->isdn.bc[u].mode = L1_MODE_NULL; + cp->isdn.bc[u].channel = u; + spin_lock_init(&cp->isdn.bc[u].txskb_lock); + } +} + + +/* Connect to the HISAX interface. Returns 0 if successfull */ +int auerisdn_probe(struct auerswald *cp) +{ + struct hisax_b_if *b_if[AUISDN_BCHANNELS]; + struct usb_endpoint_descriptor *ep; + struct auerhisax *ahp; + DECLARE_WAIT_QUEUE_HEAD(wqh); + unsigned int u; + unsigned char *ucp; + unsigned int first_time; + int ret; + + /* First allocate resources, then register hisax interface */ + + /* Allocate RX buffers */ + for (u = 0; u < AUISDN_BCHANNELS; u++) { + if (!cp->isdn.bc[u].rxbuf) { + cp->isdn.bc[u].rxbuf = + (char *) kmalloc(AUISDN_RXSIZE, GFP_KERNEL); + if (!cp->isdn.bc[u].rxbuf) { + err("can't allocate buffer for B channel RX data"); + return -1; + } + } + } + + /* Read out B-Channel output fifo size */ + ucp = kmalloc(32, GFP_KERNEL); + if (!ucp) { + err("Out of memory"); + return -3; + } + ret = usb_control_msg(cp->usbdev, /* pointer to device */ + usb_rcvctrlpipe(cp->usbdev, 0), /* pipe to control endpoint */ + AUV_GETINFO, /* USB message request value */ + AUT_RREQ, /* USB message request type value */ + 0, /* USB message value */ + AUDI_OUTFSIZE, /* USB message index value */ + ucp, /* pointer to the receive buffer */ + 32, /* length of the buffer */ + HZ * 2); /* time to wait for the message to complete before timing out */ + if (ret < 4) { + kfree(ucp); + err("can't read TX Fifo sizes for B1,B2"); + return -4; + } + for (u = 0; u < AUISDN_BCHANNELS; u++) { + ret = le16_to_cpup(ucp + u * 2); + cp->isdn.bc[u].ofsize = ret; + cp->isdn.bc[u].txfree = ret; + } + kfree(ucp); + for (u = 0; u < AUISDN_BCHANNELS; u++) { + dbg("B%d buffer size is %d", u, cp->isdn.bc[u].ofsize); + } + + /* get the B channel output INT size */ + cp->isdn.intbo_endp = AU_IRQENDPBO; + ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_OUT | AU_IRQENDPBO); + if (!ep) { + /* Some devices have another endpoint number here ... */ + cp->isdn.intbo_endp = AU_IRQENDPBO_2; + ep = usb_epnum_to_ep_desc(cp->usbdev, + USB_DIR_OUT | AU_IRQENDPBO_2); + if (!ep) { + err("can't get B channel OUT endpoint"); + return -5; + } + } + cp->isdn.outsize = ep->wMaxPacketSize; + cp->isdn.outInterval = ep->bInterval; + cp->isdn.usbdev = cp->usbdev; + + /* allocate the urb and data buffer */ + if (!cp->isdn.intbo_urbp) { + cp->isdn.intbo_urbp = usb_alloc_urb(0); + if (!cp->isdn.intbo_urbp) { + err("can't allocate urb for B channel output endpoint"); + return -6; + } + } + if (!cp->isdn.intbo_bufp) { + cp->isdn.intbo_bufp = + (char *) kmalloc(cp->isdn.outsize, GFP_KERNEL); + if (!cp->isdn.intbo_bufp) { + err("can't allocate buffer for B channel output endpoint"); + return -7; + } + } + + /* get the B channel input INT size */ + ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_IN | AU_IRQENDPBI); + if (!ep) { + err("can't get B channel IN endpoint"); + return -8; + } + cp->isdn.insize = ep->wMaxPacketSize; + + /* allocate the urb and data buffer */ + if (!cp->isdn.intbi_urbp) { + cp->isdn.intbi_urbp = usb_alloc_urb(0); + if (!cp->isdn.intbi_urbp) { + err("can't allocate urb for B channel input endpoint"); + return -9; + } + } + if (!cp->isdn.intbi_bufp) { + cp->isdn.intbi_bufp = + (char *) kmalloc(cp->isdn.insize, GFP_KERNEL); + if (!cp->isdn.intbi_bufp) { + err("can't allocate buffer for B channel input endpoint"); + return -10; + } + } + + /* setup urb */ + FILL_INT_URB(cp->isdn.intbi_urbp, cp->usbdev, + usb_rcvintpipe(cp->usbdev, AU_IRQENDPBI), + cp->isdn.intbi_bufp, cp->isdn.insize, + auerisdn_intbi_complete, cp, ep->bInterval); + /* start the urb */ + cp->isdn.intbi_urbp->status = 0; /* needed! */ + ret = usb_submit_urb(cp->isdn.intbi_urbp); + if (ret < 0) { + err("activation of B channel input int failed %d", ret); + usb_free_urb(cp->isdn.intbi_urbp); + cp->isdn.intbi_urbp = NULL; + return -11; + } + + /* request the D-channel service now */ + dbg("Requesting D channel now"); + cp->isdn.dchannelservice.id = AUH_DCHANNEL; + if (auerswald_addservice(cp, &cp->isdn.dchannelservice)) { + err("can not open D-channel"); + cp->isdn.dchannelservice.id = AUH_UNASSIGNED; + return -2; + } + + /* Find a free hisax interface */ + for (u = 0; u < AUER_MAX_DEVICES; u++) { + ahp = &auerhisax_table[u]; + if (!ahp->cp) { + first_time = (u == 0); + goto ahp_found; + } + } + /* no free interface found */ + return -12; + + /* we found a free hisax interface */ + ahp_found: + /* Wait until ipppd timeout expired. The reason behind this ugly construct: + If we connect to a hisax device without waiting for ipppd we are not able + to make a new IP connection. */ + if (ahp->last_close) { + unsigned long timeout = jiffies - ahp->last_close; + if (timeout < AUISDN_IPTIMEOUT) { + info("waiting for ipppd to timeout"); + sleep_on_timeout(&wqh, AUISDN_IPTIMEOUT - timeout); + } + } + + cp->isdn.ahp = ahp; + u = ahp->hisax_registered; + ahp->hisax_registered = 1; + ahp->cp = cp; + + /* now do the registration */ + if (!u) { + for (u = 0; u < AUISDN_BCHANNELS; u++) { + b_if[u] = &ahp->hisax_b_if[u]; + } + if (hisax_register + (&ahp->hisax_d_if, b_if, "auerswald_usb", + ISDN_PTYPE_EURO)) { + err("hisax registration failed"); + ahp->cp = NULL; + cp->isdn.ahp = NULL; + ahp->hisax_registered = 0; + return -13; + } + dbg("hisax interface registered"); + } + + /* send a D channel L1 activation indication to hisax */ + auerisdn_d_l1l2(&cp->isdn, PH_ACTIVATE | INDICATION, NULL); + cp->isdn.dc_activated = 1; + + /* do another D channel activation for problematic devices */ + cp->isdn.dcopen_timer.expires = jiffies + HZ; + dbg("add timer"); + add_timer(&cp->isdn.dcopen_timer); + + return 0; +} + +/* The USB device was disconnected */ +void auerisdn_disconnect(struct auerswald *cp) +{ + struct auerhisax *ahp; + DECLARE_WAIT_QUEUE_HEAD(wqh); + unsigned long flags; + unsigned int u; + int ret; + unsigned int stop_bc; + + dbg("auerisdn_disconnect called"); + + /* stop a running timer */ + del_timer_sync(&cp->isdn.dcopen_timer); + + /* first, stop the B channels */ + stop_bc = auerisdn_b_disconnect(cp); + + /* stop the D channels */ + auerisdn_d_l1l2(&cp->isdn, PH_DEACTIVATE | INDICATION, NULL); + cp->isdn.dc_activated = 0; + dbg("D-Channel disconnected"); + + /* Wait a moment */ + sleep_on_timeout(&wqh, HZ / 10); + + /* Shut the connection to the hisax interface */ + ahp = cp->isdn.ahp; + if (ahp) { + dbg("closing connection to hisax interface"); + ahp->cp = NULL; + cp->isdn.ahp = NULL; + /* time of last closure */ + if (stop_bc) + /* if we kill a running connection ... */ + ahp->last_close = jiffies; + else + ahp->last_close = 0; + } + + /* Now free the memory */ + if (cp->isdn.intbi_urbp) { + ret = usb_unlink_urb(cp->isdn.intbi_urbp); + if (ret) + dbg("B in: nonzero int unlink result received: %d", + ret); + usb_free_urb(cp->isdn.intbi_urbp); + cp->isdn.intbi_urbp = NULL; + } + kfree(cp->isdn.intbi_bufp); + cp->isdn.intbi_bufp = NULL; + + if (cp->isdn.intbo_urbp) { + cp->isdn.intbo_urbp->transfer_flags &= ~USB_ASYNC_UNLINK; + ret = usb_unlink_urb(cp->isdn.intbo_urbp); + if (ret) + dbg("B out: nonzero int unlink result received: %d", ret); + usb_free_urb(cp->isdn.intbo_urbp); + cp->isdn.intbo_urbp = NULL; + } + kfree(cp->isdn.intbo_bufp); + cp->isdn.intbo_bufp = NULL; + + /* Remove the rx and tx buffers */ + for (u = 0; u < AUISDN_BCHANNELS; u++) { + kfree(cp->isdn.bc[u].rxbuf); + cp->isdn.bc[u].rxbuf = NULL; + spin_lock_irqsave(&cp->isdn.bc[u].txskb_lock, flags); + if (cp->isdn.bc[u].txskb) { + skb_pull(cp->isdn.bc[u].txskb, + cp->isdn.bc[u].txskb->len); + dev_kfree_skb_any(cp->isdn.bc[u].txskb); + cp->isdn.bc[u].txskb = NULL; + } + spin_unlock_irqrestore(&cp->isdn.bc[u].txskb_lock, flags); + } + + /* Remove the D-channel connection */ + auerswald_removeservice(cp, &cp->isdn.dchannelservice); +} + + +/*-------------------------------------------------------------------*/ +/* Environment for long-lasting hisax interface */ + +/* Wrapper for hisax B0 channel L2L1 */ +static void auerisdn_b0_l2l1_wrapper(struct hisax_if *ifc, int pr, + void *arg) +{ + auerisdn_b_l2l1(ifc, pr, arg, 0); +} + +/* Wrapper for hisax B1 channel L2L1 */ +static void auerisdn_b1_l2l1_wrapper(struct hisax_if *ifc, int pr, + void *arg) +{ + auerisdn_b_l2l1(ifc, pr, arg, 1); +} + +/* Init the global variables */ +void auerisdn_init(void) +{ + struct auerhisax *ahp; + unsigned int u; + + memset(&auerhisax_table, 0, sizeof(auerhisax_table)); + for (u = 0; u < AUER_MAX_DEVICES; u++) { + ahp = &auerhisax_table[u]; + spin_lock_init(&ahp->seq_lock); + ahp->hisax_d_if.ifc.priv = ahp; + ahp->hisax_d_if.ifc.l2l1 = auerisdn_d_l2l1; + ahp->hisax_b_if[0].ifc.priv = ahp; + ahp->hisax_b_if[0].ifc.l2l1 = auerisdn_b0_l2l1_wrapper; + ahp->hisax_b_if[1].ifc.priv = ahp; + ahp->hisax_b_if[1].ifc.l2l1 = auerisdn_b1_l2l1_wrapper; + } +} + +/* Deinit the global variables */ +void auerisdn_cleanup(void) +{ + struct auerhisax *ahp; + int i; + + /* cleanup last allocated device first */ + for (i = AUER_MAX_DEVICES - 1; i >= 0; i--) { + ahp = &auerhisax_table[i]; + if (ahp->cp) { + err("hisax device %d open at cleanup", i); + } + if (ahp->hisax_registered) { + hisax_unregister(&ahp->hisax_d_if); + dbg("hisax interface %d freed", i); + } + } +} diff -Nru a/drivers/usb/auerisdn.h b/drivers/usb/auerisdn.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerisdn.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,94 @@ +/*****************************************************************************/ +/* + * auerisdn.h -- Auerswald PBX/System Telephone ISDN interface. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#ifndef AUERISDN_H +#define AUERISDN_H + +#if (CONFIG_USB_AUERISDN || CONFIG_USB_AUERISDN_MODULE) + +#include +#include "auerserv.h" +#include "auerisdn_b.h" + +#define AUISDN_IPTIMEOUT (HZ * 60) /* IP Timeout is 40s */ + +struct auerswald; +struct auerhisax; + +struct auerisdn { + struct auerscon dchannelservice; /* serving the D channel */ + struct auerhisax *ahp; /* Reference to hisax interface */ + unsigned int dc_activated; /* 1 if D-Channel is activated */ + struct auerisdnbc bc[AUISDN_BCHANNELS]; /* B channel data */ + unsigned int insize; /* Max. Block Size of Input INT */ + unsigned int outsize; /* Max. Block Size of Output INT */ + unsigned int outInterval; /* nr. of ms between INT OUT transfers */ + struct urb *intbi_urbp; /* B channel Input Interrupt urb */ + unsigned char *intbi_bufp; /* B channel Input data buffer */ + unsigned int paketsize; /* Data size of the INT OUT pakets */ + struct usb_device *usbdev; /* USB device handle */ + unsigned int intbo_state; /* Status of INT OUT urb */ + struct urb *intbo_urbp; /* B channel Output Interrupt urb */ + unsigned char *intbo_bufp; /* B channel Output data buffer */ + unsigned int intbo_index; /* Index of last served B channel */ + unsigned int intbo_toggletimer; /* data toggle timer for 2 b channels */ + unsigned int intbo_endp; /* grrr.. different on some devices */ + struct timer_list dcopen_timer; /* Open D-channel once more later... */ +}; + +struct auerhisax { + struct hisax_d_if hisax_d_if; /* Hisax D-Channel interface */ + struct hisax_b_if hisax_b_if[AUISDN_BCHANNELS]; /* Hisax B-channel interfaces */ + struct auerswald *cp; /* Context to usb device */ + unsigned int hisax_registered; /* 1 if registered at hisax interface */ + unsigned char txseq; /* L2 emulation: tx sequence byte */ + unsigned char rxseq; /* L2 emulation: rx sequence byte */ + spinlock_t seq_lock; /* Lock sequence numbers */ + unsigned long last_close; /* Time of last close in jiffies */ +}; + +/* Function Prototypes */ +void auerisdn_init_dev(struct auerswald *cp); + +int auerisdn_probe(struct auerswald *cp); + +void auerisdn_disconnect(struct auerswald *cp); + +void auerisdn_init(void); + +void auerisdn_cleanup(void); + +#else /* no CONFIG_USB_AUERISDN */ + +struct auerisdn { + int dummy; +}; + +/* Dummy ISDN functions */ +#define auerisdn_init_dev( cp) +#define auerisdn_probe( cp) 0 +#define auerisdn_disconnect( cp) +#define auerisdn_init() +#define auerisdn_cleanup() +#endif /* CONFIG_USB_AUERISDN */ + +#endif /* AUERISDN_H */ diff -Nru a/drivers/usb/auerisdn_b.c b/drivers/usb/auerisdn_b.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerisdn_b.c Thu Feb 20 12:07:16 2003 @@ -0,0 +1,689 @@ +/*****************************************************************************/ +/* + * auerisdn_b.c -- Auerswald PBX/System Telephone ISDN B-channel interface. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#include /* ISDN constants */ +#include /* skb functions */ + +#undef DEBUG /* include debug macros until it's done */ +#include /* standard usb header */ + +#include "auerisdn.h" +#include "auermain.h" + +/*-------------------------------------------------------------------*/ +/* ISDN B channel support defines */ +#define AUISDN_BC_1MS 8 /* Bytes per channel and ms */ +#define AUISDN_BC_INC 4 /* change INT OUT size increment */ +#define AUISDN_BCDATATHRESHOLD 48 /* for unsymmetric 2-B-channels */ +#define AUISDN_TOGGLETIME 6 /* Timeout for unsymmetric serve */ + +/*-------------------------------------------------------------------*/ +/* Debug support */ +#ifdef DEBUG +#define dump( desc, adr, len) \ +do { \ + unsigned int u; \ + printk (KERN_DEBUG); \ + printk (desc); \ + for (u = 0; u < len; u++) \ + printk (" %02X", adr[u] & 0xFF); \ + printk ("\n"); \ +} while (0) +#else +#define dump( desc, adr, len) +#endif + +/*-------------------------------------------------------------------*/ + +/* Callback to L2 for HISAX */ +/* This callback can be called from 3 sources: + a) from hisax context (answer from a l2l1 function) + b) from interrupt context (a B channel paket arrived, a B channel paket was sent) + c) from kernel daemon context (probe/disconnecting) +*/ +void auerisdn_b_l1l2(struct auerisdnbc *bc, int pr, void *arg) +{ + struct auerhisax *ahp; + struct sk_buff *skb; + + /* do the callback */ + ahp = bc->cp->isdn.ahp; + if (ahp) { + ahp->hisax_b_if[bc->channel].ifc.l1l2(&ahp-> + hisax_b_if[bc-> + channel]. + ifc, pr, arg); + } else { + dbg("auerisdn_b_l1l2 called without ahp"); + if (pr == (PH_DATA | INDICATION)) { + skb = (struct sk_buff *) arg; + if (skb) { + skb_pull(skb, skb->len); + dev_kfree_skb_any(skb); + } + } + } +} + +/* fill the INT OUT data buffer with new data */ +/* Transfer buffer size to fill is in urbp->transfer_buffer_length */ +static void auerisdn_bintbo_newdata(struct auerisdn *ip) +{ + unsigned long flags; + struct urb *urbp = ip->intbo_urbp; + struct auerisdnbc *bc = &ip->bc[0]; /* start with B-channel 0 */ + struct sk_buff *skb; + unsigned char *ucp; + int buf_size; + int len; + int bytes_sent; + int i; + + /* FIXME: this algorithm is fixed to 2 B-channels */ + /* Which B channel should we serve? */ + if (ip->bc[1].mode != L1_MODE_NULL) { + /* B channel 1 is used */ + if (bc->mode != L1_MODE_NULL) { + /* both B-channels are used */ + if (ip->intbo_toggletimer) { + /* simply toggling */ + ip->intbo_toggletimer--; + i = ip->intbo_index ^ 1; /* serve both channels equal */ + } else { + /* search the B channel with the most demand of data */ + i = bc->txfree - ip->bc[1].txfree; + if (i < -AUISDN_BCDATATHRESHOLD) + i = 1; /* B channel 1 needs more data */ + else if (i > AUISDN_BCDATATHRESHOLD) + i = 0; /* B channel 0 needs more data */ + else + i = ip->intbo_index ^ 1; /* serve both channels equal */ + if (i == ip->intbo_index) + ip->intbo_toggletimer = + AUISDN_TOGGLETIME; + } + bc = &ip->bc[i]; + ip->intbo_index = i; + } else { + bc = &ip->bc[1]; + } + } + dbg("INTBO: Fill B%d with %d Bytes, %d Bytes free", + bc->channel + 1, urbp->transfer_buffer_length - AUH_SIZE, + bc->txfree); + + /* Fill the buffer with data */ + ucp = ip->intbo_bufp; + *ucp++ = AUH_B1CHANNEL + bc->channel; /* First byte is channel nr. */ + buf_size = urbp->transfer_buffer_length - AUH_SIZE; + len = 0; + while (len < buf_size) { + spin_lock_irqsave(&bc->txskb_lock, flags); + if ((skb = bc->txskb)) { + /* dump ("raw tx data:", skb->data, skb->len); */ + if (bc->mode == L1_MODE_TRANS) { + bytes_sent = buf_size - len; + if (skb->len < bytes_sent) + bytes_sent = skb->len; + { /* swap tx bytes */ + register unsigned char *src = + skb->data; + unsigned int count; + for (count = 0; count < bytes_sent; + count++) + *ucp++ = + isdnhdlc_bit_rev_tab + [*src++]; + } + len += bytes_sent; + bc->lastbyte = skb->data[bytes_sent - 1]; + } else { + int bs = + isdnhdlc_encode(&bc->outp_hdlc_state, + skb->data, skb->len, + &bytes_sent, + ucp, buf_size - len); + /* dump ("hdlc data:", ucp, bs); */ + len += bs; + ucp += bs; + } + skb_pull(skb, bytes_sent); + + if (!skb->len) { + // Frame sent + bc->txskb = NULL; + spin_unlock_irqrestore(&bc->txskb_lock, + flags); + auerisdn_b_l1l2(bc, PH_DATA | CONFIRM, + (void *) skb->truesize); + dev_kfree_skb_any(skb); + continue; //while + } + } else { + if (bc->mode == L1_MODE_TRANS) { + memset(ucp, bc->lastbyte, buf_size - len); + ucp += buf_size - len; + len = buf_size; + /* dbg ("fill = 0xFF"); */ + } else { + // Send flags + int bs = + isdnhdlc_encode(&bc->outp_hdlc_state, + NULL, 0, &bytes_sent, + ucp, buf_size - len); + /* dbg ("fill = 0x%02X", (int)*ucp); */ + len += bs; + ucp += bs; + } + } + spin_unlock_irqrestore(&bc->txskb_lock, flags); + } + /* dbg ("%d Bytes to TX buffer", len); */ +} + + +/* INT OUT completion handler */ +static void auerisdn_bintbo_complete(struct urb *urbp) +{ + struct auerisdn *ip = urbp->context; + + /* unlink completion? */ + if ((urbp->status == -ENOENT) || (urbp->status == -ECONNRESET)) { + /* should we restart with another size? */ + if (ip->intbo_state == INTBOS_CHANGE) { + dbg("state => RESTART"); + ip->intbo_state = INTBOS_RESTART; + } else { + /* set up variables for later restart */ + dbg("INTBO stopped"); + ip->intbo_state = INTBOS_IDLE; + } + /* nothing more to do */ + return; + } + + /* other state != 0? */ + if (urbp->status) { + warn("auerisdn_bintbo_complete: status = %d", + urbp->status); + return; + } + + /* Should we fill in new data? */ + if (ip->intbo_state == INTBOS_CHANGE) { + dbg("state == INTBOS_CHANGE, no new data"); + return; + } + + /* fill in new data */ + auerisdn_bintbo_newdata(ip); +} + +/* set up the INT OUT URB the first time */ +/* Don't start the URB */ +static void auerisdn_bintbo_setup(struct auerisdn *ip, unsigned int len) +{ + ip->intbo_state = INTBOS_IDLE; + FILL_INT_URB(ip->intbo_urbp, ip->usbdev, + usb_sndintpipe(ip->usbdev, ip->intbo_endp), + ip->intbo_bufp, len, auerisdn_bintbo_complete, ip, + ip->outInterval); + ip->intbo_urbp->transfer_flags |= USB_ASYNC_UNLINK; + ip->intbo_urbp->status = 0; +} + +/* restart the INT OUT endpoint */ +static void auerisdn_bintbo_restart(struct auerisdn *ip) +{ + struct urb *urbp = ip->intbo_urbp; + int status; + + /* dbg ("auerisdn_intbo_restart"); */ + + /* fresh restart */ + auerisdn_bintbo_setup(ip, ip->paketsize + AUH_SIZE); + + /* Fill in new data */ + auerisdn_bintbo_newdata(ip); + + /* restart the urb */ + ip->intbo_state = INTBOS_RUNNING; + status = usb_submit_urb(urbp); + if (status < 0) { + err("can't submit INT OUT urb, status = %d", status); + urbp->status = status; + urbp->complete(urbp); + } +} + +/* change the size of the INT OUT endpoint */ +static void auerisdn_bchange(struct auerisdn *ip, unsigned int paketsize) +{ + /* changing... */ + dbg("txfree[0] = %d, txfree[1] = %d, old size = %d, new size = %d", + ip->bc[0].txfree, ip->bc[1].txfree, ip->paketsize, paketsize); + ip->paketsize = paketsize; + + if (paketsize == 0) { + /* stop the INT OUT endpoint */ + dbg("stop unlinking INT out urb"); + ip->intbo_state = INTBOS_IDLE; + usb_unlink_urb(ip->intbo_urbp); + return; + } + if (ip->intbo_state != INTBOS_IDLE) { + /* dbg ("unlinking INT out urb"); */ + ip->intbo_state = INTBOS_CHANGE; + usb_unlink_urb(ip->intbo_urbp); + } else { + /* dbg ("restart immediately"); */ + auerisdn_bintbo_restart(ip); + } +} + +/* serve the outgoing B channel interrupt */ +/* Called from the INT IN completion handler */ +static void auerisdn_bserv(struct auerisdn *ip) +{ + struct auerisdnbc *bc; + unsigned int u; + unsigned int paketsize; + + /* should we start the INT OUT endpoint again? */ + if (ip->intbo_state == INTBOS_RESTART) { + /* dbg ("Restart INT OUT from INT IN"); */ + auerisdn_bintbo_restart(ip); + return; + } + /* no new calculation if change already in progress */ + if (ip->intbo_state == INTBOS_CHANGE) + return; + + /* calculation of transfer parameters for INT OUT endpoint */ + paketsize = 0; + for (u = 0; u < AUISDN_BCHANNELS; u++) { + bc = &ip->bc[u]; + if (bc->mode != L1_MODE_NULL) { /* B channel is active */ + unsigned int bpp = AUISDN_BC_1MS * ip->outInterval; + if (bc->txfree < bpp) { /* buffer is full, throttle */ + bc->txsize = bpp - AUISDN_BC_INC; + paketsize += bpp - AUISDN_BC_INC; + } else if (bc->txfree < bpp * 2) { + paketsize += bc->txsize; /* schmidt-trigger, continue */ + } else if (bc->txfree < bpp * 4) { /* we are in synch */ + bc->txsize = bpp; + paketsize += bpp; + } else if (bc->txfree > bc->ofsize / 2) {/* we have to fill the buffer */ + bc->txsize = bpp + AUISDN_BC_INC; + paketsize += bpp + AUISDN_BC_INC; + } else { + paketsize += bc->txsize; /* schmidt-trigger, continue */ + } + } + } + + /* check if we have to change the paket size */ + if (paketsize != ip->paketsize) + auerisdn_bchange(ip, paketsize); +} + +/* Send activation/deactivation state to L2 */ +static void auerisdn_bconf(struct auerisdnbc *bc) +{ + unsigned long flags; + struct sk_buff *skb; + + if (bc->mode == L1_MODE_NULL) { + auerisdn_b_l1l2(bc, PH_DEACTIVATE | INDICATION, NULL); + /* recycle old txskb */ + spin_lock_irqsave(&bc->txskb_lock, flags); + skb = bc->txskb; + bc->txskb = NULL; + spin_unlock_irqrestore(&bc->txskb_lock, flags); + if (skb) { + skb_pull(skb, skb->len); + auerisdn_b_l1l2(bc, PH_DATA | CONFIRM, + (void *) skb->truesize); + dev_kfree_skb_any(skb); + } + } else { + auerisdn_b_l1l2(bc, PH_ACTIVATE | INDICATION, NULL); + } +} + +/* B channel setup completion handler */ +static void auerisdn_bmode_complete(struct urb *urb) +{ + struct auerswald *cp; + struct auerbuf *bp = (struct auerbuf *) urb->context; + struct auerisdnbc *bc; + int channel; + + dbg("auerisdn_bmode_complete called"); + cp = ((struct auerswald *) ((char *) (bp->list) - + (unsigned + long) (&((struct auerswald *) 0)-> + bufctl))); + + /* select the B-channel */ + channel = le16_to_cpu(bp->dr->wIndex); + channel -= AUH_B1CHANNEL; + if (channel < 0) + goto rel; + if (channel >= AUISDN_BCHANNELS) + goto rel; + bc = &cp->isdn.bc[channel]; + + /* Check for success */ + if (urb->status) { + err("complete with non-zero status: %d", urb->status); + } else { + bc->mode = *bp->bufp; + } + /* Signal current mode to L2 */ + auerisdn_bconf(bc); + + /* reuse the buffer */ + rel:auerbuf_releasebuf(bp); + + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); +} + +/* Setup a B channel transfer mode */ +static void auerisdn_bmode(struct auerisdnbc *bc, unsigned int mode) +{ + struct auerswald *cp = bc->cp; + struct auerbuf *bp; + int ret; + + /* don't allow activation on disconnect */ + if (cp->disconnecting) { + mode = L1_MODE_NULL; + + /* Else check if something changed */ + } else if (bc->mode != mode) { + if ((mode != L1_MODE_NULL) && (mode != L1_MODE_TRANS)) { + /* init RX hdlc decoder */ + dbg("rcv init"); + isdnhdlc_rcv_init(&bc->inp_hdlc_state, 0); + /* init TX hdlc decoder */ + dbg("out init"); + isdnhdlc_out_init(&bc->outp_hdlc_state, 0, 0); + } + /* stop ASAP */ + if (mode == L1_MODE_NULL) + bc->mode = mode; + if ((bc->mode == L1_MODE_NULL) || (mode == L1_MODE_NULL)) { + /* Activation or deactivation required */ + + /* get a buffer for the command */ + bp = auerbuf_getbuf(&cp->bufctl); + /* if no buffer available: can't change the mode */ + if (!bp) { + err("auerisdn_bmode: no data buffer available"); + return; + } + + /* fill the control message */ + bp->dr->bRequestType = AUT_WREQ; + bp->dr->bRequest = AUV_CHANNELCTL; + if (mode != L1_MODE_NULL) + bp->dr->wValue = cpu_to_le16(1); + else + bp->dr->wValue = cpu_to_le16(0); + bp->dr->wIndex = + cpu_to_le16(AUH_B1CHANNEL + bc->channel); + bp->dr->wLength = cpu_to_le16(0); + *bp->bufp = mode; + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_sndctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, + bp->bufp, 0, + (usb_complete_t) + auerisdn_bmode_complete, bp); + + /* submit the control msg */ + ret = + auerchain_submit_urb(&cp->controlchain, + bp->urbp); + if (ret) { + bp->urbp->status = ret; + auerisdn_bmode_complete(bp->urbp); + } + return; + } + } + /* new mode is set */ + bc->mode = mode; + + /* send confirmation to L2 */ + auerisdn_bconf(bc); +} + +/* B-channel transfer function L2->L1 */ +void auerisdn_b_l2l1(struct hisax_if *ifc, int pr, void *arg, + unsigned int channel) +{ + struct auerhisax *ahp; + struct auerisdnbc *bc; + struct auerswald *cp; + struct sk_buff *skb; + unsigned long flags; + int mode; + + cp = NULL; + ahp = (struct auerhisax *) ifc->priv; + if (ahp) + cp = ahp->cp; + if (cp && !cp->disconnecting) { + /* normal execution */ + bc = &cp->isdn.bc[channel]; + switch (pr) { + case PH_ACTIVATE | REQUEST: /* activation request */ + mode = (int) arg; /* one of the L1_MODE constants */ + dbg("B%d, PH_ACTIVATE_REQUEST Mode = %d", + bc->channel + 1, mode); + auerisdn_bmode(bc, mode); + break; + case PH_DEACTIVATE | REQUEST: /* deactivation request */ + dbg("B%d, PH_DEACTIVATE_REQUEST", bc->channel + 1); + auerisdn_bmode(bc, L1_MODE_NULL); + break; + case PH_DATA | REQUEST: /* Transmit data request */ + skb = (struct sk_buff *) arg; + spin_lock_irqsave(&bc->txskb_lock, flags); + if (bc->txskb) { + err("Overflow in B channel TX"); + skb_pull(skb, skb->len); + dev_kfree_skb_any(skb); + } else { + if (cp->disconnecting + || (bc->mode == L1_MODE_NULL)) { + skb_pull(skb, skb->len); + spin_unlock_irqrestore(&bc-> + txskb_lock, + flags); + auerisdn_b_l1l2(bc, + PH_DATA | CONFIRM, + (void *) skb-> + truesize); + dev_kfree_skb_any(skb); + goto next; + } else + bc->txskb = skb; + } + spin_unlock_irqrestore(&bc->txskb_lock, flags); + next:break; + default: + warn("pr %#x\n", pr); + break; + } + } else { + /* hisax interface is down */ + switch (pr) { + case PH_ACTIVATE | REQUEST: /* activation request */ + dbg("B channel: PH_ACTIVATE | REQUEST with interface down"); + /* don't answer this request! Endless... */ + break; + case PH_DEACTIVATE | REQUEST: /* deactivation request */ + dbg("B channel: PH_DEACTIVATE | REQUEST with interface down"); + ifc->l1l2(ifc, PH_DEACTIVATE | INDICATION, NULL); + break; + case PH_DATA | REQUEST: /* Transmit data request */ + dbg("B channel: PH_DATA | REQUEST with interface down"); + skb = (struct sk_buff *) arg; + /* free data buffer */ + if (skb) { + skb_pull(skb, skb->len); + dev_kfree_skb_any(skb); + } + /* send confirmation back to layer 2 */ + ifc->l1l2(ifc, PH_DATA | CONFIRM, NULL); + break; + default: + warn("pr %#x\n", pr); + break; + } + } +} + +/* Completion handler for B channel input endpoint */ +void auerisdn_intbi_complete(struct urb *urb) +{ + unsigned int bytecount; + unsigned char *ucp; + int channel; + unsigned int syncbit; + unsigned int syncdata; + struct auerisdnbc *bc; + struct sk_buff *skb; + int count; + int status; + struct auerswald *cp = (struct auerswald *) urb->context; + /* do not respond to an error condition */ + if (urb->status != 0) { + dbg("nonzero URB status = %d", urb->status); + return; + } + if (cp->disconnecting) + return; + + /* Parse and extract the header information */ + bytecount = urb->actual_length; + ucp = cp->isdn.intbi_bufp; + if (!bytecount) + return; /* no data */ + channel = *ucp & AUH_TYPEMASK; + syncbit = *ucp & AUH_SYNC; + ucp++; + bytecount--; + channel -= AUH_B1CHANNEL; + if (channel < 0) + return; /* unknown data channel, no B1,B2 */ + if (channel >= AUISDN_BCHANNELS) + return; /* unknown data channel, no B1,B2 */ + bc = &cp->isdn.bc[channel]; + if (!bytecount) + return; + /* Calculate amount of bytes which are free in tx device buffer */ + bc->txfree = ((255 - *ucp++) * bc->ofsize) / 256; + /* dbg ("%d Bytes free in TX buffer", bc->txfree); */ + bytecount--; + + /* Next Byte: TX sync information */ + if (syncbit) { + if (!bytecount) + goto int_tx; + syncdata = *ucp++; + dbg("Sync data = %d", syncdata); + bytecount--; + } + /* The rest of the paket is plain data */ + if (!bytecount) + goto int_tx; + /* dump ("RX Data is:", ucp, bytecount); */ + + /* Send B channel data to upper layers */ + while (bytecount > 0) { + if (bc->mode == L1_MODE_NULL) { + /* skip the data. Nobody needs them */ + status = 0; + bytecount = 0; + } else if (bc->mode == L1_MODE_TRANS) { + { /* swap rx bytes */ + register unsigned char *dest = bc->rxbuf; + status = bytecount; + for (; bytecount; bytecount--) + *dest++ = + isdnhdlc_bit_rev_tab[*ucp++]; + } + + } else { + status = isdnhdlc_decode(&bc->inp_hdlc_state, ucp, + bytecount, &count, + bc->rxbuf, AUISDN_RXSIZE); + ucp += count; + bytecount -= count; + } + if (status > 0) { + /* Good frame received */ + if (!(skb = dev_alloc_skb(status))) { + warn("receive out of memory"); + break; + } + memcpy(skb_put(skb, status), bc->rxbuf, status); + /* dump ("HDLC Paket", bc->rxbuf, status); */ + auerisdn_b_l1l2(bc, PH_DATA | INDICATION, skb); + /* these errors may actually happen at the start of a connection! */ + } else if (status == -HDLC_CRC_ERROR) { + dbg("CRC error"); + } else if (status == -HDLC_FRAMING_ERROR) { + dbg("framing error"); + } else if (status == -HDLC_LENGTH_ERROR) { + dbg("length error"); + } + } + + int_tx: /* serve the outgoing B channel */ + auerisdn_bserv(&cp->isdn); +} + +/* Stop the B channel activity. The device is disconnecting */ +/* This function is called after cp->disconnecting is true */ +unsigned int auerisdn_b_disconnect(struct auerswald *cp) +{ + unsigned int u; + struct auerisdnbc *bc; + unsigned int result = 0; + + /* Close the B channels */ + for (u = 0; u < AUISDN_BCHANNELS; u++) { + bc = &cp->isdn.bc[u]; + if (bc->mode != L1_MODE_NULL) { /* B channel is active */ + auerisdn_bmode(bc, L1_MODE_NULL); + result = 1; + } + } + /* return 1 if there is B channel traffic */ + return result; +} diff -Nru a/drivers/usb/auerisdn_b.h b/drivers/usb/auerisdn_b.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerisdn_b.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,66 @@ +/*****************************************************************************/ +/* + * auerisdn_b.h -- Auerswald PBX/System Telephone ISDN B channel interface. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#ifndef AUERISDN_B_H +#define AUERISDN_B_H + +#include <../drivers/isdn/hisax/hisax_if.h> +#include +#include "auerbuf.h" +#include <../drivers/isdn/hisax/isdnhdlc.h> + +#define AUISDN_RXSIZE 4096 /* RX buffer size */ +#define AUISDN_BCHANNELS 2 /* Number of supported B channels */ + +/* states for intbo_state */ +#define INTBOS_IDLE 0 +#define INTBOS_RUNNING 1 +#define INTBOS_CHANGE 2 +#define INTBOS_RESTART 3 + +/* ...................................................................*/ +/* B channel state data */ +struct auerswald; +struct auerisdnbc { + struct auerswald *cp; /* Context to usb device */ + struct sk_buff *txskb; /* sk buff to transmitt */ + spinlock_t txskb_lock; /* protect against races */ + unsigned int mode; /* B-channel mode */ + unsigned int channel; /* Number of this B-channel */ + unsigned int ofsize; /* Size of device OUT fifo in Bytes */ + int txfree; /* free bytes in tx buffer of device */ + unsigned int txsize; /* size of data paket for this channel */ + unsigned char *rxbuf; /* Receiver input buffer */ + struct isdnhdlc_vars inp_hdlc_state; /* state for RX software HDLC */ + struct isdnhdlc_vars outp_hdlc_state; /* state for TX software HDLC */ + unsigned int lastbyte; /* last byte sent out to trans. B channel */ +}; + +/* Function Prototypes */ +void auerisdn_b_l2l1(struct hisax_if *ifc, int pr, void *arg, + unsigned int channel); +void auerisdn_b_l1l2(struct auerisdnbc *bc, int pr, void *arg); + +void auerisdn_intbi_complete(struct urb *urb); +unsigned int auerisdn_b_disconnect(struct auerswald *cp); + +#endif /* AUERISDN_B_H */ diff -Nru a/drivers/usb/auermain.c b/drivers/usb/auermain.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auermain.c Thu Feb 20 12:07:16 2003 @@ -0,0 +1,897 @@ +/*****************************************************************************/ +/* + * auermain.c -- Auerswald PBX/System Telephone usb driver. + * + * Copyright (C) 2002 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 +#include +#include +#include +#undef DEBUG /* include debug macros until it's done */ +#include +#include "auerchain.h" +#include "auerbuf.h" +#include "auerchar.h" +#include "auerserv.h" +#include "auermain.h" +#include "auerisdn.h" + +/*-------------------------------------------------------------------*/ +/* Debug support */ +#ifdef DEBUG +#define dump( desc, adr, len) \ +do { \ + unsigned int u; \ + printk (KERN_DEBUG); \ + printk (desc); \ + for (u = 0; u < len; u++) \ + printk (" %02X", adr[u] & 0xFF); \ + printk ("\n"); \ +} while (0) +#else +#define dump( desc, adr, len) +#endif + +/*-------------------------------------------------------------------*/ +/* Version Information */ +#define DRIVER_VERSION "1.2.0" +#define DRIVER_AUTHOR "Wolfgang Mües " +#define DRIVER_DESC "Auerswald PBX/System Telephone usb driver" + +/*-------------------------------------------------------------------*/ +/* Internal data structures */ + +/* the global usb devfs handle */ +extern devfs_handle_t usb_devfs_handle; + +/* array of pointers to our devices that are currently connected */ +static struct auerswald *auerdev_table[AUER_MAX_DEVICES]; + +/* lock to protect the auerdev_table structure */ +static struct semaphore auerdev_table_mutex; + +/*-------------------------------------------------------------------*/ +/* Forwards */ +static void auerswald_ctrlread_complete(struct urb *urb); + +/*-------------------------------------------------------------------*/ +/* Completion handlers */ + +/* Values of urb->status or results of usb_submit_urb(): +0 Initial, OK +-EINPROGRESS during submission until end +-ENOENT if urb is unlinked +-ETIMEDOUT Transfer timed out, NAK +-ENOMEM Memory Overflow +-ENODEV Specified USB-device or bus doesn't exist +-ENXIO URB already queued +-EINVAL a) Invalid transfer type specified (or not supported) + b) Invalid interrupt interval (0n256) +-EAGAIN a) Specified ISO start frame too early + b) (using ISO-ASAP) Too much scheduled for the future wait some time and try again. +-EFBIG Too much ISO frames requested (currently uhci900) +-EPIPE Specified pipe-handle/Endpoint is already stalled +-EMSGSIZE Endpoint message size is zero, do interface/alternate setting +-EPROTO a) Bitstuff error + b) Unknown USB error +-EILSEQ CRC mismatch +-ENOSR Buffer error +-EREMOTEIO Short packet detected +-EXDEV ISO transfer only partially completed look at individual frame status for details +-EINVAL ISO madness, if this happens: Log off and go home +-EOVERFLOW babble +*/ + +/* check if a status code allows a retry */ +static int auerswald_status_retry(int status) +{ + switch (status) { + case 0: + case -ETIMEDOUT: + case -EOVERFLOW: + case -EAGAIN: + case -EPIPE: + case -EPROTO: + case -EILSEQ: + case -ENOSR: + case -EREMOTEIO: + return 1; /* do a retry */ + } + return 0; /* no retry possible */ +} + + +/* Completion of asynchronous write block */ +void auerchar_ctrlwrite_complete(struct urb *urb) +{ + struct auerbuf *bp = (struct auerbuf *) urb->context; + struct auerswald *cp = + ((struct auerswald *) ((char *) (bp->list) - + (unsigned + long) (&((struct auerswald *) 0)-> + bufctl))); + dbg("auerchar_ctrlwrite_complete called"); + + /* reuse the buffer */ + auerbuf_releasebuf(bp); + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); +} + +/* Completion handler for dummy retry packet */ +static void auerswald_ctrlread_wretcomplete(struct urb *urb) +{ + struct auerbuf *bp = (struct auerbuf *) urb->context; + struct auerswald *cp; + int ret; + dbg("auerswald_ctrlread_wretcomplete called"); + dbg("complete with status: %d", urb->status); + cp = ((struct auerswald *) ((char *) (bp->list) - + (unsigned + long) (&((struct auerswald *) 0)-> + bufctl))); + + /* check if it is possible to advance */ + if (!auerswald_status_retry(urb->status) || !cp->usbdev) { + /* reuse the buffer */ + err("control dummy: transmission error %d, can not retry", + urb->status); + auerbuf_releasebuf(bp); + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); + return; + } + + /* fill the control message */ + bp->dr->bRequestType = AUT_RREQ; + bp->dr->bRequest = AUV_RBLOCK; + bp->dr->wLength = bp->dr->wValue; /* temporary stored */ + bp->dr->wValue = cpu_to_le16(1); /* Retry Flag */ + /* bp->dr->wIndex = channel id; remains */ + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_rcvctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, bp->bufp, + le16_to_cpu(bp->dr->wLength), + (usb_complete_t) auerswald_ctrlread_complete, bp); + + /* submit the control msg as next paket */ + ret = auerchain_submit_urb_list(&cp->controlchain, bp->urbp, 1); + if (ret) { + dbg("auerswald_ctrlread_complete: nonzero result of auerchain_submit_urb_list %d", ret); + bp->urbp->status = ret; + auerswald_ctrlread_complete(bp->urbp); + } +} + +/* completion handler for receiving of control messages */ +static void auerswald_ctrlread_complete(struct urb *urb) +{ + unsigned int serviceid; + struct auerswald *cp; + struct auerscon *scp; + struct auerbuf *bp = (struct auerbuf *) urb->context; + int ret; + dbg("auerswald_ctrlread_complete called"); + + cp = ((struct auerswald *) ((char *) (bp->list) - + (unsigned + long) (&((struct auerswald *) 0)-> + bufctl))); + + /* check if there is valid data in this urb */ + if (urb->status) { + dbg("complete with non-zero status: %d", urb->status); + /* should we do a retry? */ + if (!auerswald_status_retry(urb->status) + || !cp->usbdev || (cp->version < AUV_RETRY) + || (bp->retries >= AU_RETRIES)) { + /* reuse the buffer */ + err("control read: transmission error %d, can not retry", urb->status); + auerbuf_releasebuf(bp); + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); + return; + } + bp->retries++; + dbg("Retry count = %d", bp->retries); + /* send a long dummy control-write-message to allow device firmware to react */ + bp->dr->bRequestType = AUT_WREQ; + bp->dr->bRequest = AUV_DUMMY; + bp->dr->wValue = bp->dr->wLength; /* temporary storage */ + // bp->dr->wIndex channel ID remains + bp->dr->wLength = cpu_to_le16(32); /* >= 8 bytes */ + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_sndctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, bp->bufp, 32, + (usb_complete_t) + auerswald_ctrlread_wretcomplete, bp); + + /* submit the control msg as next paket */ + ret = + auerchain_submit_urb_list(&cp->controlchain, bp->urbp, + 1); + if (ret) { + dbg("auerswald_ctrlread_complete: nonzero result of auerchain_submit_urb_list %d", ret); + bp->urbp->status = ret; + auerswald_ctrlread_wretcomplete(bp->urbp); + } + return; + } + + /* get the actual bytecount (incl. headerbyte) */ + bp->len = urb->actual_length; + serviceid = bp->bufp[0] & AUH_TYPEMASK; + dbg("Paket with serviceid %d and %d bytes received", serviceid, + bp->len); + + /* dispatch the paket */ + scp = cp->services[serviceid]; + if (scp) { + /* look, Ma, a listener! */ + scp->dispatch(scp, bp); + } + + /* release the paket */ + auerbuf_releasebuf(bp); + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); +} + +/*-------------------------------------------------------------------*/ +/* Handling of Interrupt Endpoint */ +/* This interrupt Endpoint is used to inform the host about waiting + messages from the USB device. +*/ +/* int completion handler. */ +static void auerswald_int_complete(struct urb *urb) +{ + unsigned int channelid; + unsigned int bytecount; + int ret; + struct auerbuf *bp = NULL; + struct auerswald *cp = (struct auerswald *) urb->context; + + dbg("auerswald_int_complete called"); + + /* do not respond to an error condition */ + if (urb->status != 0) { + dbg("nonzero URB status = %d", urb->status); + return; + } + + /* check if all needed data was received */ + if (urb->actual_length < AU_IRQMINSIZE) { + dbg("invalid data length received: %d bytes", + urb->actual_length); + return; + } + + /* check the command code */ + if (cp->intbufp[0] != AU_IRQCMDID) { + dbg("invalid command received: %d", cp->intbufp[0]); + return; + } + + /* check the command type */ + if (cp->intbufp[1] != AU_BLOCKRDY) { + dbg("invalid command type received: %d", cp->intbufp[1]); + return; + } + + /* now extract the information */ + channelid = cp->intbufp[2]; + bytecount = le16_to_cpup(&cp->intbufp[3]); + + /* check the channel id */ + if (channelid >= AUH_TYPESIZE) { + dbg("invalid channel id received: %d", channelid); + return; + } + + /* check the byte count */ + if (bytecount > (cp->maxControlLength + AUH_SIZE)) { + dbg("invalid byte count received: %d", bytecount); + return; + } + dbg("Service Channel = %d", channelid); + dbg("Byte Count = %d", bytecount); + + /* get a buffer for the next data paket */ + bp = auerbuf_getbuf(&cp->bufctl); + /* if no buffer available: skip it */ + if (!bp) { + dbg("auerswald_int_complete: no data buffer available"); + /* can we do something more? + This is a big problem: if this int packet is ignored, the + device will wait forever and not signal any more data. + The only real solution is: having enought buffers! + Or perhaps temporary disabling the int endpoint? + */ + return; + } + + /* fill the control message */ + bp->dr->bRequestType = AUT_RREQ; + bp->dr->bRequest = AUV_RBLOCK; + bp->dr->wValue = cpu_to_le16(0); + bp->dr->wIndex = cpu_to_le16(channelid | AUH_DIRECT | AUH_UNSPLIT); + bp->dr->wLength = cpu_to_le16(bytecount); + FILL_CONTROL_URB(bp->urbp, cp->usbdev, + usb_rcvctrlpipe(cp->usbdev, 0), + (unsigned char *) bp->dr, bp->bufp, bytecount, + (usb_complete_t) auerswald_ctrlread_complete, bp); + + /* submit the control msg */ + ret = auerchain_submit_urb(&cp->controlchain, bp->urbp); + if (ret) { + dbg("auerswald_int_complete: nonzero result of auerchain_submit_urb %d", ret); + bp->urbp->status = ret; + auerswald_ctrlread_complete(bp->urbp); + /* here applies the same problem as above: device locking! */ + } +} + +/* int memory deallocation + NOTE: no mutex please! +*/ +static void auerswald_int_free(struct auerswald *cp) +{ + if (cp->inturbp) { + usb_free_urb(cp->inturbp); + cp->inturbp = NULL; + } + kfree(cp->intbufp); +} + +/* This function is called to activate the interrupt + endpoint. This function returns 0 if successfull or an error code. + NOTE: no mutex please! +*/ +static int auerswald_int_open(struct auerswald *cp) +{ + int ret; + struct usb_endpoint_descriptor *ep; + int irqsize; + dbg("auerswald_int_open"); + + ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_IN | AU_IRQENDP); + if (!ep) { + ret = -EFAULT; + goto intoend; + } + irqsize = ep->wMaxPacketSize; + cp->irqsize = irqsize; + + /* allocate the urb and data buffer */ + if (!cp->inturbp) { + cp->inturbp = usb_alloc_urb(0); + if (!cp->inturbp) { + ret = -ENOMEM; + goto intoend; + } + } + if (!cp->intbufp) { + cp->intbufp = (char *) kmalloc(irqsize, GFP_KERNEL); + if (!cp->intbufp) { + ret = -ENOMEM; + goto intoend; + } + } + /* setup urb */ + FILL_INT_URB(cp->inturbp, cp->usbdev, + usb_rcvintpipe(cp->usbdev, AU_IRQENDP), cp->intbufp, + irqsize, auerswald_int_complete, cp, ep->bInterval); + /* start the urb */ + cp->inturbp->status = 0; /* needed! */ + ret = usb_submit_urb(cp->inturbp); + + intoend: + if (ret < 0) { + /* activation of interrupt endpoint has failed. Now clean up. */ + dbg("auerswald_int_open: activation of int endpoint failed"); + + /* deallocate memory */ + auerswald_int_free(cp); + } + return ret; +} + +/* This function is called to deactivate the interrupt + endpoint. This function returns 0 if successfull or an error code. + NOTE: no mutex please! +*/ +static int auerswald_int_release(struct auerswald *cp) +{ + int ret = 0; + dbg("auerswald_int_release"); + + /* stop the int endpoint */ + if (cp->inturbp) { + ret = usb_unlink_urb(cp->inturbp); + if (ret) + dbg("nonzero int unlink result received: %d", ret); + } + + /* deallocate memory */ + auerswald_int_free(cp); + + return ret; +} + +/* --------------------------------------------------------------------- */ +/* Helper functions */ + +/* Delete an auerswald driver context */ +void auerswald_delete(struct auerswald *cp) +{ + dbg("auerswald_delete"); + if (cp == NULL) + return; + + /* Wake up all processes waiting for a buffer */ + wake_up(&cp->bufferwait); + + /* Cleaning up */ + auerisdn_disconnect(cp); + auerswald_int_release(cp); + auerchain_free(&cp->controlchain); + auerbuf_free_buffers(&cp->bufctl); + + /* release the memory */ + kfree(cp); +} + + +/* add a new service to the device + scp->id must be set! + return: 0 if OK, else error code +*/ +int auerswald_addservice(struct auerswald *cp, struct auerscon *scp) +{ + int ret; + + /* is the device available? */ + if (!cp->usbdev) { + dbg("usbdev == NULL"); + return -EIO; /*no: can not add a service, sorry */ + } + + /* is the service available? */ + if (cp->services[scp->id]) { + dbg("service is busy"); + return -EBUSY; + } + + /* device is available, service is free */ + cp->services[scp->id] = scp; + + /* register service in device */ + ret = auerchain_control_msg(&cp->controlchain, /* pointer to control chain */ + cp->usbdev, /* pointer to device */ + usb_sndctrlpipe(cp->usbdev, 0), /* pipe to control endpoint */ + AUV_CHANNELCTL, /* USB message request value */ + AUT_WREQ, /* USB message request type value */ + 0x01, /* open */ /* USB message value */ + scp->id, /* USB message index value */ + NULL, /* pointer to the data to send */ + 0, /* length in bytes of the data to send */ + HZ * 2); /* time to wait for the message to complete before timing out */ + if (ret < 0) { + dbg("auerswald_addservice: auerchain_control_msg returned error code %d", ret); + /* undo above actions */ + cp->services[scp->id] = NULL; + return ret; + } + + dbg("auerswald_addservice: channel open OK"); + return 0; +} + + +/* remove a service from the the device + scp->id must be set! */ +void auerswald_removeservice(struct auerswald *cp, struct auerscon *scp) +{ + dbg("auerswald_removeservice called"); + + /* check if we have a service allocated */ + if (scp->id == AUH_UNASSIGNED) + return; + + /* If there is a device: close the channel */ + if (cp->usbdev && !cp->disconnecting) { + /* Close the service channel inside the device */ + int ret = auerchain_control_msg(&cp->controlchain, /* pointer to control chain */ + cp->usbdev, /* pointer to device */ + usb_sndctrlpipe(cp->usbdev, 0), /* pipe to control endpoint */ + AUV_CHANNELCTL, /* USB message request value */ + AUT_WREQ, /* USB message request type value */ + 0x00, /* close */ /* USB message value */ + scp->id, /* USB message index value */ + NULL, /* pointer to the data to send */ + 0, /* length in bytes of the data to send */ + HZ * 2); /* time to wait for the message to complete before timing out */ + if (ret < 0) { + dbg("auerswald_removeservice: auerchain_control_msg returned error code %d", ret); + } else { + dbg("auerswald_removeservice: channel close OK"); + } + } + + /* remove the service from the device */ + cp->services[scp->id] = NULL; + scp->id = AUH_UNASSIGNED; +} + + +/*----------------------------------------------------------------------*/ +/* File operation structure */ +static struct file_operations auerswald_fops = { + owner:THIS_MODULE, + llseek:auerchar_llseek, + read:auerchar_read, + write:auerchar_write, + ioctl:auerchar_ioctl, + open:auerchar_open, + release:auerchar_release, +}; + +/* --------------------------------------------------------------------- */ +/* Special USB driver functions */ + +/* Probe if this driver wants to serve an USB device + + This entry point is called whenever a new device is attached to the bus. + Then the device driver has to create a new instance of its internal data + structures for the new device. + + The dev argument specifies the device context, which contains pointers + to all USB descriptors. The interface argument specifies the interface + number. If a USB driver wants to bind itself to a particular device and + interface it has to return a pointer. This pointer normally references + the device driver's context structure. + + Probing normally is done by checking the vendor and product identifications + or the class and subclass definitions. If they match the interface number + is compared with the ones supported by the driver. When probing is done + class based it might be necessary to parse some more USB descriptors because + the device properties can differ in a wide range. +*/ +static void *auerswald_probe(struct usb_device *usbdev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct auerswald *cp = NULL; + DECLARE_WAIT_QUEUE_HEAD(wqh); + unsigned int dtindex; + unsigned int u = 0; + char *pbuf; + int ret; + + dbg("probe: vendor id 0x%x, device id 0x%x ifnum:%d", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + ifnum); + + /* See if the device offered us matches that we can accept */ + if (usbdev->descriptor.idVendor != ID_AUERSWALD) + return NULL; + + /* we use only the first -and only- interface */ + if (ifnum != 0) + return NULL; + + /* prevent module unloading while sleeping */ + MOD_INC_USE_COUNT; + + /* allocate memory for our device and intialize it */ + cp = kmalloc(sizeof(struct auerswald), GFP_KERNEL); + if (cp == NULL) { + err("out of memory"); + goto pfail; + } + + /* Initialize device descriptor */ + memset(cp, 0, sizeof(struct auerswald)); + init_MUTEX(&cp->mutex); + cp->usbdev = usbdev; + auerchain_init(&cp->controlchain); + auerbuf_init(&cp->bufctl); + init_waitqueue_head(&cp->bufferwait); + auerisdn_init_dev(cp); + + /* find a free slot in the device table */ + down(&auerdev_table_mutex); + for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) { + if (auerdev_table[dtindex] == NULL) + break; + } + if (dtindex >= AUER_MAX_DEVICES) { + err("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES); + up(&auerdev_table_mutex); + goto pfail; + } + + /* Give the device a name */ + sprintf(cp->name, AU_PREFIX "%d", dtindex); + + /* Store the index */ + cp->dtindex = dtindex; + auerdev_table[dtindex] = cp; + up(&auerdev_table_mutex); + + /* initialize the devfs node for this device and register it */ + cp->devfs = devfs_register(usb_devfs_handle, cp->name, + DEVFS_FL_DEFAULT, USB_MAJOR, + AUER_MINOR_BASE + dtindex, + S_IFCHR | S_IRUGO | S_IWUGO, + &auerswald_fops, NULL); + + /* Get the usb version of the device */ + cp->version = cp->usbdev->descriptor.bcdDevice; + dbg("Version is %X", cp->version); + + /* allow some time to settle the device */ + sleep_on_timeout(&wqh, HZ / 3); + + /* Try to get a suitable textual description of the device */ + /* Device name: */ + ret = + usb_string(cp->usbdev, AUSI_DEVICE, cp->dev_desc, + AUSI_DLEN - 1); + if (ret >= 0) { + u += ret; + /* Append Serial Number */ + memcpy(&cp->dev_desc[u], ",Ser# ", 6); + u += 6; + ret = + usb_string(cp->usbdev, AUSI_SERIALNR, &cp->dev_desc[u], + AUSI_DLEN - u - 1); + if (ret >= 0) { + u += ret; + /* Append subscriber number */ + memcpy(&cp->dev_desc[u], ", ", 2); + u += 2; + ret = + usb_string(cp->usbdev, AUSI_MSN, + &cp->dev_desc[u], + AUSI_DLEN - u - 1); + if (ret >= 0) { + u += ret; + } + } + } + cp->dev_desc[u] = '\0'; + info("device is a %s", cp->dev_desc); + + /* get the maximum allowed control transfer length */ + pbuf = (char *) kmalloc(2, GFP_KERNEL); /* use an allocated buffer because of urb target */ + if (!pbuf) { + err("out of memory"); + goto pfail; + } + ret = usb_control_msg(cp->usbdev, /* pointer to device */ + usb_rcvctrlpipe(cp->usbdev, 0), /* pipe to control endpoint */ + AUV_GETINFO, /* USB message request value */ + AUT_RREQ, /* USB message request type value */ + 0, /* USB message value */ + AUDI_MBCTRANS, /* USB message index value */ + pbuf, /* pointer to the receive buffer */ + 2, /* length of the buffer */ + HZ * 2); /* time to wait for the message to complete before timing out */ + if (ret == 2) { + cp->maxControlLength = le16_to_cpup(pbuf); + kfree(pbuf); + dbg("setup: max. allowed control transfersize is %d bytes", + cp->maxControlLength); + } else { + kfree(pbuf); + err("setup: getting max. allowed control transfer length failed with error %d", ret); + goto pfail; + } + /* allocate a chain for the control messages */ + if (auerchain_setup(&cp->controlchain, AUCH_ELEMENTS)) { + err("out of memory"); + goto pfail; + } + + /* allocate buffers for control messages */ + if (auerbuf_setup + (&cp->bufctl, AU_RBUFFERS * 2, + cp->maxControlLength + AUH_SIZE)) { + err("out of memory"); + goto pfail; + } + + /* start the interrupt endpoint */ + if (auerswald_int_open(cp)) { + err("int endpoint failed"); + goto pfail; + } + + /* Try to connect to hisax interface */ + if (auerisdn_probe(cp)) { + err("hisax connect failed"); + goto pfail; + } + + /* all OK */ + return cp; + + /* Error exit: clean up the memory */ + pfail:auerswald_delete(cp); + MOD_DEC_USE_COUNT; + return NULL; +} + + +/* Disconnect driver from a served device + + This function is called whenever a device which was served by this driver + is disconnected. + + The argument dev specifies the device context and the driver_context + returns a pointer to the previously registered driver_context of the + probe function. After returning from the disconnect function the USB + framework completly deallocates all data structures associated with + this device. So especially the usb_device structure must not be used + any longer by the usb driver. +*/ +static void auerswald_disconnect(struct usb_device *usbdev, + void *driver_context) +{ + struct auerswald *cp = (struct auerswald *) driver_context; + unsigned int u; + + /* all parallel tasks can react on disconnect ASAP */ + cp->disconnecting = 1; + down(&cp->mutex); + info("device /dev/usb/%s now disconnecting", cp->name); + + /* remove from device table */ + /* Nobody can open() this device any more */ + down(&auerdev_table_mutex); + auerdev_table[cp->dtindex] = NULL; + up(&auerdev_table_mutex); + + /* remove our devfs node */ + /* Nobody can see this device any more */ + devfs_unregister(cp->devfs); + + /* stop the ISDN connection */ + auerisdn_disconnect(cp); + + /* Stop the interrupt endpoint 0 */ + auerswald_int_release(cp); + + /* remove the control chain allocated in auerswald_probe + This has the benefit of + a) all pending (a)synchronous urbs are unlinked + b) all buffers dealing with urbs are reclaimed + */ + auerchain_free(&cp->controlchain); + + if (cp->open_count == 0) { + struct auerscon *scp; + /* nobody is using this device. So we can clean up now */ + up(&cp->mutex); /* up() is possible here because no other task + can open the device (see above). I don't want + to kfree() a locked mutex. */ + /* disconnect the D channel */ + scp = cp->services[AUH_DCHANNEL]; + if (scp) + scp->disconnect(scp); + auerswald_delete(cp); + } else { + /* device is used. Remove the pointer to the + usb device (it's not valid any more). The last + release() will do the clean up */ + cp->usbdev = NULL; + up(&cp->mutex); + /* Terminate waiting writers */ + wake_up(&cp->bufferwait); + /* Inform all waiting readers */ + for (u = 0; u < AUH_TYPESIZE; u++) { + struct auerscon *scp = cp->services[u]; + if (scp) + scp->disconnect(scp); + } + } + + /* The device releases this module */ + MOD_DEC_USE_COUNT; +} + +/* Descriptor for the devices which are served by this driver. + NOTE: this struct is parsed by the usbmanager install scripts. + Don't change without caution! +*/ +static struct usb_device_id auerswald_ids[] = { + {USB_DEVICE(ID_AUERSWALD, 0x00C0)}, /* COMpact 2104 USB/DSL */ + {USB_DEVICE(ID_AUERSWALD, 0x00DB)}, /* COMpact 4410/2206 USB */ + {USB_DEVICE(ID_AUERSWALD, 0x00DC)}, /* comming soon... */ + {USB_DEVICE(ID_AUERSWALD, 0x00F1)}, /* Comfort 2000 System Telephone */ + {USB_DEVICE(ID_AUERSWALD, 0x00F2)}, /* Comfort 1200 System Telephone */ + {} /* Terminating entry */ +}; + +/* Standard module device table */ +MODULE_DEVICE_TABLE(usb, auerswald_ids); + +/* Standard usb driver struct */ +static struct usb_driver auerswald_driver = { + name:"auerswald", + probe:auerswald_probe, + disconnect:auerswald_disconnect, + fops:&auerswald_fops, + minor:AUER_MINOR_BASE, + id_table:auerswald_ids, +}; + + +/* --------------------------------------------------------------------- */ +/* Module loading/unloading */ + +/* Driver initialisation. Called after module loading. + NOTE: there is no concurrency at _init +*/ +static int __init auerswald_init(void) +{ + int result; + dbg("init"); + + /* initialize the device table */ + memset(&auerdev_table, 0, sizeof(auerdev_table)); + init_MUTEX(&auerdev_table_mutex); + auerisdn_init(); + + /* register driver at the USB subsystem */ + /* NOTE: usb_register() may call probe()! */ + result = usb_register(&auerswald_driver); + if (result < 0) { + err("driver could not be registered"); + return -1; + } + return 0; +} + +/* Driver deinit. Called before module removal. + NOTE: there is no concurrency at _cleanup +*/ +static void __exit auerswald_cleanup(void) +{ + dbg("cleanup"); + auerisdn_cleanup(); + usb_deregister(&auerswald_driver); +} + +/* --------------------------------------------------------------------- */ +/* Linux device driver module description */ + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +module_init(auerswald_init); +module_exit(auerswald_cleanup); + +/* --------------------------------------------------------------------- */ diff -Nru a/drivers/usb/auermain.h b/drivers/usb/auermain.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auermain.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,172 @@ +/*****************************************************************************/ +/* + * auermain.h -- Auerswald PBX/System Telephone usb driver. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +#ifndef AUERMAIN_H +#define AUERMAIN_H + +#include +#include +#include "auerchain.h" +#include "auerbuf.h" +#include "auerserv.h" +#include "auerisdn.h" + +/*-------------------------------------------------------------------*/ +/* Private declarations for Auerswald USB driver */ + +/* Auerswald Vendor ID */ +#define ID_AUERSWALD 0x09BF + +#ifndef AUER_MINOR_BASE /* allow external override */ +#define AUER_MINOR_BASE 112 /* auerswald driver minor number */ +#endif + +/* we can have up to this number of device plugged in at once */ +#define AUER_MAX_DEVICES 16 + +/* prefix for the device descriptors in /dev/usb */ +#define AU_PREFIX "auer" + +/* 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_SYNC 0x40 /* Sync to start of HDLC frame for B1,B2 */ + +#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 */ + +/* B channel Interrupt endpoint definitions */ +#define AU_IRQENDPBI 2 /* Input Endpoint number */ +#define AU_IRQENDPBO 3 /* Output Endpoint number for 4410, 2206 */ +#define AU_IRQENDPBO_2 2 /* Output Endpoint number for 2104 */ + +/* 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 */ + +/* ...................................................................*/ +/* USB device context */ +struct auerswald { + struct semaphore mutex; /* protection in user context */ + char name[16]; /* name of the /dev/usb entry */ + unsigned int dtindex; /* index in the device table */ + devfs_handle_t devfs; /* devfs device node */ + 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 */ + struct auerbufctl bufctl; /* Buffer control for control transfers */ + struct auerscon *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 */ + volatile unsigned int disconnecting;/* 1: removal in progress */ + struct auerisdn isdn; /* ISDN-Related parameters */ +}; + +/* array of pointers to our devices that are currently connected */ +extern struct auerswald *auerdev_table[AUER_MAX_DEVICES]; + +/* lock to protect the auerdev_table structure */ +extern struct semaphore auerdev_table_mutex; + +void auerswald_removeservice(struct auerswald *cp, struct auerscon *scp); + +int auerswald_addservice(struct auerswald *cp, struct auerscon *scp); + +void auerchar_ctrlwrite_complete(struct urb *urb); + +void auerswald_delete(struct auerswald *cp); + +#endif /* AUERMAIN_H */ diff -Nru a/drivers/usb/auerserv.h b/drivers/usb/auerserv.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/auerserv.h Thu Feb 20 12:07:16 2003 @@ -0,0 +1,47 @@ +/*****************************************************************************/ +/* + * auerserv.h -- Auerswald PBX/System Telephone service request structure. + * + * Copyright (C) 2002 Wolfgang Mües (wolfgang@iksw-muees.de) + * + * 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. + */ + /*****************************************************************************/ + +/* The auerswald ISDN devices have a logical channel concept. Many channels are + * realized via one control endpoint or INT endpoint. At the receiver side, these + * messages must be dispatched. Some data may be for an application which is + * connected through the char interface, other data may be D-channel information + * routed to ISDN4LINUX. The auerscon struct is used to dispatch the data. + */ + +#ifndef AUERSERV_H +#define AUERSERV_H + +#include "auerbuf.h" + +/* service context */ +struct auerscon; +typedef void (*auer_dispatch_t) (struct auerscon *, struct auerbuf *); +typedef void (*auer_disconn_t) (struct auerscon *); + +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 readers */ +}; + + +#endif /* AUERSERV_H */ diff -Nru a/drivers/usb/auerswald.c b/drivers/usb/auerswald.c --- a/drivers/usb/auerswald.c Thu Feb 20 12:07:15 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2196 +0,0 @@ -/*****************************************************************************/ -/* - * 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 -#include -#include -#include -#include -#undef DEBUG /* include debug macros until it's done */ -#include - -/*-------------------------------------------------------------------*/ -/* 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 " -#define DRIVER_DESC "Auerswald PBX/System Telephone usb driver" - -/*-------------------------------------------------------------------*/ -/* Private declarations for Auerswald USB driver */ - -/* Auerswald Vendor ID */ -#define ID_AUERSWALD 0x09BF - -#ifndef AUER_MINOR_BASE /* allow external override */ -#define AUER_MINOR_BASE 112 /* auerswald driver minor number */ -#endif - -/* we can have up to this number of device plugged in at once */ -#define AUER_MAX_DEVICES 16 - -/* prefix for the device descriptors in /dev/usb */ -#define AU_PREFIX "auer" - -/* 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 *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[16]; /* name of the /dev/usb entry */ - unsigned int dtindex; /* index in the device table */ - devfs_handle_t devfs; /* devfs device node */ - 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; - -/* the global usb devfs handle */ -extern devfs_handle_t usb_devfs_handle; - -/* array of pointers to our devices that are currently connected */ -static pauerswald_t dev_table[AUER_MAX_DEVICES]; - -/* lock to protect the dev_table structure */ -static struct semaphore dev_table_mutex; - -/* ................................................................... */ -/* 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); -static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp); - - -/*-------------------------------------------------------------------*/ -/* USB chain helper functions */ -/* -------------------------- */ - -/* completion function for chained urbs */ -static void auerchain_complete (struct urb * urb) -{ - 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); - - /* 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); - - /* 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); - } - } 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); - /* 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); - } - } - - 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 - USB_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); - 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); - 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 &= ~USB_ASYNC_UNLINK; - dbg ("unlink active urb"); - usb_unlink_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) -{ - 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) -{ - DECLARE_WAITQUEUE (wait, current); - auerchain_chs_t chs; - int status; - - dbg ("auerchain_start_wait_urb called"); - init_waitqueue_head (&chs.wqh); - chs.done = 0; - - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&chs.wqh, &wait); - urb->context = &chs; - status = auerchain_submit_urb (acp, urb); - if (status) { - /* something went wrong */ - set_current_state (TASK_RUNNING); - remove_wait_queue (&chs.wqh, &wait); - return status; - } - - while (timeout && !chs.done) - { - timeout = schedule_timeout (timeout); - set_current_state(TASK_UNINTERRUPTIBLE); - rmb(); - } - - set_current_state (TASK_RUNNING); - remove_wait_queue (&chs.wqh, &wait); - - if (!timeout && !chs.done) { - if (urb->status != -EINPROGRESS) { /* No callback?!! */ - dbg ("auerchain_start_wait_urb: raced timeout"); - status = urb->status; - } else { - dbg ("auerchain_start_wait_urb: timeout"); - auerchain_unlink_urb (acp, urb); /* remove urb safely */ - status = -ETIMEDOUT; - } - } else - status = urb->status; - - if (actual_length) - *actual_length = urb->actual_length; - - return status; -} - - -/* auerchain_control_msg - Builds a control urb, sends it off and waits for completion - acp: pointer to the auerchain - 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 the transfered length, 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 auerchain_submit_urb() -*/ -static int auerchain_control_msg (pauerchain_t acp, struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, - __u16 value, __u16 index, void *data, __u16 size, int timeout) -{ - int ret; - struct usb_ctrlrequest *dr; - struct urb *urb; - int length; - - dbg ("auerchain_control_msg"); - dr = kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL); - if (!dr) - return -ENOMEM; - urb = usb_alloc_urb (0); - if (!urb) { - kfree (dr); - return -ENOMEM; - } - - dr->bRequestType = requesttype; - dr->bRequest = request; - dr->wValue = cpu_to_le16 (value); - dr->wIndex = cpu_to_le16 (index); - dr->wLength = cpu_to_le16 (size); - - FILL_CONTROL_URB (urb, dev, pipe, (unsigned char*)dr, data, size, /* build urb */ - (usb_complete_t)auerchain_blocking_completion,0); - ret = auerchain_start_wait_urb (acp, urb, timeout, &length); - - usb_free_urb (urb); - kfree (dr); - - if (ret < 0) - return ret; - else - return length; -} - - -/*-------------------------------------------------------------------*/ -/* Buffer List helper functions */ - -/* free a single auerbuf */ -static void auerbuf_free (pauerbuf_t bp) -{ - if (bp->bufp) { - kfree (bp->bufp); - } - if (bp->dr) { - kfree (bp->dr); - } - if (bp->urbp) { - usb_free_urb (bp->urbp); - } - kfree (bp); -} - -/* free the buffers from an auerbuf list */ -static void auerbuf_free_list (struct list_head *q) -{ - struct list_head *tmp; - struct list_head *p; - pauerbuf_t bp; - - dbg ("auerbuf_free_list"); - for (p = q->next; p != q;) { - bp = list_entry (p, auerbuf_t, buff_list); - tmp = p->next; - list_del (p); - p = tmp; - auerbuf_free (bp); - } -} - -/* init the members of a list control block */ -static void auerbuf_init (pauerbufctl_t bcp) -{ - dbg ("auerbuf_init"); - spin_lock_init (&bcp->lock); - INIT_LIST_HEAD (&bcp->free_buff_list); - INIT_LIST_HEAD (&bcp->rec_buff_list); -} - -/* free all buffers from an auerbuf chain */ -static void auerbuf_free_buffers (pauerbufctl_t bcp) -{ - unsigned long flags; - dbg ("auerbuf_free_buffers"); - - spin_lock_irqsave (&bcp->lock, flags); - - auerbuf_free_list (&bcp->free_buff_list); - auerbuf_free_list (&bcp->rec_buff_list); - - spin_unlock_irqrestore (&bcp->lock, flags); -} - -/* setup a list of buffers */ -/* requirement: auerbuf_init() */ -static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned int bufsize) -{ - pauerbuf_t bep; - - dbg ("auerbuf_setup called with %d elements of %d bytes", numElements, bufsize); - - /* fill the list of free elements */ - for (;numElements; numElements--) { - bep = (pauerbuf_t) kmalloc (sizeof (auerbuf_t), GFP_KERNEL); - if (!bep) goto bl_fail; - memset (bep, 0, sizeof (auerbuf_t)); - bep->list = bcp; - INIT_LIST_HEAD (&bep->buff_list); - bep->bufp = (char *) kmalloc (bufsize, GFP_KERNEL); - if (!bep->bufp) goto bl_fail; - bep->dr = (struct usb_ctrlrequest *) kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL); - if (!bep->dr) goto bl_fail; - bep->urbp = usb_alloc_urb (0); - if (!bep->urbp) goto bl_fail; - list_add_tail (&bep->buff_list, &bcp->free_buff_list); - } - return 0; - -bl_fail:/* not enought memory. Free allocated elements */ - dbg ("auerbuf_setup: no more memory"); - auerbuf_free_buffers (bcp); - return -ENOMEM; -} - -/* insert a used buffer into the free list */ -static void auerbuf_releasebuf( pauerbuf_t bp) -{ - unsigned long flags; - pauerbufctl_t bcp = bp->list; - bp->retries = 0; - - dbg ("auerbuf_releasebuf called"); - spin_lock_irqsave (&bcp->lock, flags); - list_add_tail (&bp->buff_list, &bcp->free_buff_list); - spin_unlock_irqrestore (&bcp->lock, flags); -} - - -/*-------------------------------------------------------------------*/ -/* Completion handlers */ - -/* Values of urb->status or results of usb_submit_urb(): -0 Initial, OK --EINPROGRESS during submission until end --ENOENT if urb is unlinked --ETIMEDOUT Transfer timed out, NAK --ENOMEM Memory Overflow --ENODEV Specified USB-device or bus doesn't exist --ENXIO URB already queued --EINVAL a) Invalid transfer type specified (or not supported) - b) Invalid interrupt interval (0n256) --EAGAIN a) Specified ISO start frame too early - b) (using ISO-ASAP) Too much scheduled for the future wait some time and try again. --EFBIG Too much ISO frames requested (currently uhci900) --EPIPE Specified pipe-handle/Endpoint is already stalled --EMSGSIZE Endpoint message size is zero, do interface/alternate setting --EPROTO a) Bitstuff error - b) Unknown USB error --EILSEQ CRC mismatch --ENOSR Buffer error --EREMOTEIO Short packet detected --EXDEV ISO transfer only partially completed look at individual frame status for details --EINVAL ISO madness, if this happens: Log off and go home --EOVERFLOW babble -*/ - -/* check if a status code allows a retry */ -static int auerswald_status_retry (int status) -{ - switch (status) { - case 0: - case -ETIMEDOUT: - case -EOVERFLOW: - case -EAGAIN: - case -EPIPE: - case -EPROTO: - case -EILSEQ: - case -ENOSR: - case -EREMOTEIO: - return 1; /* do a retry */ - } - return 0; /* no retry possible */ -} - -/* Completion of asynchronous write block */ -static void auerchar_ctrlwrite_complete (struct urb * urb) -{ - pauerbuf_t bp = (pauerbuf_t) urb->context; - pauerswald_t cp = ((pauerswald_t)((char *)(bp->list)-(unsigned long)(&((pauerswald_t)0)->bufctl))); - dbg ("auerchar_ctrlwrite_complete called"); - - /* reuse the buffer */ - auerbuf_releasebuf (bp); - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); -} - -/* Completion handler for dummy retry packet */ -static void auerswald_ctrlread_wretcomplete (struct urb * urb) -{ - pauerbuf_t bp = (pauerbuf_t) urb->context; - pauerswald_t cp; - int ret; - dbg ("auerswald_ctrlread_wretcomplete called"); - dbg ("complete with status: %d", urb->status); - cp = ((pauerswald_t)((char *)(bp->list)-(unsigned long)(&((pauerswald_t)0)->bufctl))); - - /* check if it is possible to advance */ - if (!auerswald_status_retry (urb->status) || !cp->usbdev) { - /* reuse the buffer */ - err ("control dummy: transmission error %d, can not retry", urb->status); - auerbuf_releasebuf (bp); - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); - return; - } - - /* fill the control message */ - bp->dr->bRequestType = AUT_RREQ; - bp->dr->bRequest = AUV_RBLOCK; - bp->dr->wLength = bp->dr->wValue; /* temporary stored */ - bp->dr->wValue = cpu_to_le16 (1); /* Retry Flag */ - /* bp->dr->index = channel id; remains */ - FILL_CONTROL_URB (bp->urbp, cp->usbdev, usb_rcvctrlpipe (cp->usbdev, 0), - (unsigned char*)bp->dr, bp->bufp, le16_to_cpu (bp->dr->wLength), - (usb_complete_t)auerswald_ctrlread_complete,bp); - - /* submit the control msg as next paket */ - ret = auerchain_submit_urb_list (&cp->controlchain, bp->urbp, 1); - if (ret) { - dbg ("auerswald_ctrlread_complete: nonzero result of auerchain_submit_urb_list %d", ret); - bp->urbp->status = ret; - auerswald_ctrlread_complete (bp->urbp); - } -} - -/* completion handler for receiving of control messages */ -static void auerswald_ctrlread_complete (struct urb * urb) -{ - unsigned int serviceid; - pauerswald_t cp; - pauerscon_t scp; - pauerbuf_t bp = (pauerbuf_t) urb->context; - int ret; - dbg ("auerswald_ctrlread_complete called"); - - cp = ((pauerswald_t)((char *)(bp->list)-(unsigned long)(&((pauerswald_t)0)->bufctl))); - - /* check if there is valid data in this urb */ - if (urb->status) { - dbg ("complete with non-zero status: %d", urb->status); - /* should we do a retry? */ - if (!auerswald_status_retry (urb->status) - || !cp->usbdev - || (cp->version < AUV_RETRY) - || (bp->retries >= AU_RETRIES)) { - /* reuse the buffer */ - err ("control read: transmission error %d, can not retry", urb->status); - auerbuf_releasebuf (bp); - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); - return; - } - bp->retries++; - dbg ("Retry count = %d", bp->retries); - /* send a long dummy control-write-message to allow device firmware to react */ - bp->dr->bRequestType = AUT_WREQ; - bp->dr->bRequest = AUV_DUMMY; - bp->dr->wValue = bp->dr->wLength; /* temporary storage */ - // bp->dr->index channel ID remains - bp->dr->wLength = cpu_to_le16 (32); /* >= 8 bytes */ - FILL_CONTROL_URB (bp->urbp, cp->usbdev, usb_sndctrlpipe (cp->usbdev, 0), - (unsigned char*)bp->dr, bp->bufp, 32, - (usb_complete_t)auerswald_ctrlread_wretcomplete,bp); - - /* submit the control msg as next paket */ - ret = auerchain_submit_urb_list (&cp->controlchain, bp->urbp, 1); - if (ret) { - dbg ("auerswald_ctrlread_complete: nonzero result of auerchain_submit_urb_list %d", ret); - bp->urbp->status = ret; - auerswald_ctrlread_wretcomplete (bp->urbp); - } - return; - } - - /* get the actual bytecount (incl. headerbyte) */ - bp->len = urb->actual_length; - serviceid = bp->bufp[0] & AUH_TYPEMASK; - dbg ("Paket with serviceid %d and %d bytes received", serviceid, bp->len); - - /* dispatch the paket */ - scp = cp->services[serviceid]; - if (scp) { - /* look, Ma, a listener! */ - scp->dispatch (scp, bp); - } - - /* release the paket */ - auerbuf_releasebuf (bp); - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); -} - -/*-------------------------------------------------------------------*/ -/* Handling of Interrupt Endpoint */ -/* This interrupt Endpoint is used to inform the host about waiting - messages from the USB device. -*/ -/* int completion handler. */ -static void auerswald_int_complete (struct urb * urb) -{ - unsigned long flags; - unsigned int channelid; - unsigned int bytecount; - int ret; - pauerbuf_t bp = NULL; - pauerswald_t cp = (pauerswald_t) urb->context; - - dbg ("auerswald_int_complete called"); - - /* do not respond to an error condition */ - if (urb->status != 0) { - dbg ("nonzero URB status = %d", urb->status); - return; - } - - /* check if all needed data was received */ - if (urb->actual_length < AU_IRQMINSIZE) { - dbg ("invalid data length received: %d bytes", urb->actual_length); - return; - } - - /* check the command code */ - if (cp->intbufp[0] != AU_IRQCMDID) { - dbg ("invalid command received: %d", cp->intbufp[0]); - return; - } - - /* check the command type */ - if (cp->intbufp[1] != AU_BLOCKRDY) { - dbg ("invalid command type received: %d", cp->intbufp[1]); - return; - } - - /* now extract the information */ - channelid = cp->intbufp[2]; - bytecount = le16_to_cpup (&cp->intbufp[3]); - - /* check the channel id */ - if (channelid >= AUH_TYPESIZE) { - dbg ("invalid channel id received: %d", channelid); - return; - } - - /* check the byte count */ - if (bytecount > (cp->maxControlLength+AUH_SIZE)) { - dbg ("invalid byte count received: %d", bytecount); - return; - } - dbg ("Service Channel = %d", channelid); - dbg ("Byte Count = %d", bytecount); - - /* get a buffer for the next data paket */ - spin_lock_irqsave (&cp->bufctl.lock, flags); - if (!list_empty (&cp->bufctl.free_buff_list)) { - /* yes: get the entry */ - struct list_head *tmp = cp->bufctl.free_buff_list.next; - list_del (tmp); - bp = list_entry (tmp, auerbuf_t, buff_list); - } - spin_unlock_irqrestore (&cp->bufctl.lock, flags); - - /* if no buffer available: skip it */ - if (!bp) { - dbg ("auerswald_int_complete: no data buffer available"); - /* can we do something more? - This is a big problem: if this int packet is ignored, the - device will wait forever and not signal any more data. - The only real solution is: having enought buffers! - Or perhaps temporary disabling the int endpoint? - */ - return; - } - - /* fill the control message */ - bp->dr->bRequestType = AUT_RREQ; - bp->dr->bRequest = AUV_RBLOCK; - bp->dr->wValue = cpu_to_le16 (0); - bp->dr->wIndex = cpu_to_le16 (channelid | AUH_DIRECT | AUH_UNSPLIT); - bp->dr->wLength = cpu_to_le16 (bytecount); - FILL_CONTROL_URB (bp->urbp, cp->usbdev, usb_rcvctrlpipe (cp->usbdev, 0), - (unsigned char*)bp->dr, bp->bufp, bytecount, - (usb_complete_t)auerswald_ctrlread_complete,bp); - - /* submit the control msg */ - ret = auerchain_submit_urb (&cp->controlchain, bp->urbp); - if (ret) { - dbg ("auerswald_int_complete: nonzero result of auerchain_submit_urb %d", ret); - bp->urbp->status = ret; - auerswald_ctrlread_complete( bp->urbp); - /* here applies the same problem as above: device locking! */ - } -} - -/* int memory deallocation - NOTE: no mutex please! -*/ -static void auerswald_int_free (pauerswald_t cp) -{ - if (cp->inturbp) { - usb_free_urb (cp->inturbp); - cp->inturbp = NULL; - } - if (cp->intbufp) { - kfree (cp->intbufp); - cp->intbufp = NULL; - } -} - -/* This function is called to activate the interrupt - endpoint. This function returns 0 if successfull or an error code. - NOTE: no mutex please! -*/ -static int auerswald_int_open (pauerswald_t cp) -{ - int ret; - struct usb_endpoint_descriptor *ep; - int irqsize; - dbg ("auerswald_int_open"); - - ep = usb_epnum_to_ep_desc (cp->usbdev, USB_DIR_IN | AU_IRQENDP); - if (!ep) { - ret = -EFAULT; - goto intoend; - } - irqsize = ep->wMaxPacketSize; - cp->irqsize = irqsize; - - /* allocate the urb and data buffer */ - if (!cp->inturbp) { - cp->inturbp = usb_alloc_urb (0); - if (!cp->inturbp) { - ret = -ENOMEM; - goto intoend; - } - } - if (!cp->intbufp) { - cp->intbufp = (char *) kmalloc (irqsize, GFP_KERNEL); - if (!cp->intbufp) { - ret = -ENOMEM; - goto intoend; - } - } - /* setup urb */ - FILL_INT_URB (cp->inturbp, cp->usbdev, usb_rcvintpipe (cp->usbdev,AU_IRQENDP), cp->intbufp, irqsize, auerswald_int_complete, cp, ep->bInterval); - /* start the urb */ - cp->inturbp->status = 0; /* needed! */ - ret = usb_submit_urb (cp->inturbp); - -intoend: - if (ret < 0) { - /* activation of interrupt endpoint has failed. Now clean up. */ - dbg ("auerswald_int_open: activation of int endpoint failed"); - - /* deallocate memory */ - auerswald_int_free (cp); - } - return ret; -} - -/* This function is called to deactivate the interrupt - endpoint. This function returns 0 if successfull or an error code. - NOTE: no mutex please! -*/ -static int auerswald_int_release (pauerswald_t cp) -{ - int ret = 0; - dbg ("auerswald_int_release"); - - /* stop the int endpoint */ - if (cp->inturbp) { - ret = usb_unlink_urb (cp->inturbp); - if (ret) - dbg ("nonzero int unlink result received: %d", ret); - } - - /* deallocate memory */ - auerswald_int_free (cp); - - return ret; -} - -/* --------------------------------------------------------------------- */ -/* Helper functions */ - -/* wake up waiting readers */ -static void auerchar_disconnect (pauerscon_t scp) -{ - pauerchar_t ccp = ((pauerchar_t)((char *)(scp)-(unsigned long)(&((pauerchar_t)0)->scontext))); - dbg ("auerchar_disconnect called"); - ccp->removed = 1; - wake_up (&ccp->readwait); -} - - -/* dispatch a read paket to a waiting character device */ -static void auerchar_ctrlread_dispatch (pauerscon_t scp, pauerbuf_t bp) -{ - unsigned long flags; - pauerchar_t ccp; - pauerbuf_t newbp = NULL; - char * charp; - dbg ("auerchar_ctrlread_dispatch called"); - ccp = ((pauerchar_t)((char *)(scp)-(unsigned long)(&((pauerchar_t)0)->scontext))); - - /* get a read buffer from character device context */ - spin_lock_irqsave (&ccp->bufctl.lock, flags); - if (!list_empty (&ccp->bufctl.free_buff_list)) { - /* yes: get the entry */ - struct list_head *tmp = ccp->bufctl.free_buff_list.next; - list_del (tmp); - newbp = list_entry (tmp, auerbuf_t, buff_list); - } - spin_unlock_irqrestore (&ccp->bufctl.lock, flags); - - if (!newbp) { - dbg ("No read buffer available, discard paket!"); - return; /* no buffer, no dispatch */ - } - - /* copy information to new buffer element - (all buffers have the same length) */ - charp = newbp->bufp; - newbp->bufp = bp->bufp; - bp->bufp = charp; - newbp->len = bp->len; - - /* insert new buffer in read list */ - spin_lock_irqsave (&ccp->bufctl.lock, flags); - list_add_tail (&newbp->buff_list, &ccp->bufctl.rec_buff_list); - spin_unlock_irqrestore (&ccp->bufctl.lock, flags); - dbg ("read buffer appended to rec_list"); - - /* wake up pending synchronous reads */ - wake_up (&ccp->readwait); -} - - -/* Delete an auerswald driver context */ -static void auerswald_delete( pauerswald_t cp) -{ - dbg( "auerswald_delete"); - if (cp == NULL) return; - - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); - - /* Cleaning up */ - auerswald_int_release (cp); - auerchain_free (&cp->controlchain); - auerbuf_free_buffers (&cp->bufctl); - - /* release the memory */ - kfree( cp); -} - - -/* Delete an auerswald character context */ -static void auerchar_delete( pauerchar_t ccp) -{ - dbg ("auerchar_delete"); - if (ccp == NULL) return; - - /* wake up pending synchronous reads */ - ccp->removed = 1; - wake_up (&ccp->readwait); - - /* remove the read buffer */ - if (ccp->readbuf) { - auerbuf_releasebuf (ccp->readbuf); - ccp->readbuf = NULL; - } - - /* remove the character buffers */ - auerbuf_free_buffers (&ccp->bufctl); - - /* release the memory */ - kfree( ccp); -} - - -/* add a new service to the device - scp->id must be set! - return: 0 if OK, else error code -*/ -static int auerswald_addservice (pauerswald_t cp, pauerscon_t scp) -{ - int ret; - - /* is the device available? */ - if (!cp->usbdev) { - dbg ("usbdev == NULL"); - return -EIO; /*no: can not add a service, sorry*/ - } - - /* is the service available? */ - if (cp->services[scp->id]) { - dbg ("service is busy"); - return -EBUSY; - } - - /* device is available, service is free */ - cp->services[scp->id] = scp; - - /* register service in device */ - ret = auerchain_control_msg( - &cp->controlchain, /* pointer to control chain */ - cp->usbdev, /* pointer to device */ - usb_sndctrlpipe (cp->usbdev, 0), /* pipe to control endpoint */ - AUV_CHANNELCTL, /* USB message request value */ - AUT_WREQ, /* USB message request type value */ - 0x01, /* open USB message value */ - scp->id, /* USB message index value */ - NULL, /* pointer to the data to send */ - 0, /* length in bytes of the data to send */ - HZ * 2); /* time to wait for the message to complete before timing out */ - if (ret < 0) { - dbg ("auerswald_addservice: auerchain_control_msg returned error code %d", ret); - /* undo above actions */ - cp->services[scp->id] = NULL; - return ret; - } - - dbg ("auerswald_addservice: channel open OK"); - return 0; -} - - -/* remove a service from the device - scp->id must be set! */ -static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp) -{ - dbg ("auerswald_removeservice called"); - - /* check if we have a service allocated */ - if (scp->id == AUH_UNASSIGNED) return; - - /* If there is a device: close the channel */ - if (cp->usbdev) { - /* Close the service channel inside the device */ - int ret = auerchain_control_msg( - &cp->controlchain, /* pointer to control chain */ - cp->usbdev, /* pointer to device */ - usb_sndctrlpipe (cp->usbdev, 0), /* pipe to control endpoint */ - AUV_CHANNELCTL, /* USB message request value */ - AUT_WREQ, /* USB message request type value */ - 0x00, // close /* USB message value */ - scp->id, /* USB message index value */ - NULL, /* pointer to the data to send */ - 0, /* length in bytes of the data to send */ - HZ * 2); /* time to wait for the message to complete before timing out */ - if (ret < 0) { - dbg ("auerswald_removeservice: auerchain_control_msg returned error code %d", ret); - } - else { - dbg ("auerswald_removeservice: channel close OK"); - } - } - - /* remove the service from the device */ - cp->services[scp->id] = NULL; - scp->id = AUH_UNASSIGNED; -} - - -/* --------------------------------------------------------------------- */ -/* Char device functions */ - -/* Open a new character device */ -static int auerchar_open (struct inode *inode, struct file *file) -{ - int dtindex = MINOR(inode->i_rdev) - AUER_MINOR_BASE; - pauerswald_t cp = NULL; - pauerchar_t ccp = NULL; - int ret; - - /* minor number in range? */ - if ((dtindex < 0) || (dtindex >= AUER_MAX_DEVICES)) { - return -ENODEV; - } - /* usb device available? */ - if (down_interruptible (&dev_table_mutex)) { - return -ERESTARTSYS; - } - cp = dev_table[dtindex]; - if (cp == NULL) { - up (&dev_table_mutex); - return -ENODEV; - } - if (down_interruptible (&cp->mutex)) { - up (&dev_table_mutex); - return -ERESTARTSYS; - } - up (&dev_table_mutex); - - /* we have access to the device. Now lets allocate memory */ - ccp = (pauerchar_t) kmalloc(sizeof(auerchar_t), GFP_KERNEL); - if (ccp == NULL) { - err ("out of memory"); - ret = -ENOMEM; - goto ofail; - } - - /* Initialize device descriptor */ - memset( ccp, 0, sizeof(auerchar_t)); - init_MUTEX( &ccp->mutex); - init_MUTEX( &ccp->readmutex); - auerbuf_init (&ccp->bufctl); - ccp->scontext.id = AUH_UNASSIGNED; - ccp->scontext.dispatch = auerchar_ctrlread_dispatch; - ccp->scontext.disconnect = auerchar_disconnect; - init_waitqueue_head (&ccp->readwait); - - ret = auerbuf_setup (&ccp->bufctl, AU_RBUFFERS, cp->maxControlLength+AUH_SIZE); - if (ret) { - goto ofail; - } - - cp->open_count++; - ccp->auerdev = cp; - dbg("open %s as /dev/usb/%s", cp->dev_desc, cp->name); - up (&cp->mutex); - - /* file IO stuff */ - file->f_pos = 0; - file->private_data = ccp; - return 0; - - /* Error exit */ -ofail: up (&cp->mutex); - auerchar_delete (ccp); - return ret; -} - - -/* IOCTL functions */ -static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - pauerchar_t ccp = (pauerchar_t) file->private_data; - int ret = 0; - audevinfo_t devinfo; - pauerswald_t cp = NULL; - unsigned int u; - dbg ("ioctl"); - - /* get the mutexes */ - if (down_interruptible (&ccp->mutex)) { - return -ERESTARTSYS; - } - cp = ccp->auerdev; - if (!cp) { - up (&ccp->mutex); - return -ENODEV; - } - if (down_interruptible (&cp->mutex)) { - up(&ccp->mutex); - return -ERESTARTSYS; - } - - /* Check for removal */ - if (!cp->usbdev) { - up(&cp->mutex); - up(&ccp->mutex); - return -ENODEV; - } - - switch (cmd) { - - /* return != 0 if Transmitt channel ready to send */ - case IOCTL_AU_TXREADY: - dbg ("IOCTL_AU_TXREADY"); - u = ccp->auerdev - && (ccp->scontext.id != AUH_UNASSIGNED) - && !list_empty (&cp->bufctl.free_buff_list); - ret = put_user (u, (unsigned int *) arg); - break; - - /* return != 0 if connected to a service channel */ - case IOCTL_AU_CONNECT: - dbg ("IOCTL_AU_CONNECT"); - u = (ccp->scontext.id != AUH_UNASSIGNED); - ret = put_user (u, (unsigned int *) arg); - break; - - /* return != 0 if Receive Data available */ - case IOCTL_AU_RXAVAIL: - dbg ("IOCTL_AU_RXAVAIL"); - if (ccp->scontext.id == AUH_UNASSIGNED) { - ret = -EIO; - break; - } - u = 0; /* no data */ - if (ccp->readbuf) { - int restlen = ccp->readbuf->len - ccp->readoffset; - if (restlen > 0) u = 1; - } - if (!u) { - if (!list_empty (&ccp->bufctl.rec_buff_list)) { - u = 1; - } - } - ret = put_user (u, (unsigned int *) arg); - break; - - /* return the max. buffer length for the device */ - case IOCTL_AU_BUFLEN: - dbg ("IOCTL_AU_BUFLEN"); - u = cp->maxControlLength; - ret = put_user (u, (unsigned int *) arg); - break; - - /* requesting a service channel */ - case IOCTL_AU_SERVREQ: - dbg ("IOCTL_AU_SERVREQ"); - /* requesting a service means: release the previous one first */ - auerswald_removeservice (cp, &ccp->scontext); - /* get the channel number */ - ret = get_user (u, (unsigned int *) arg); - if (ret) { - break; - } - if ((u < AUH_FIRSTUSERCH) || (u >= AUH_TYPESIZE)) { - ret = -EIO; - break; - } - dbg ("auerchar service request parameters are ok"); - ccp->scontext.id = u; - - /* request the service now */ - ret = auerswald_addservice (cp, &ccp->scontext); - if (ret) { - /* no: revert service entry */ - ccp->scontext.id = AUH_UNASSIGNED; - } - break; - - /* get a string descriptor for the device */ - case IOCTL_AU_DEVINFO: - dbg ("IOCTL_AU_DEVINFO"); - if (copy_from_user (&devinfo, (void *) arg, sizeof (audevinfo_t))) { - ret = -EFAULT; - break; - } - u = strlen(cp->dev_desc)+1; - if (u > devinfo.bsize) { - u = devinfo.bsize; - } - ret = copy_to_user(devinfo.buf, cp->dev_desc, u)?-EFAULT:0; - break; - - /* get the max. string descriptor length */ - case IOCTL_AU_SLEN: - dbg ("IOCTL_AU_SLEN"); - u = AUSI_DLEN; - ret = put_user (u, (unsigned int *) arg); - break; - - default: - dbg ("IOCTL_AU_UNKNOWN"); - ret = -ENOIOCTLCMD; - break; - } - /* release the mutexes */ - up(&cp->mutex); - up(&ccp->mutex); - return ret; -} - - -/* Seek is not supported */ -static loff_t auerchar_llseek (struct file *file, loff_t offset, int origin) -{ - dbg ("auerchar_seek"); - return -ESPIPE; -} - - -/* Read data from the device */ -static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t * ppos) -{ - unsigned long flags; - pauerchar_t ccp = (pauerchar_t) file->private_data; - pauerbuf_t bp = NULL; - wait_queue_t wait; - - dbg ("auerchar_read"); - - /* Error checking */ - if (!ccp) - return -EIO; - if (*ppos) - return -ESPIPE; - if (count == 0) - return 0; - - /* get the mutex */ - if (down_interruptible (&ccp->mutex)) - return -ERESTARTSYS; - - /* Can we expect to read something? */ - if (ccp->scontext.id == AUH_UNASSIGNED) { - up (&ccp->mutex); - return -EIO; - } - - /* only one reader per device allowed */ - if (down_interruptible (&ccp->readmutex)) { - up (&ccp->mutex); - return -ERESTARTSYS; - } - - /* read data from readbuf, if available */ -doreadbuf: - bp = ccp->readbuf; - if (bp) { - /* read the maximum bytes */ - int restlen = bp->len - ccp->readoffset; - if (restlen < 0) - restlen = 0; - if (count > restlen) - count = restlen; - if (count) { - if (copy_to_user (buf, bp->bufp+ccp->readoffset, count)) { - dbg ("auerswald_read: copy_to_user failed"); - up (&ccp->readmutex); - up (&ccp->mutex); - return -EFAULT; - } - } - /* advance the read offset */ - ccp->readoffset += count; - restlen -= count; - // reuse the read buffer - if (restlen <= 0) { - auerbuf_releasebuf (bp); - ccp->readbuf = NULL; - } - /* return with number of bytes read */ - if (count) { - up (&ccp->readmutex); - up (&ccp->mutex); - return count; - } - } - - /* a read buffer is not available. Try to get the next data block. */ -doreadlist: - /* Preparing for sleep */ - init_waitqueue_entry (&wait, current); - set_current_state (TASK_INTERRUPTIBLE); - add_wait_queue (&ccp->readwait, &wait); - - bp = NULL; - spin_lock_irqsave (&ccp->bufctl.lock, flags); - if (!list_empty (&ccp->bufctl.rec_buff_list)) { - /* yes: get the entry */ - struct list_head *tmp = ccp->bufctl.rec_buff_list.next; - list_del (tmp); - bp = list_entry (tmp, auerbuf_t, buff_list); - } - spin_unlock_irqrestore (&ccp->bufctl.lock, flags); - - /* have we got data? */ - if (bp) { - ccp->readbuf = bp; - ccp->readoffset = AUH_SIZE; /* for headerbyte */ - set_current_state (TASK_RUNNING); - remove_wait_queue (&ccp->readwait, &wait); - goto doreadbuf; /* now we can read! */ - } - - /* no data available. Should we wait? */ - if (file->f_flags & O_NONBLOCK) { - dbg ("No read buffer available, returning -EAGAIN"); - set_current_state (TASK_RUNNING); - remove_wait_queue (&ccp->readwait, &wait); - up (&ccp->readmutex); - up (&ccp->mutex); - return -EAGAIN; /* nonblocking, no data available */ - } - - /* yes, we should wait! */ - up (&ccp->mutex); /* allow other operations while we wait */ - schedule(); - remove_wait_queue (&ccp->readwait, &wait); - if (signal_pending (current)) { - /* waked up by a signal */ - up (&ccp->readmutex); - return -ERESTARTSYS; - } - - /* Anything left to read? */ - if ((ccp->scontext.id == AUH_UNASSIGNED) || ccp->removed) { - up (&ccp->readmutex); - return -EIO; - } - - if (down_interruptible (&ccp->mutex)) { - up (&ccp->readmutex); - return -ERESTARTSYS; - } - - /* try to read the incomming data again */ - goto doreadlist; -} - - -/* Write a data block into the right service channel of the device */ -static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, loff_t *ppos) -{ - pauerchar_t ccp = (pauerchar_t) file->private_data; - pauerswald_t cp = NULL; - pauerbuf_t bp; - unsigned long flags; - int ret; - wait_queue_t wait; - - dbg ("auerchar_write %d bytes", len); - - /* Error checking */ - if (!ccp) - return -EIO; - if (*ppos) - return -ESPIPE; - if (len == 0) - return 0; - -write_again: - /* get the mutex */ - if (down_interruptible (&ccp->mutex)) - return -ERESTARTSYS; - - /* Can we expect to write something? */ - if (ccp->scontext.id == AUH_UNASSIGNED) { - up (&ccp->mutex); - return -EIO; - } - - cp = ccp->auerdev; - if (!cp) { - up (&ccp->mutex); - return -ERESTARTSYS; - } - if (down_interruptible (&cp->mutex)) { - up (&ccp->mutex); - return -ERESTARTSYS; - } - if (!cp->usbdev) { - up (&cp->mutex); - up (&ccp->mutex); - return -EIO; - } - /* Prepare for sleep */ - init_waitqueue_entry (&wait, current); - set_current_state (TASK_INTERRUPTIBLE); - add_wait_queue (&cp->bufferwait, &wait); - - /* Try to get a buffer from the device pool. - We can't use a buffer from ccp->bufctl because the write - command will last beond a release() */ - bp = NULL; - spin_lock_irqsave (&cp->bufctl.lock, flags); - if (!list_empty (&cp->bufctl.free_buff_list)) { - /* yes: get the entry */ - struct list_head *tmp = cp->bufctl.free_buff_list.next; - list_del (tmp); - bp = list_entry (tmp, auerbuf_t, buff_list); - } - spin_unlock_irqrestore (&cp->bufctl.lock, flags); - - /* are there any buffers left? */ - if (!bp) { - up (&cp->mutex); - up (&ccp->mutex); - - /* NONBLOCK: don't wait */ - if (file->f_flags & O_NONBLOCK) { - set_current_state (TASK_RUNNING); - remove_wait_queue (&cp->bufferwait, &wait); - return -EAGAIN; - } - - /* BLOCKING: wait */ - schedule(); - remove_wait_queue (&cp->bufferwait, &wait); - if (signal_pending (current)) { - /* waked up by a signal */ - return -ERESTARTSYS; - } - goto write_again; - } else { - set_current_state (TASK_RUNNING); - remove_wait_queue (&cp->bufferwait, &wait); - } - - /* protect against too big write requests */ - if (len > cp->maxControlLength) len = cp->maxControlLength; - - /* Fill the buffer */ - if (copy_from_user ( bp->bufp+AUH_SIZE, buf, len)) { - dbg ("copy_from_user failed"); - auerbuf_releasebuf (bp); - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); - up (&cp->mutex); - up (&ccp->mutex); - return -EIO; - } - - /* set the header byte */ - *(bp->bufp) = ccp->scontext.id | AUH_DIRECT | AUH_UNSPLIT; - - /* Set the transfer Parameters */ - bp->len = len+AUH_SIZE; - bp->dr->bRequestType = AUT_WREQ; - bp->dr->bRequest = AUV_WBLOCK; - bp->dr->wValue = cpu_to_le16 (0); - bp->dr->wIndex = cpu_to_le16 (ccp->scontext.id | AUH_DIRECT | AUH_UNSPLIT); - bp->dr->wLength = cpu_to_le16 (len+AUH_SIZE); - FILL_CONTROL_URB (bp->urbp, cp->usbdev, usb_sndctrlpipe (cp->usbdev, 0), - (unsigned char*)bp->dr, bp->bufp, len+AUH_SIZE, - auerchar_ctrlwrite_complete, bp); - /* up we go */ - ret = auerchain_submit_urb (&cp->controlchain, bp->urbp); - up (&cp->mutex); - if (ret) { - dbg ("auerchar_write: nonzero result of auerchain_submit_urb %d", ret); - auerbuf_releasebuf (bp); - /* Wake up all processes waiting for a buffer */ - wake_up (&cp->bufferwait); - up (&ccp->mutex); - return -EIO; - } - else { - dbg ("auerchar_write: Write OK"); - up (&ccp->mutex); - return len; - } -} - - -/* Close a character device */ -static int auerchar_release (struct inode *inode, struct file *file) -{ - pauerchar_t ccp = (pauerchar_t) file->private_data; - pauerswald_t cp; - dbg("release"); - - /* get the mutexes */ - if (down_interruptible (&ccp->mutex)) { - return -ERESTARTSYS; - } - cp = ccp->auerdev; - if (cp) { - if (down_interruptible (&cp->mutex)) { - up (&ccp->mutex); - return -ERESTARTSYS; - } - /* remove an open service */ - auerswald_removeservice (cp, &ccp->scontext); - /* detach from device */ - if ((--cp->open_count <= 0) && (cp->usbdev == NULL)) { - /* usb device waits for removal */ - up (&cp->mutex); - auerswald_delete (cp); - } else { - up (&cp->mutex); - } - cp = NULL; - ccp->auerdev = NULL; - } - up (&ccp->mutex); - auerchar_delete (ccp); - - return 0; -} - - -/*----------------------------------------------------------------------*/ -/* File operation structure */ -static struct file_operations auerswald_fops = -{ - owner: THIS_MODULE, - llseek: auerchar_llseek, - read: auerchar_read, - write: auerchar_write, - ioctl: auerchar_ioctl, - open: auerchar_open, - release: auerchar_release, -}; - - -/* --------------------------------------------------------------------- */ -/* Special USB driver functions */ - -/* Probe if this driver wants to serve an USB device - - This entry point is called whenever a new device is attached to the bus. - Then the device driver has to create a new instance of its internal data - structures for the new device. - - The dev argument specifies the device context, which contains pointers - to all USB descriptors. The interface argument specifies the interface - number. If a USB driver wants to bind itself to a particular device and - interface it has to return a pointer. This pointer normally references - the device driver's context structure. - - Probing normally is done by checking the vendor and product identifications - or the class and subclass definitions. If they match the interface number - is compared with the ones supported by the driver. When probing is done - class based it might be necessary to parse some more USB descriptors because - the device properties can differ in a wide range. -*/ -static void *auerswald_probe (struct usb_device *usbdev, unsigned int ifnum, - const struct usb_device_id *id) -{ - pauerswald_t cp = NULL; - DECLARE_WAIT_QUEUE_HEAD (wqh); - unsigned int dtindex; - unsigned int u = 0; - char *pbuf; - int ret; - - dbg ("probe: vendor id 0x%x, device id 0x%x ifnum:%d", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); - - /* See if the device offered us matches that we can accept */ - if (usbdev->descriptor.idVendor != ID_AUERSWALD) return NULL; - - /* we use only the first -and only- interface */ - if (ifnum != 0) return NULL; - - /* prevent module unloading while sleeping */ - MOD_INC_USE_COUNT; - - /* allocate memory for our device and intialize it */ - cp = kmalloc (sizeof(auerswald_t), GFP_KERNEL); - if (cp == NULL) { - err ("out of memory"); - goto pfail; - } - - /* Initialize device descriptor */ - memset (cp, 0, sizeof(auerswald_t)); - init_MUTEX (&cp->mutex); - cp->usbdev = usbdev; - auerchain_init (&cp->controlchain); - auerbuf_init (&cp->bufctl); - init_waitqueue_head (&cp->bufferwait); - - /* find a free slot in the device table */ - down (&dev_table_mutex); - for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) { - if (dev_table[dtindex] == NULL) - break; - } - if ( dtindex >= AUER_MAX_DEVICES) { - err ("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES); - up (&dev_table_mutex); - goto pfail; - } - - /* Give the device a name */ - sprintf (cp->name, AU_PREFIX "%d", dtindex); - - /* Store the index */ - cp->dtindex = dtindex; - dev_table[dtindex] = cp; - up (&dev_table_mutex); - - /* initialize the devfs node for this device and register it */ - cp->devfs = devfs_register (usb_devfs_handle, cp->name, - DEVFS_FL_DEFAULT, USB_MAJOR, - AUER_MINOR_BASE + dtindex, - S_IFCHR | S_IRUGO | S_IWUGO, - &auerswald_fops, NULL); - - /* Get the usb version of the device */ - cp->version = cp->usbdev->descriptor.bcdDevice; - dbg ("Version is %X", cp->version); - - /* allow some time to settle the device */ - sleep_on_timeout (&wqh, HZ / 3 ); - - /* Try to get a suitable textual description of the device */ - /* Device name:*/ - ret = usb_string( cp->usbdev, AUSI_DEVICE, cp->dev_desc, AUSI_DLEN-1); - if (ret >= 0) { - u += ret; - /* Append Serial Number */ - memcpy(&cp->dev_desc[u], ",Ser# ", 6); - u += 6; - ret = usb_string( cp->usbdev, AUSI_SERIALNR, &cp->dev_desc[u], AUSI_DLEN-u-1); - if (ret >= 0) { - u += ret; - /* Append subscriber number */ - memcpy(&cp->dev_desc[u], ", ", 2); - u += 2; - ret = usb_string( cp->usbdev, AUSI_MSN, &cp->dev_desc[u], AUSI_DLEN-u-1); - if (ret >= 0) { - u += ret; - } - } - } - cp->dev_desc[u] = '\0'; - info("device is a %s", cp->dev_desc); - - /* get the maximum allowed control transfer length */ - pbuf = (char *) kmalloc (2, GFP_KERNEL); /* use an allocated buffer because of urb target */ - if (!pbuf) { - err( "out of memory"); - goto pfail; - } - ret = usb_control_msg(cp->usbdev, /* pointer to device */ - usb_rcvctrlpipe( cp->usbdev, 0 ), /* pipe to control endpoint */ - AUV_GETINFO, /* USB message request value */ - AUT_RREQ, /* USB message request type value */ - 0, /* USB message value */ - AUDI_MBCTRANS, /* USB message index value */ - pbuf, /* pointer to the receive buffer */ - 2, /* length of the buffer */ - HZ * 2); /* time to wait for the message to complete before timing out */ - if (ret == 2) { - cp->maxControlLength = le16_to_cpup(pbuf); - kfree(pbuf); - dbg("setup: max. allowed control transfersize is %d bytes", cp->maxControlLength); - } else { - kfree(pbuf); - err("setup: getting max. allowed control transfer length failed with error %d", ret); - goto pfail; - } - - /* allocate a chain for the control messages */ - if (auerchain_setup (&cp->controlchain, AUCH_ELEMENTS)) { - err ("out of memory"); - goto pfail; - } - - /* allocate buffers for control messages */ - if (auerbuf_setup (&cp->bufctl, AU_RBUFFERS, cp->maxControlLength+AUH_SIZE)) { - err ("out of memory"); - goto pfail; - } - - /* start the interrupt endpoint */ - if (auerswald_int_open (cp)) { - err ("int endpoint failed"); - goto pfail; - } - - /* all OK */ - return cp; - - /* Error exit: clean up the memory */ -pfail: auerswald_delete (cp); - MOD_DEC_USE_COUNT; - return NULL; -} - - -/* Disconnect driver from a served device - - This function is called whenever a device which was served by this driver - is disconnected. - - The argument dev specifies the device context and the driver_context - returns a pointer to the previously registered driver_context of the - probe function. After returning from the disconnect function the USB - framework completly deallocates all data structures associated with - this device. So especially the usb_device structure must not be used - any longer by the usb driver. -*/ -static void auerswald_disconnect (struct usb_device *usbdev, void *driver_context) -{ - pauerswald_t cp = (pauerswald_t) driver_context; - unsigned int u; - - down (&cp->mutex); - info ("device /dev/usb/%s now disconnecting", cp->name); - - /* remove from device table */ - /* Nobody can open() this device any more */ - down (&dev_table_mutex); - dev_table[cp->dtindex] = NULL; - up (&dev_table_mutex); - - /* remove our devfs node */ - /* Nobody can see this device any more */ - devfs_unregister (cp->devfs); - - /* Stop the interrupt endpoint */ - auerswald_int_release (cp); - - /* remove the control chain allocated in auerswald_probe - This has the benefit of - a) all pending (a)synchronous urbs are unlinked - b) all buffers dealing with urbs are reclaimed - */ - auerchain_free (&cp->controlchain); - - if (cp->open_count == 0) { - /* nobody is using this device. So we can clean up now */ - up (&cp->mutex);/* up() is possible here because no other task - can open the device (see above). I don't want - to kfree() a locked mutex. */ - auerswald_delete (cp); - } else { - /* device is used. Remove the pointer to the - usb device (it's not valid any more). The last - release() will do the clean up */ - cp->usbdev = NULL; - up (&cp->mutex); - /* Terminate waiting writers */ - wake_up (&cp->bufferwait); - /* Inform all waiting readers */ - for ( u = 0; u < AUH_TYPESIZE; u++) { - pauerscon_t scp = cp->services[u]; - if (scp) scp->disconnect( scp); - } - } - - /* The device releases this module */ - MOD_DEC_USE_COUNT; -} - -/* Descriptor for the devices which are served by this driver. - NOTE: this struct is parsed by the usbmanager install scripts. - Don't change without caution! -*/ -static struct usb_device_id auerswald_ids [] = { - { USB_DEVICE (ID_AUERSWALD, 0x00C0) }, /* COMpact 2104 USB */ - { USB_DEVICE (ID_AUERSWALD, 0x00DB) }, /* COMpact 4410/2206 USB */ - { USB_DEVICE (ID_AUERSWALD, 0x00F1) }, /* Comfort 2000 System Telephone */ - { USB_DEVICE (ID_AUERSWALD, 0x00F2) }, /* Comfort 1200 System Telephone */ - { } /* Terminating entry */ -}; - -/* Standard module device table */ -MODULE_DEVICE_TABLE (usb, auerswald_ids); - -/* Standard usb driver struct */ -static struct usb_driver auerswald_driver = { - name: "auerswald", - probe: auerswald_probe, - disconnect: auerswald_disconnect, - fops: &auerswald_fops, - minor: AUER_MINOR_BASE, - id_table: auerswald_ids, -}; - - -/* --------------------------------------------------------------------- */ -/* Module loading/unloading */ - -/* Driver initialisation. Called after module loading. - NOTE: there is no concurrency at _init -*/ -static int __init auerswald_init (void) -{ - int result; - dbg ("init"); - - /* initialize the device table */ - memset (&dev_table, 0, sizeof(dev_table)); - init_MUTEX (&dev_table_mutex); - - /* register driver at the USB subsystem */ - result = usb_register (&auerswald_driver); - if (result < 0) { - err ("driver could not be registered"); - return -1; - } - return 0; -} - -/* Driver deinit. Called before module removal. - NOTE: there is no concurrency at _cleanup -*/ -static void __exit auerswald_cleanup (void) -{ - dbg ("cleanup"); - usb_deregister (&auerswald_driver); -} - -/* --------------------------------------------------------------------- */ -/* Linux device driver module description */ - -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_DESC); - -module_init (auerswald_init); -module_exit (auerswald_cleanup); - -/* --------------------------------------------------------------------- */ -