[prev in list] [next in list] [prev in thread] [next in thread]
List: linux-usb-devel
Subject: [linux-usb-devel] Digi Acceleport 2.2.16 Backport Patch
From: Al Borchers <alborchers () steinerpoint ! com>
Date: 2000-06-28 20:34:40
[Download RAW message or body]
Vojtech --
Some problems showed up in our Digi Acceleport USB Serial driver
in the 2.2.16 backport patch. Some of these problems were in
2.4.0 also, one was specific to 2.2.16.
I have already sent a couple patches to Greg and Randy that update
digi_acceleport.c beyond the version in the test2-pre2-v3 backport.
What I have attached here is a cumulative patch, that includes all
those changes, plus the change specificly for 2.2.16. It can be
applied to 2.2.16 with the test2-pre2-v3 backport to bring
digi_acceleport up to date.
If incremental patches would be better, let me know.
The only change specific to 2.2.16 in digi_acceleport.c is easy to make
by hand from the most current digi_acceleport.c. Find the line that says
"For 2.2.16 backport" and remove the comment. Like this
@@ -486,7 +488,7 @@
/* wake up other tty processes */
wake_up_interruptible( &tty->write_wait );
- /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
+ wake_up_interruptible( &tty->poll_wait );
}
Thanks, and thanks so much for doing the backport. It has been very
helpful for us.
-- Al
["digi_acceleport-2.2.16-usb-2.4.0-test2-pre2-v3.diff" (text/plain)]
diff -urN -X patch_ignore \
linux-2.2.16-usb-2.4.0-test2-pre2-v3/drivers/usb/serial/digi_acceleport.c \
linux/drivers/usb/serial/digi_acceleport.c
--- linux-2.2.16-usb-2.4.0-test2-pre2-v3/drivers/usb/serial/digi_acceleport.c Wed Jun \
28 16:07:08 2000
+++ linux/drivers/usb/serial/digi_acceleport.c Wed Jun 28 11:16:34 2000
@@ -14,6 +14,25 @@
* Peter Berger (pberger@brimson.com)
* Al Borchers (borchers@steinerpoint.com)
*
+* (6/27/2000) pberger and borchers
+* -- Zeroed out sync field in the wakeup_task before first use;
+* otherwise the uninitialized value might prevent the task from
+* being scheduled.
+* -- Initialized ret value to 0 in write_bulk_callback, otherwise
+* the uninitialized value could cause a spurious debugging message.
+*
+* (6/22/2000) pberger and borchers
+* -- Made cond_wait_... inline--apparently on SPARC the flags arg
+* to spin_lock_irqsave cannot be passed to another function
+* to call spin_unlock_irqrestore. Thanks to Pauline Middelink.
+* -- In digi_set_modem_signals the inner nested spin locks use just
+* spin_lock() rather than spin_lock_irqsave(). The old code
+* mistakenly left interrupts off. Thanks to Pauline Middelink.
+* -- copy_from_user (which can sleep) is no longer called while a
+* spinlock is held. We copy to a local buffer before getting
+* the spinlock--don't like the extra copy but the code is simpler.
+* -- Printk and dbg are no longer called while a spin lock is held.
+*
* (6/4/2000) pberger and borchers
* -- Replaced separate calls to spin_unlock_irqrestore and
* interruptible_sleep_on_interruptible with a new function
@@ -119,8 +138,10 @@
* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to
* recheck the condition they are sleeping on. This is defensive,
* in case a wake up is lost.
+* - Following Documentation/DocBook/kernel-locking.pdf no spin locks
+* are held when calling copy_to/from_user or printk.
*
-* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $
+* $Id: digi_acceleport.c,v 1.62 2000/06/28 16:16:24 root Exp root $
*/
#define __NO_VERSION__
@@ -309,7 +330,7 @@
int dp_transmit_idle;
int dp_in_close;
wait_queue_head_t dp_close_wait; /* wait queue for close */
- struct tq_struct dp_tasks;
+ struct tq_struct dp_wakeup_task;
} digi_private_t;
@@ -404,7 +425,7 @@
* wake ups. This is used to implement condition variables.
*/
-static long cond_wait_interruptible_timeout_irqrestore(
+static inline long cond_wait_interruptible_timeout_irqrestore(
wait_queue_head_t *q, long timeout,
spinlock_t *lock, unsigned long flags )
{
@@ -467,6 +488,7 @@
/* wake up other tty processes */
wake_up_interruptible( &tty->write_wait );
+ wake_up_interruptible( &tty->poll_wait );
}
@@ -517,17 +539,17 @@
if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) {
count -= len;
buf += len;
- } else {
- dbg(
- "digi_write_oob_command: usb_submit_urb failed, ret=%d",
- ret );
- break;
}
}
spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+ if( ret ) {
+ dbg( "digi_write_oob_command: usb_submit_urb failed, ret=%d",
+ ret );
+ }
+
return( ret );
}
@@ -571,8 +593,8 @@
}
/* len must be a multiple of 4 and small enough to */
- /* guarantee the write will send all data (or none), */
- /* so commands are not split */
+ /* guarantee the write will send buffered data first, */
+ /* so commands are in order with data and not split */
len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len );
if( len > 4 )
len &= ~3;
@@ -590,35 +612,21 @@
port->write_urb->transfer_buffer_length = len;
}
-#ifdef DEBUG_DATA
- {
- int i;
-
- printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
- priv->dp_port_num, port->write_urb->transfer_buffer_length );
- for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
- printk( "%.2x ",
- ((unsigned char *)port->write_urb->transfer_buffer)[i] );
- }
- printk( "\n" );
- }
-#endif
-
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
priv->dp_buf_len = 0;
count -= len;
buf += len;
- } else {
- dbg(
- "digi_write_inb_command: usb_submit_urb failed, ret=%d",
- ret );
- break;
}
}
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ if( ret ) {
+ dbg( "digi_write_inb_command: usb_submit_urb failed, ret=%d",
+ ret );
+ }
+
return( ret );
}
@@ -649,10 +657,10 @@
port_priv->dp_port_num, modem_signals );
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
- spin_lock_irqsave( &port_priv->dp_port_lock, flags );
+ spin_lock( &port_priv->dp_port_lock );
while( oob_port->write_urb->status == -EINPROGRESS ) {
- spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ spin_unlock( &port_priv->dp_port_lock );
cond_wait_interruptible_timeout_irqrestore(
&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
&oob_priv->dp_port_lock, flags );
@@ -660,7 +668,7 @@
return( -EINTR );
}
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
- spin_lock_irqsave( &port_priv->dp_port_lock, flags );
+ spin_lock( &port_priv->dp_port_lock );
}
data[0] = DIGI_CMD_SET_DTR_SIGNAL;
@@ -681,14 +689,16 @@
port_priv->dp_modem_signals =
(port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS))
| (modem_signals&(TIOCM_DTR|TIOCM_RTS));
- } else {
- dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d",
- ret );
}
- spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ spin_unlock( &port_priv->dp_port_lock );
spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+ if( ret ) {
+ dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d",
+ ret );
+ }
+
return( ret );
}
@@ -1048,12 +1058,19 @@
int ret,data_len,new_len;
digi_private_t *priv = (digi_private_t *)(port->private);
unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned char user_buf[64]; /* 64 bytes is max USB bulk packet */
unsigned long flags = 0;
dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d",
priv->dp_port_num, count, from_user, in_interrupt() );
+ /* copy user data (which can sleep) before getting spin lock */
+ count = MIN( 64, MIN( count, port->bulk_out_size-2 ) );
+ if( from_user && copy_from_user( user_buf, buf, count ) ) {
+ return( -EFAULT );
+ }
+
/* be sure only one write proceeds at a time */
/* there are races on the port private buffer */
/* and races to check write_urb->status */
@@ -1098,40 +1115,19 @@
data += priv->dp_buf_len;
/* copy in new data */
- if( from_user ) {
- if( copy_from_user( data, buf, new_len ) ) {
- spin_unlock_irqrestore( &priv->dp_port_lock, flags );
- return( -EFAULT );
- }
- } else {
- memcpy( data, buf, new_len );
- }
-
-#ifdef DEBUG_DATA
- {
- int i;
-
- printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
- priv->dp_port_num, port->write_urb->transfer_buffer_length );
- for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
- printk( "%.2x ",
- ((unsigned char *)port->write_urb->transfer_buffer)[i] );
- }
- printk( "\n" );
- }
-#endif
+ memcpy( data, from_user ? user_buf : buf, new_len );
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
ret = new_len;
priv->dp_buf_len = 0;
- } else {
- dbg( "digi_write: usb_submit_urb failed, ret=%d",
- ret );
}
/* return length of new data written, or error */
-dbg( "digi_write: returning %d", ret );
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ if( ret < 0 ) {
+ dbg( "digi_write: usb_submit_urb failed, ret=%d", ret );
+ }
+dbg( "digi_write: returning %d", ret );
return( ret );
}
@@ -1143,7 +1139,7 @@
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
digi_private_t *priv = (digi_private_t *)(port->private);
- int ret;
+ int ret = 0;
dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
@@ -1177,24 +1173,8 @@
memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf,
priv->dp_buf_len );
-#ifdef DEBUG_DATA
- {
- int i;
-
- printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, \
data=",
- priv->dp_port_num, port->write_urb->transfer_buffer_length );
- for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
- printk( "%.2x ",
- ((unsigned char *)port->write_urb->transfer_buffer)[i] );
- }
- printk( "\n" );
- }
-#endif
-
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
priv->dp_buf_len = 0;
- } else {
- dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret );
}
}
@@ -1202,13 +1182,16 @@
/* wake up processes sleeping on writes immediately */
digi_wakeup_write( port );
- spin_unlock( &priv->dp_port_lock );
-
/* also queue up a wakeup at scheduler time, in case we */
/* lost the race in write_chan(). */
- priv->dp_tasks.routine = (void *)digi_wakeup_write_lock;
- priv->dp_tasks.data = (void *)port;
- queue_task( &(priv->dp_tasks), &tq_scheduler );
+ queue_task( &priv->dp_wakeup_task, &tq_scheduler );
+
+ spin_unlock( &priv->dp_port_lock );
+
+ if( ret ) {
+ dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d",
+ ret );
+ }
}
@@ -1221,8 +1204,6 @@
unsigned long flags = 0;
-dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num );
-
spin_lock_irqsave( &priv->dp_port_lock, flags );
if( port->write_urb->status == -EINPROGRESS )
@@ -1244,8 +1225,6 @@
digi_private_t *priv = (digi_private_t *)(port->private);
-dbg( "digi_chars_in_buffer: TOP: port=%d", priv->dp_port_num );
-
if( port->write_urb->status == -EINPROGRESS ) {
dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, \
port->bulk_out_size - 2 ); /* return( port->bulk_out_size - 2 ); */
@@ -1457,13 +1436,14 @@
int i,ret = 0;
- spin_lock( &startup_lock );
-
/* be sure this happens exactly once */
+ spin_lock( &startup_lock );
if( device_startup ) {
spin_unlock( &startup_lock );
return( 0 );
}
+ device_startup = 1;
+ spin_unlock( &startup_lock );
/* start reading from each bulk in endpoint for the device */
for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) {
@@ -1476,10 +1456,6 @@
}
- device_startup = 1;
-
- spin_unlock( &startup_lock );
-
return( ret );
}
@@ -1518,8 +1494,10 @@
priv->dp_transmit_idle = 0;
priv->dp_in_close = 0;
init_waitqueue_head( &priv->dp_close_wait );
- priv->dp_tasks.next = NULL;
- priv->dp_tasks.data = NULL;
+ priv->dp_wakeup_task.next = NULL;
+ priv->dp_wakeup_task.sync = 0;
+ priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock;
+ priv->dp_wakeup_task.data = (void *)(&serial->port[i]);
spin_lock_init( &priv->dp_port_lock );
/* initialize write wait queue for this port */
@@ -1596,17 +1574,6 @@
return;
goto resubmit;
}
-
-#ifdef DEBUG_DATA
-if( urb->actual_length ) {
- printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=",
- priv->dp_port_num, urb->actual_length );
- for( i=0; i<urb->actual_length; ++i ) {
- printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] );
- }
- printk( "\n" );
-}
-#endif
if( urb->actual_length != len + 2 )
err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, \
opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, \
urb->actual_length, status );
_______________________________________________
linux-usb-devel mailing list
linux-usb-devel@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/linux-usb-devel
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic