patch-2.4.0-test5 linux/drivers/usb/serial/digi_acceleport.c

Next file: linux/drivers/usb/serial/ftdi_sio.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-test4/linux/drivers/usb/serial/digi_acceleport.c linux/drivers/usb/serial/digi_acceleport.c
@@ -14,6 +14,22 @@
 *  Peter Berger (pberger@brimson.com)
 *  Al Borchers (borchers@steinerpoint.com)
 *
+*  (7/15/2000) borchers
+*    -- Fixed race in open when a close is in progress.
+*    -- Keep count of opens and dec the module use count for each
+*       outstanding open when shutdown is called (on disconnect).
+*    -- Fixed sanity checks in read_bulk_callback and write_bulk_callback
+*       so pointers are checked before use.
+*    -- Split read bulk callback into in band and out of band
+*       callbacks, and no longer restart read chains if there is
+*       a status error or a sanity error.  This fixed the seg
+*       faults and other errors we used to get on disconnect.
+*    -- Port->active is once again a flag, not a count, as it was
+*       intended by usb-serial.  Since it was only a char it would
+*       have been limited to 256 simultaneous opens.  Now the open
+*       count is kept in the port private structure in dp_open_count.
+*    -- Added code for modularization of the digi_acceleport driver.
+*
 *  (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
@@ -141,7 +157,7 @@
 *  - 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.63 2000/06/28 18:28:31 root Exp root $
+*  $Id: digi_acceleport.c,v 1.5 2000/07/18 04:52:43 root Exp $
 */
 
 #include <linux/config.h>
@@ -325,8 +341,9 @@
 	int dp_buf_len;
 	unsigned char dp_buf[DIGI_PORT_BUF_LEN];
 	unsigned int dp_modem_signals;
+	int dp_open_count;			/* inc on open, dec on close */
 	int dp_transmit_idle;
-	int dp_in_close;
+	int dp_in_close;			/* close in progress */
 	wait_queue_head_t dp_close_wait;	/* wait queue for close */
 	struct tq_struct dp_wakeup_task;
 } digi_private_t;
@@ -361,7 +378,8 @@
 static int digi_startup( struct usb_serial *serial );
 static void digi_shutdown( struct usb_serial *serial );
 static void digi_read_bulk_callback( struct urb *urb );
-static void digi_read_oob_callback( struct urb *urb );
+static int digi_read_inb_callback( struct urb *urb );
+static int digi_read_oob_callback( struct urb *urb );
 
 
 /* Statics */
@@ -1135,14 +1153,21 @@
 {
 
 	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);
+	struct usb_serial *serial;
+	digi_private_t *priv;
 	int ret = 0;
 
 
-dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
+dbg( "digi_write_bulk_callback: TOP" );
+
+	/* port sanity check */
+	if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) {
+		err( __FUNCTION__ ": port or port->private is NULL, status=%d",
+			urb->status );
+		return;
+	}
 
-	/* handle callback on out-of-band port */
+	/* handle oob callback */
 	if( priv->dp_port_num == oob_port_num ) {
 		dbg( "digi_write_bulk_callback: oob callback" );
 		spin_lock( &priv->dp_port_lock );
@@ -1152,10 +1177,11 @@
 	}
 
 	/* sanity checks */
-	if( port_paranoia_check( port, "digi_write_bulk_callback" )
-	|| serial_paranoia_check( serial, "digi_write_bulk_callback" ) ) {
+	if( port_paranoia_check( port, "digi_write_bulk_callback" ) )
+		return;
+	serial = port->serial;
+	if( serial_paranoia_check( serial, "digi_write_bulk_callback" ) )
 		return;
-	}
 
 	/* try to send any buffered data on this port */
 	spin_lock( &priv->dp_port_lock );
@@ -1187,8 +1213,8 @@
 	spin_unlock( &priv->dp_port_lock );
 
 	if( ret ) {
-		dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d",
-		ret );
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+			ret, priv->dp_port_num );
 	}
 
 }
@@ -1245,21 +1271,13 @@
 	unsigned long flags = 0;
 
 
-dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active );
+dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, port->active, priv->dp_open_count );
 
 	/* be sure the device is started up */
 	if( digi_startup_device( port->serial ) != 0 )
 		return( -ENXIO );
 
-	/* if port is already open, just return */
-	/* be sure exactly one open proceeds */
 	spin_lock_irqsave( &priv->dp_port_lock, flags );
-	if( port->active >= 1 && !priv->dp_in_close ) {
-		++port->active;
-		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
-		MOD_INC_USE_COUNT;
-		return( 0 );
-	}
 
 	/* don't wait on a close in progress for non-blocking opens */
 	if( priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
@@ -1267,23 +1285,31 @@
 		return( -EAGAIN );
 	}
 
-	/* prevent other opens from proceeding, before giving up lock */
-	++port->active;
-
-	/* wait for close to finish */
+	/* wait for a close in progress to finish */
 	while( priv->dp_in_close ) {
 		cond_wait_interruptible_timeout_irqrestore(
 			&priv->dp_close_wait, DIGI_RETRY_TIMEOUT,
 			&priv->dp_port_lock, flags );
 		if( signal_pending(current) ) {
-			--port->active;
 			return( -EINTR );
 		}
 		spin_lock_irqsave( &priv->dp_port_lock, flags );
 	}
 
-	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+	/* if port is already open, just return */
+	/* be sure exactly one open proceeds */
+	if( port->active ) {
+		++priv->dp_open_count;
+		MOD_INC_USE_COUNT;
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return( 0 );
+	}
+
+	/* open is certain */
+	port->active = 1;
+	++priv->dp_open_count;
 	MOD_INC_USE_COUNT;
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
  
 	/* read modem signals automatically whenever they change */
 	buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
@@ -1323,17 +1349,17 @@
 	unsigned long flags = 0;
 
 
-dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active );
+dbg( "digi_close: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, port->active, priv->dp_open_count );
 
 
 	/* do cleanup only after final close on this port */
 	spin_lock_irqsave( &priv->dp_port_lock, flags );
-	if( port->active > 1 ) {
-		--port->active;
-		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+	if( priv->dp_open_count > 1 ) {
+		--priv->dp_open_count;
 		MOD_DEC_USE_COUNT;
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
 		return;
-	} else if( port->active <= 0 ) {
+	} else if( priv->dp_open_count <= 0 ) {
 		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
 		return;
 	}
@@ -1410,13 +1436,13 @@
 	tty->closing = 0;
 
 	spin_lock_irqsave( &priv->dp_port_lock, flags );
-	--port->active;
+	port->active = 0;
 	priv->dp_in_close = 0;
+	--priv->dp_open_count;
+	MOD_DEC_USE_COUNT;
 	wake_up_interruptible( &priv->dp_close_wait );
 	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
 
-	MOD_DEC_USE_COUNT;
-
 dbg( "digi_close: done" );
 }
 
@@ -1489,6 +1515,7 @@
 		priv->dp_port_num = i;
 		priv->dp_buf_len = 0;
 		priv->dp_modem_signals = 0;
+		priv->dp_open_count = 0;
 		priv->dp_transmit_idle = 0;
 		priv->dp_in_close = 0;
 		init_waitqueue_head( &priv->dp_close_wait );
@@ -1517,18 +1544,31 @@
 {
 
 	int i;
+	digi_private_t *priv;
+	unsigned long flags;
 
 
-dbg( "digi_shutdown: TOP" );
+dbg( "digi_shutdown: TOP, in_interrupt()=%d", in_interrupt() );
 
 	/* stop reads and writes on all ports */
 	for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) {
-		usb_unlink_urb (serial->port[i].read_urb);
-		usb_unlink_urb (serial->port[i].write_urb);
+		usb_unlink_urb( serial->port[i].read_urb );
+		usb_unlink_urb( serial->port[i].write_urb );
 	}
 
 	device_startup = 0;
 
+	/* dec module use count */
+	for( i=0; i<digi_acceleport_device.num_ports; i++ ) {
+		priv = serial->port[i].private;
+		spin_lock_irqsave( &priv->dp_port_lock, flags );
+		while( priv->dp_open_count > 0 ) {
+			MOD_DEC_USE_COUNT;
+			--priv->dp_open_count;
+		}
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+	}
+
 	/* free the private data structures for all ports */
 	/* number of regular ports + 1 for the out-of-band port */
 	for( i=0; i<digi_acceleport_device.num_ports+1; i++ )
@@ -1541,40 +1581,76 @@
 {
 
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-	struct usb_serial *serial = port->serial;
-	struct tty_struct *tty = port->tty;
-	digi_private_t *priv = (digi_private_t *)(port->private);
-	int opcode = ((unsigned char *)urb->transfer_buffer)[0];
-	int len = ((unsigned char *)urb->transfer_buffer)[1];
-	int status = ((unsigned char *)urb->transfer_buffer)[2];
-	unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3;
-	int ret,i;
+	digi_private_t *priv;
+	int ret;
 
 
-dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num );
+dbg( "digi_read_bulk_callback: TOP" );
 
-	/* handle oob callback */
-	if( priv->dp_port_num == oob_port_num ) {
-		digi_read_oob_callback( urb );
+	/* port sanity check, do not resubmit if port is not valid */
+	if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) {
+		err( __FUNCTION__ ": port or port->private is NULL, status=%d",
+			urb->status );
 		return;
 	}
 
-	/* sanity checks */
-	if( port_paranoia_check( port, "digi_read_bulk_callback" )
-	|| serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) {
-		goto resubmit;
+	/* do not resubmit urb if it has any status error */
+	if( urb->status ) {
+		err( __FUNCTION__ ": nonzero read bulk status: status=%d, port=%d", urb->status, priv->dp_port_num );
+		return;
 	}
 
-	if( urb->status ) {
-		dbg( "digi_read_bulk_callback: nonzero read bulk status: %d",
-			urb->status );
-		if( urb->status == -ENOENT )
+	/* handle oob or inb callback, do not resubmit if error */
+	if( priv->dp_port_num == oob_port_num ) {
+		if( digi_read_oob_callback( urb ) != 0 )
 			return;
-		goto resubmit;
+	} else {
+		if( digi_read_inb_callback( urb ) != 0 )
+			return;
+	}
+
+	/* continue read */
+	if( (ret=usb_submit_urb(urb)) != 0 ) {
+		err( __FUNCTION__ ": failed resubmitting urb, ret=%d, port=%d",
+			ret, priv->dp_port_num );
 	}
 
-	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 );
+}
+
+
+/* 
+*  Digi Read INB Callback
+*
+*  Digi Read INB Callback handles reads on the in band ports, sending
+*  the data on to the tty subsystem.  When called we know port and
+*  port->private are not NULL.  It returns 0 if successful, and -1 if
+*  the sanity checks failed.
+*/
+
+static int digi_read_inb_callback( struct urb *urb )
+{
+
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = port->serial;
+	struct tty_struct *tty = port->tty;
+	digi_private_t *priv = (digi_private_t *)(port->private);
+	int opcode = ((unsigned char *)urb->transfer_buffer)[0];
+	int len = ((unsigned char *)urb->transfer_buffer)[1];
+	int status = ((unsigned char *)urb->transfer_buffer)[2];
+	unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3;
+	int i;
+
+
+	/* sanity checks */
+	if( port_paranoia_check( port, __FUNCTION__ )
+	|| serial_paranoia_check( serial, __FUNCTION__ ) )
+		return( -1 );
+
+	/* short packet check */
+	if( urb->actual_length != len + 2 ) {
+     		err( __FUNCTION__ ": INCOMPLETE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status );
+		return( -1 );
+	}
 
 	/* receive data */
 	if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) {
@@ -1585,36 +1661,39 @@
 	  	tty_flip_buffer_push(tty);
 	}
 
-	/* continue read */
-resubmit:
-	if( (ret=usb_submit_urb(urb)) != 0 ) {
-		dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d",
-			ret );
-	}
+	return( 0 );
 
 }
 
 
-static void digi_read_oob_callback( struct urb *urb )
+/* 
+*  Digi Read OOB Callback
+*
+*  Digi Read OOB Callback handles reads on the out of band port.
+*  When called we know port and port->private are not NULL.  It
+*  returns 0 if successful, and -1 if the sanity checks failed.
+*/
+
+static int digi_read_oob_callback( struct urb *urb )
 {
 
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct usb_serial *serial = port->serial;
-	digi_private_t *priv;
+	digi_private_t *priv = (digi_private_t *)(port->private);
 	int opcode, line, status, val;
-	int i,ret;
+	int i;
 
 
 dbg( "digi_read_oob_callback: len=%d", urb->actual_length );
 
-	if( urb->status ) {
-		dbg( "digi_read_oob_callback: nonzero read bulk status on oob: %d",
-			urb->status );
-		if( urb->status == -ENOENT )
-			return;
-		goto resubmit;
+	/* sanity check */
+	if( serial == NULL ) {
+		err( __FUNCTION__ ": port->serial is NULL, status=%d, port=%d",
+			urb->status, priv->dp_port_num );
+		return( -1 );
 	}
 
+	/* handle each oob command */
 	for( i=0; i<urb->actual_length-3; ) {
 
 		opcode = ((unsigned char *)urb->transfer_buffer)[i++];
@@ -1627,7 +1706,10 @@
 		if( status != 0 )
 			continue;
 
-		priv = serial->port[line].private;
+		if( (priv=serial->port[line].private) == NULL ) {
+			dbg( __FUNCTION__ ": port[%d].private is NULL!", line );
+			continue;
+		}
 
 		if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) {
 
@@ -1665,11 +1747,25 @@
 
 	}
 
+	return( 0 );
+
+}
+
+
+int digi_init (void)
+{
+	usb_serial_register (&digi_acceleport_device);
+	return 0;
+}
 
-resubmit:
-	if( (ret=usb_submit_urb(urb)) != 0 ) {
-		dbg( "digi_read_oob_callback: failed resubmitting oob urb, ret=%d",
-		ret );
-	}
 
+void digi_exit (void)
+{
+	usb_serial_deregister (&digi_acceleport_device);
 }
+
+
+module_init(digi_init);
+module_exit(digi_exit);
+
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)