[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