Commit f0b11874 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

patch from Wolfgang Mües <wmues@nexgo.de> for the usb auerswald.c driver:

  	- Code-Review from Oliver Neukum: remove SMP races.
  	- Added some wake_up calls after auerbuf_releasebuf to wake up tasks waiting
  	  for cp buffers.
  	- Change the module count handling to automatic (owner: THIS_MODULE).
parent 040fdeaf
...@@ -49,7 +49,7 @@ do { \ ...@@ -49,7 +49,7 @@ do { \
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
/* Version Information */ /* Version Information */
#define DRIVER_VERSION "0.9.9" #define DRIVER_VERSION "0.9.11"
#define DRIVER_AUTHOR "Wolfgang Mes <wmues@nexgo.de>" #define DRIVER_AUTHOR "Wolfgang Mes <wmues@nexgo.de>"
#define DRIVER_DESC "Auerswald PBX/System Telephone usb driver" #define DRIVER_DESC "Auerswald PBX/System Telephone usb driver"
...@@ -191,6 +191,13 @@ typedef struct auerchain ...@@ -191,6 +191,13 @@ typedef struct auerchain
struct list_head free_list; /* list of available elements */ struct list_head free_list; /* list of available elements */
} auerchain_t,*pauerchain_t; } 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 */ /* buffer element */
struct auerbufctl; /* forward */ struct auerbufctl; /* forward */
...@@ -330,7 +337,7 @@ static void auerchain_complete (struct urb * urb) ...@@ -330,7 +337,7 @@ static void auerchain_complete (struct urb * urb)
urb = acep->urbp; urb = acep->urbp;
dbg ("auerchain_complete: submitting next urb from chain"); dbg ("auerchain_complete: submitting next urb from chain");
urb->status = 0; /* needed! */ urb->status = 0; /* needed! */
result = usb_submit_urb( urb); result = usb_submit_urb(urb, GFP_KERNEL);
/* check for submit errors */ /* check for submit errors */
if (result) { if (result) {
...@@ -408,7 +415,7 @@ static int auerchain_submit_urb_list (pauerchain_t acp, struct urb * urb, int ea ...@@ -408,7 +415,7 @@ static int auerchain_submit_urb_list (pauerchain_t acp, struct urb * urb, int ea
if (acep) { if (acep) {
dbg("submitting urb immediate"); dbg("submitting urb immediate");
urb->status = 0; /* needed! */ urb->status = 0; /* needed! */
result = usb_submit_urb( urb); result = usb_submit_urb(urb, GFP_KERNEL);
/* check for submit errors */ /* check for submit errors */
if (result) { if (result) {
urb->status = result; urb->status = result;
...@@ -600,8 +607,10 @@ static int auerchain_setup (pauerchain_t acp, unsigned int numElements) ...@@ -600,8 +607,10 @@ static int auerchain_setup (pauerchain_t acp, unsigned int numElements)
/* completion handler for synchronous chained URBs */ /* completion handler for synchronous chained URBs */
static void auerchain_blocking_completion (struct urb *urb) static void auerchain_blocking_completion (struct urb *urb)
{ {
wait_queue_head_t *wakeup = (wait_queue_head_t *)urb->context; pauerchain_chs_t pchs = (pauerchain_chs_t)urb->context;
wake_up (wakeup); pchs->done = 1;
wmb();
wake_up (&pchs->wqh);
} }
...@@ -609,36 +618,43 @@ static void auerchain_blocking_completion (struct urb *urb) ...@@ -609,36 +618,43 @@ static void auerchain_blocking_completion (struct urb *urb)
static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length) static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length)
{ {
DECLARE_WAITQUEUE (wait, current); DECLARE_WAITQUEUE (wait, current);
DECLARE_WAIT_QUEUE_HEAD (wqh); auerchain_chs_t chs;
int status; int status;
dbg ("auerchain_start_wait_urb called"); dbg ("auerchain_start_wait_urb called");
init_waitqueue_head (&wqh); init_waitqueue_head (&chs.wqh);
current->state = TASK_INTERRUPTIBLE; chs.done = 0;
add_wait_queue (&wqh, &wait);
urb->context = &wqh; set_current_state (TASK_UNINTERRUPTIBLE);
status = auerchain_submit_urb ( acp, urb); add_wait_queue (&chs.wqh, &wait);
urb->context = &chs;
status = auerchain_submit_urb (acp, urb);
if (status) { if (status) {
/* something went wrong */ /* something went wrong */
current->state = TASK_RUNNING; set_current_state (TASK_RUNNING);
remove_wait_queue (&wqh, &wait); remove_wait_queue (&chs.wqh, &wait);
return status; return status;
} }
if (urb->status == -EINPROGRESS) { while (timeout && !chs.done)
while (timeout && urb->status == -EINPROGRESS) {
status = timeout = schedule_timeout (timeout); timeout = schedule_timeout (timeout);
} else set_current_state(TASK_UNINTERRUPTIBLE);
status = 1; rmb();
}
current->state = TASK_RUNNING; set_current_state (TASK_RUNNING);
remove_wait_queue (&wqh, &wait); remove_wait_queue (&chs.wqh, &wait);
if (!status) { if (!timeout && !chs.done) {
/* timeout */ if (urb->status != -EINPROGRESS) { /* No callback?!! */
dbg ("auerchain_start_wait_urb: raced timeout");
status = urb->status;
} else {
dbg ("auerchain_start_wait_urb: timeout"); dbg ("auerchain_start_wait_urb: timeout");
auerchain_unlink_urb (acp, urb); /* remove urb safely */ auerchain_unlink_urb (acp, urb); /* remove urb safely */
status = -ETIMEDOUT; status = -ETIMEDOUT;
}
} else } else
status = urb->status; status = urb->status;
...@@ -932,6 +948,8 @@ static void auerswald_ctrlread_complete (struct urb * urb) ...@@ -932,6 +948,8 @@ static void auerswald_ctrlread_complete (struct urb * urb)
/* reuse the buffer */ /* reuse the buffer */
err ("control read: transmission error %d, can not retry", urb->status); err ("control read: transmission error %d, can not retry", urb->status);
auerbuf_releasebuf (bp); auerbuf_releasebuf (bp);
/* Wake up all processes waiting for a buffer */
wake_up (&cp->bufferwait);
return; return;
} }
bp->retries++; bp->retries++;
...@@ -1128,7 +1146,7 @@ static int auerswald_int_open (pauerswald_t cp) ...@@ -1128,7 +1146,7 @@ static int auerswald_int_open (pauerswald_t cp)
FILL_INT_URB (cp->inturbp, cp->usbdev, usb_rcvintpipe (cp->usbdev,AU_IRQENDP), cp->intbufp, irqsize, auerswald_int_complete, cp, ep->bInterval); 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 */ /* start the urb */
cp->inturbp->status = 0; /* needed! */ cp->inturbp->status = 0; /* needed! */
ret = usb_submit_urb (cp->inturbp); ret = usb_submit_urb (cp->inturbp, GFP_KERNEL);
intoend: intoend:
if (ret < 0) { if (ret < 0) {
...@@ -1376,9 +1394,6 @@ static int auerchar_open (struct inode *inode, struct file *file) ...@@ -1376,9 +1394,6 @@ static int auerchar_open (struct inode *inode, struct file *file)
} }
up (&dev_table_mutex); up (&dev_table_mutex);
/* prevent module unloading */
MOD_INC_USE_COUNT;
/* we have access to the device. Now lets allocate memory */ /* we have access to the device. Now lets allocate memory */
ccp = (pauerchar_t) kmalloc(sizeof(auerchar_t), GFP_KERNEL); ccp = (pauerchar_t) kmalloc(sizeof(auerchar_t), GFP_KERNEL);
if (ccp == NULL) { if (ccp == NULL) {
...@@ -1415,7 +1430,6 @@ static int auerchar_open (struct inode *inode, struct file *file) ...@@ -1415,7 +1430,6 @@ static int auerchar_open (struct inode *inode, struct file *file)
/* Error exit */ /* Error exit */
ofail: up (&cp->mutex); ofail: up (&cp->mutex);
auerchar_delete (ccp); auerchar_delete (ccp);
MOD_DEC_USE_COUNT;
return ret; return ret;
} }
...@@ -1568,6 +1582,8 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t ...@@ -1568,6 +1582,8 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t
unsigned long flags; unsigned long flags;
pauerchar_t ccp = (pauerchar_t) file->private_data; pauerchar_t ccp = (pauerchar_t) file->private_data;
pauerbuf_t bp = NULL; pauerbuf_t bp = NULL;
wait_queue_t wait;
dbg ("auerchar_read"); dbg ("auerchar_read");
/* Error checking */ /* Error checking */
...@@ -1630,6 +1646,11 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t ...@@ -1630,6 +1646,11 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t
/* a read buffer is not available. Try to get the next data block. */ /* a read buffer is not available. Try to get the next data block. */
doreadlist: doreadlist:
/* Preparing for sleep */
init_waitqueue_entry (&wait, current);
set_current_state (TASK_INTERRUPTIBLE);
add_wait_queue (&ccp->readwait, &wait);
bp = NULL; bp = NULL;
spin_lock_irqsave (&ccp->bufctl.lock, flags); spin_lock_irqsave (&ccp->bufctl.lock, flags);
if (!list_empty (&ccp->bufctl.rec_buff_list)) { if (!list_empty (&ccp->bufctl.rec_buff_list)) {
...@@ -1644,12 +1665,16 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t ...@@ -1644,12 +1665,16 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t
if (bp) { if (bp) {
ccp->readbuf = bp; ccp->readbuf = bp;
ccp->readoffset = AUH_SIZE; /* for headerbyte */ ccp->readoffset = AUH_SIZE; /* for headerbyte */
set_current_state (TASK_RUNNING);
remove_wait_queue (&ccp->readwait, &wait);
goto doreadbuf; /* now we can read! */ goto doreadbuf; /* now we can read! */
} }
/* no data available. Should we wait? */ /* no data available. Should we wait? */
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
dbg ("No read buffer available, returning -EAGAIN"); dbg ("No read buffer available, returning -EAGAIN");
set_current_state (TASK_RUNNING);
remove_wait_queue (&ccp->readwait, &wait);
up (&ccp->readmutex); up (&ccp->readmutex);
up (&ccp->mutex); up (&ccp->mutex);
return -EAGAIN; /* nonblocking, no data available */ return -EAGAIN; /* nonblocking, no data available */
...@@ -1657,7 +1682,8 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t ...@@ -1657,7 +1682,8 @@ static ssize_t auerchar_read (struct file *file, char *buf, size_t count, loff_t
/* yes, we should wait! */ /* yes, we should wait! */
up (&ccp->mutex); /* allow other operations while we wait */ up (&ccp->mutex); /* allow other operations while we wait */
interruptible_sleep_on (&ccp->readwait); schedule();
remove_wait_queue (&ccp->readwait, &wait);
if (signal_pending (current)) { if (signal_pending (current)) {
/* waked up by a signal */ /* waked up by a signal */
up (&ccp->readmutex); up (&ccp->readmutex);
...@@ -1688,6 +1714,7 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l ...@@ -1688,6 +1714,7 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l
pauerbuf_t bp; pauerbuf_t bp;
unsigned long flags; unsigned long flags;
int ret; int ret;
wait_queue_t wait;
dbg ("auerchar_write %d bytes", len); dbg ("auerchar_write %d bytes", len);
...@@ -1724,6 +1751,11 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l ...@@ -1724,6 +1751,11 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l
up (&ccp->mutex); up (&ccp->mutex);
return -EIO; 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. /* Try to get a buffer from the device pool.
We can't use a buffer from ccp->bufctl because the write We can't use a buffer from ccp->bufctl because the write
command will last beond a release() */ command will last beond a release() */
...@@ -1744,16 +1776,22 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l ...@@ -1744,16 +1776,22 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l
/* NONBLOCK: don't wait */ /* NONBLOCK: don't wait */
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
set_current_state (TASK_RUNNING);
remove_wait_queue (&cp->bufferwait, &wait);
return -EAGAIN; return -EAGAIN;
} }
/* BLOCKING: wait */ /* BLOCKING: wait */
interruptible_sleep_on (&cp->bufferwait); schedule();
remove_wait_queue (&cp->bufferwait, &wait);
if (signal_pending (current)) { if (signal_pending (current)) {
/* waked up by a signal */ /* waked up by a signal */
return -ERESTARTSYS; return -ERESTARTSYS;
} }
goto write_again; goto write_again;
} else {
set_current_state (TASK_RUNNING);
remove_wait_queue (&cp->bufferwait, &wait);
} }
/* protect against too big write requests */ /* protect against too big write requests */
...@@ -1763,6 +1801,8 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l ...@@ -1763,6 +1801,8 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l
if (copy_from_user ( bp->bufp+AUH_SIZE, buf, len)) { if (copy_from_user ( bp->bufp+AUH_SIZE, buf, len)) {
dbg ("copy_from_user failed"); dbg ("copy_from_user failed");
auerbuf_releasebuf (bp); auerbuf_releasebuf (bp);
/* Wake up all processes waiting for a buffer */
wake_up (&cp->bufferwait);
up (&cp->mutex); up (&cp->mutex);
up (&ccp->mutex); up (&ccp->mutex);
return -EIO; return -EIO;
...@@ -1787,6 +1827,8 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l ...@@ -1787,6 +1827,8 @@ static ssize_t auerchar_write (struct file *file, const char *buf, size_t len, l
if (ret) { if (ret) {
dbg ("auerchar_write: nonzero result of auerchain_submit_urb %d", ret); dbg ("auerchar_write: nonzero result of auerchain_submit_urb %d", ret);
auerbuf_releasebuf (bp); auerbuf_releasebuf (bp);
/* Wake up all processes waiting for a buffer */
wake_up (&cp->bufferwait);
up (&ccp->mutex); up (&ccp->mutex);
return -EIO; return -EIO;
} }
...@@ -1831,9 +1873,6 @@ static int auerchar_release (struct inode *inode, struct file *file) ...@@ -1831,9 +1873,6 @@ static int auerchar_release (struct inode *inode, struct file *file)
up (&ccp->mutex); up (&ccp->mutex);
auerchar_delete (ccp); auerchar_delete (ccp);
/* release the module */
MOD_DEC_USE_COUNT;
return 0; return 0;
} }
...@@ -2154,3 +2193,4 @@ module_init (auerswald_init); ...@@ -2154,3 +2193,4 @@ module_init (auerswald_init);
module_exit (auerswald_cleanup); module_exit (auerswald_cleanup);
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment