patch-2.4.0-test3 linux/drivers/usb/serial/digi_acceleport.c
Next file: linux/drivers/usb/serial/keyspan.c
Previous file: linux/drivers/usb/serial/Makefile
Back to the patch index
Back to the overall index
- Lines: 381
- Date:
Wed Jun 28 19:49:00 2000
- Orig file:
v2.4.0-test2/linux/drivers/usb/serial/digi_acceleport.c
- Orig date:
Fri Jun 23 21:55:10 2000
diff -u --recursive --new-file v2.4.0-test2/linux/drivers/usb/serial/digi_acceleport.c linux/drivers/usb/serial/digi_acceleport.c
@@ -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.63 2000/06/28 18:28:31 root Exp root $
*/
#include <linux/config.h>
@@ -307,7 +328,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;
@@ -402,7 +423,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 )
{
@@ -465,6 +486,7 @@
/* wake up other tty processes */
wake_up_interruptible( &tty->write_wait );
+ /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
}
@@ -515,17 +537,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 );
}
@@ -569,8 +591,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;
@@ -588,35 +610,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 );
}
@@ -647,10 +655,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 );
@@ -658,7 +666,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;
@@ -679,14 +687,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 );
}
@@ -1046,12 +1056,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 */
@@ -1096,40 +1113,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 );
}
@@ -1141,7 +1137,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 );
@@ -1175,24 +1171,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 );
}
}
@@ -1200,13 +1180,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 );
+ }
}
@@ -1219,8 +1202,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 )
@@ -1242,8 +1223,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 ); */
@@ -1455,13 +1434,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++ ) {
@@ -1474,10 +1454,6 @@
}
- device_startup = 1;
-
- spin_unlock( &startup_lock );
-
return( ret );
}
@@ -1516,8 +1492,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 */
@@ -1594,17 +1572,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 );
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)