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

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)