patch-2.4.0-test2 linux/drivers/usb/serial/digi_acceleport.c
Next file: linux/drivers/usb/serial/usbserial.c
Previous file: linux/drivers/usb/scanner.h
Back to the patch index
Back to the overall index
- Lines: 1379
- Date:
Mon Jun 19 13:42:42 2000
- Orig file:
v2.4.0-test1/linux/drivers/usb/serial/digi_acceleport.c
- Orig date:
Tue May 23 15:31:35 2000
diff -u --recursive --new-file v2.4.0-test1/linux/drivers/usb/serial/digi_acceleport.c linux/drivers/usb/serial/digi_acceleport.c
@@ -14,24 +14,75 @@
* Peter Berger (pberger@brimson.com)
* Al Borchers (borchers@steinerpoint.com)
*
+* (6/4/2000) pberger and borchers
+* -- Replaced separate calls to spin_unlock_irqrestore and
+* interruptible_sleep_on_interruptible with a new function
+* cond_wait_interruptible_timeout_irqrestore. This eliminates
+* the race condition where the wake up could happen after
+* the unlock and before the sleep.
+* -- Close now waits for output to drain.
+* -- Open waits until any close in progress is finished.
+* -- All out of band responses are now processed, not just the
+* first in a USB packet.
+* -- Fixed a bug that prevented the driver from working when the
+* first Digi port was not the first USB serial port--the driver
+* was mistakenly using the external USB serial port number to
+* try to index into its internal ports.
+* -- Fixed an SMP bug -- write_bulk_callback is called directly from
+* an interrupt, so spin_lock_irqsave/spin_unlock_irqrestore are
+* needed for locks outside write_bulk_callback that are also
+* acquired by write_bulk_callback to prevent deadlocks.
+* -- Fixed support for select() by making digi_chars_in_buffer()
+* return 256 when -EINPROGRESS is set, as the line discipline
+* code in n_tty.c expects.
+* -- Fixed an include file ordering problem that prevented debugging
+* messages from working.
+* -- Fixed an intermittent timeout problem that caused writes to
+* sometimes get stuck on some machines on some kernels. It turns
+* out in these circumstances write_chan() (in n_tty.c) was
+* asleep waiting for our wakeup call. Even though we call
+* wake_up_interruptible() in digi_write_bulk_callback(), there is
+* a race condition that could cause the wakeup to fail: if our
+* wake_up_interruptible() call occurs between the time that our
+* driver write routine finishes and write_chan() sets current->state
+* to TASK_INTERRUPTIBLE, the effect of our wakeup setting the state
+* to TASK_RUNNING will be lost and write_chan's subsequent call to
+* schedule() will never return (unless it catches a signal).
+* This race condition occurs because write_bulk_callback() (and thus
+* the wakeup) are called asynchonously from an interrupt, rather than
+* from the scheduler. We can avoid the race by calling the wakeup
+* from the scheduler queue and that's our fix: Now, at the end of
+* write_bulk_callback() we queue up a wakeup call on the scheduler
+* task queue. We still also invoke the wakeup directly since that
+* squeezes a bit more performance out of the driver, and any lost
+* race conditions will get cleaned up at the next scheduler run.
+*
+* NOTE: The problem also goes away if you comment out
+* the two code lines in write_chan() where current->state
+* is set to TASK_RUNNING just before calling driver.write() and to
+* TASK_INTERRUPTIBLE immediately afterwards. This is why the
+* problem did not show up with the 2.2 kernels -- they do not
+* include that code.
+*
* (5/16/2000) pberger and borchers
-* -- added timeouts to sleeps
-* -- handle transition to/from B0 in digi_set_termios
+* -- Added timeouts to sleeps, to defend against lost wake ups.
+* -- Handle transition to/from B0 baud rate in digi_set_termios.
*
* (5/13/2000) pberger and borchers
-* -- all commands now sent on out of band port, using digi_write_oob
-* -- get modem control signals whenever they change, support TIOCMGET/
-* SET/BIS/BIC ioctls
+* -- All commands now sent on out of band port, using
+* digi_write_oob_command.
+* -- Get modem control signals whenever they change, support TIOCMGET/
+* SET/BIS/BIC ioctls.
* -- digi_set_termios now supports parity, word size, stop bits, and
-* receive enable
-* -- cleaned up open and close, use digi_set_termios and digi_write_oob
-* to set port parameters
-* -- added digi_startup_device to start read chains on all ports
-* -- write buffer is only used when count==1, to be sure put_char can
-* write a char (unless the buffer is full)
+* receive enable.
+* -- Cleaned up open and close, use digi_set_termios and
+* digi_write_oob_command to set port parameters.
+* -- Added digi_startup_device to start read chains on all ports.
+* -- Write buffer is only used when count==1, to be sure put_char can
+* write a char (unless the buffer is full).
*
* (5/10/2000) pberger and borchers
-* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls
+* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls on open/close.
* -- Fixed problem where the first incoming character is lost on
* port opens after the first close on that port. Now we keep
* the read_urb chain open until shutdown.
@@ -43,7 +94,33 @@
* (5/3/2000) pberger and borchers
* -- First alpha version of the driver--many known limitations and bugs.
*
-* $Id: digi_acceleport.c,v 1.43 2000/05/17 03:21:38 root Exp root $
+*
+* Locking and SMP
+*
+* - Each port, including the out-of-band port, has a lock used to
+* serialize all access to the port's private structure.
+* - The port lock is also used to serialize all writes and access to
+* the port's URB.
+* - The port lock is also used for the port write_wait condition
+* variable. Holding the port lock will prevent a wake up on the
+* port's write_wait; this can be used with cond_wait_... to be sure
+* the wake up is not lost in a race when dropping the lock and
+* sleeping waiting for the wakeup.
+* - digi_write() does not sleep, since it is sometimes called on
+* interrupt time.
+* - digi_write_bulk_callback() and digi_read_bulk_callback() are
+* called directly from interrupts. Hence spin_lock_irqsave()
+* and spin_lock_irqrestore() are used in the rest of the code
+* for any locks they acquire.
+* - digi_write_bulk_callback() gets the port lock before waking up
+* processes sleeping on the port write_wait. It also schedules
+* wake ups so they happen from the scheduler, because the tty
+* system can miss wake ups from interrupts.
+* - 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.
+*
+* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $
*/
#include <linux/config.h>
@@ -60,8 +137,7 @@
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#include <linux/usb.h>
-#include "usb-serial.h"
+#include <linux/tqueue.h>
#ifdef CONFIG_USB_SERIAL_DEBUG
#define DEBUG
@@ -69,16 +145,25 @@
#undef DEBUG
#endif
+#include <linux/usb.h>
+#include "usb-serial.h"
+
/* Defines */
/* port buffer length -- must be <= transfer buffer length - 2 */
/* so we can be sure to send the full buffer in one urb */
-#define DIGI_PORT_BUF_LEN 16
+#define DIGI_PORT_BUF_LEN 8
-/* retry timeout while waiting for urb->status to go to 0 */
+/* retry timeout while sleeping */
#define DIGI_RETRY_TIMEOUT (HZ/10)
+/* timeout while waiting for tty output to drain in close */
+/* this delay is used twice in close, so the total delay could */
+/* be twice this value */
+#define DIGI_CLOSE_TIMEOUT (5*HZ)
+
+
/* AccelePort USB Defines */
/* ids */
@@ -173,6 +258,9 @@
#define DIGI_FLUSH_RX 2
#define DIGI_RESUME_TX 4 /* clears xoff condition */
+#define DIGI_TRANSMIT_NOT_IDLE 0
+#define DIGI_TRANSMIT_IDLE 1
+
#define DIGI_DISABLE 0
#define DIGI_ENABLE 1
@@ -211,18 +299,29 @@
/* Structures */
typedef struct digi_private {
+ int dp_port_num;
spinlock_t dp_port_lock;
int dp_buf_len;
unsigned char dp_buf[DIGI_PORT_BUF_LEN];
unsigned int dp_modem_signals;
+ int dp_transmit_idle;
+ int dp_in_close;
+ wait_queue_head_t dp_close_wait; /* wait queue for close */
+ struct tq_struct dp_tasks;
} digi_private_t;
/* Local Function Declarations */
-static int digi_write_oob( unsigned char *buf, int count );
+static void digi_wakeup_write( struct usb_serial_port *port );
+static void digi_wakeup_write_lock( struct usb_serial_port *port );
+static int digi_write_oob_command( unsigned char *buf, int count );
+static int digi_write_inb_command( struct usb_serial_port *port,
+ unsigned char *buf, int count ) __attribute__((unused));
static int digi_set_modem_signals( struct usb_serial_port *port,
unsigned int modem_signals );
+static int digi_transmit_idle( struct usb_serial_port *port,
+ unsigned long timeout );
static void digi_rx_throttle (struct usb_serial_port *port);
static void digi_rx_unthrottle (struct usb_serial_port *port);
static void digi_set_termios( struct usb_serial_port *port,
@@ -241,22 +340,24 @@
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( struct urb *urb );
+static void digi_read_oob_callback( struct urb *urb );
/* Statics */
/* device info needed for the Digi serial converter */
-static __u16 digi_vendor_id = DIGI_VENDOR_ID;
-static __u16 digi_product_id = DIGI_ID;
+static u16 digi_vendor_id = DIGI_VENDOR_ID;
+static u16 digi_product_id = DIGI_ID;
/* out of band port */
static int oob_port_num; /* index of out-of-band port */
static struct usb_serial_port *oob_port; /* out-of-band port */
static int device_startup = 0;
-/* startup lock -- used to by digi_startup_device */
-spinlock_t startup_lock;
+spinlock_t startup_lock; /* used by startup_device */
+
+static wait_queue_head_t modem_change_wait;
+static wait_queue_head_t transmit_idle_wait;
/* Globals */
@@ -292,7 +393,84 @@
/* Functions */
/*
-* Digi Write OOB
+* Cond Wait Interruptible Timeout Irqrestore
+*
+* Do spin_unlock_irqrestore and interruptible_sleep_on_timeout
+* so that wake ups are not lost if they occur between the unlock
+* and the sleep. In other words, spin_lock_irqrestore and
+* interruptible_sleep_on_timeout are "atomic" with respect to
+* wake ups. This is used to implement condition variables.
+*/
+
+static long cond_wait_interruptible_timeout_irqrestore(
+ wait_queue_head_t *q, long timeout,
+ spinlock_t *lock, unsigned long flags )
+{
+
+ wait_queue_t wait;
+
+
+ init_waitqueue_entry( &wait, current );
+
+ set_current_state( TASK_INTERRUPTIBLE );
+
+ add_wait_queue( q, &wait );
+
+ spin_unlock_irqrestore( lock, flags );
+
+ timeout = schedule_timeout(timeout);
+
+ set_current_state( TASK_RUNNING );
+
+ remove_wait_queue( q, &wait );
+
+ return( timeout );
+
+}
+
+
+/*
+* Digi Wakeup Write
+*
+* Wake up port, line discipline, and tty processes sleeping
+* on writes.
+*/
+
+static void digi_wakeup_write_lock( struct usb_serial_port *port )
+{
+
+ unsigned long flags;
+ digi_private_t *priv = (digi_private_t *)(port->private);
+
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ digi_wakeup_write( port );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+}
+
+static void digi_wakeup_write( struct usb_serial_port *port )
+{
+
+ struct tty_struct *tty = port->tty;
+
+
+ /* wake up port processes */
+ wake_up_interruptible( &port->write_wait );
+
+ /* wake up line discipline */
+ if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup )
+ (tty->ldisc.write_wakeup)(tty);
+
+ /* wake up other tty processes */
+ wake_up_interruptible( &tty->write_wait );
+
+}
+
+
+/*
+* Digi Write OOB Command
*
* Write commands on the out of band port. Commands are 4
* bytes each, multiple commands can be sent at once, and
@@ -301,28 +479,29 @@
* a negative error returned by usb_submit_urb.
*/
-static int digi_write_oob( unsigned char *buf, int count )
+static int digi_write_oob_command( unsigned char *buf, int count )
{
int ret = 0;
int len;
digi_private_t *oob_priv = (digi_private_t *)(oob_port->private);
+ unsigned long flags = 0;
-dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count );
+dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count );
- spin_lock( &oob_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
while( count > 0 ) {
while( oob_port->write_urb->status == -EINPROGRESS ) {
- spin_unlock( &oob_priv->dp_port_lock );
- interruptible_sleep_on_timeout( &oob_port->write_wait,
- DIGI_RETRY_TIMEOUT );
+ cond_wait_interruptible_timeout_irqrestore(
+ &oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+ &oob_priv->dp_port_lock, flags );
if( signal_pending(current) ) {
return( -EINTR );
}
- spin_lock( &oob_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
}
/* len must be a multiple of 4, so commands are not split */
@@ -337,14 +516,106 @@
count -= len;
buf += len;
} else {
- dbg( "digi_write_oob: usb_submit_urb failed, ret=%d",
- ret );
+ dbg(
+ "digi_write_oob_command: usb_submit_urb failed, ret=%d",
+ ret );
+ break;
+ }
+
+ }
+
+ spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+
+ return( ret );
+
+}
+
+
+/*
+* Digi Write In Band Command
+*
+* Write commands on the given port. Commands are 4
+* bytes each, multiple commands can be sent at once, and
+* no command will be split across USB packets. Returns 0
+* if successful, or a negative error returned by digi_write.
+*/
+
+static int digi_write_inb_command( struct usb_serial_port *port,
+ unsigned char *buf, int count )
+{
+
+ int ret = 0;
+ int len;
+ digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned long flags = 0;
+
+
+dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num,
+count );
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+ while( count > 0 ) {
+
+ while( port->write_urb->status == -EINPROGRESS ) {
+ cond_wait_interruptible_timeout_irqrestore(
+ &port->write_wait, DIGI_RETRY_TIMEOUT,
+ &priv->dp_port_lock, flags );
+ if( signal_pending(current) ) {
+ return( -EINTR );
+ }
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ }
+
+ /* 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 */
+ len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len );
+ if( len > 4 )
+ len &= ~3;
+
+ /* write any buffered data first */
+ if( priv->dp_buf_len > 0 ) {
+ data[0] = DIGI_CMD_SEND_DATA;
+ data[1] = priv->dp_buf_len;
+ memcpy( data+2, priv->dp_buf, priv->dp_buf_len );
+ memcpy( data+2+priv->dp_buf_len, buf, len );
+ port->write_urb->transfer_buffer_length
+ = priv->dp_buf_len+2+len;
+ } else {
+ memcpy( data, buf, len );
+ 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( &oob_priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( ret );
@@ -369,35 +640,35 @@
unsigned char *data = oob_port->write_urb->transfer_buffer;
digi_private_t *port_priv = (digi_private_t *)(port->private);
digi_private_t *oob_priv = (digi_private_t *)(oob_port->private);
+ unsigned long flags = 0;
dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",
-port->number, modem_signals );
+port_priv->dp_port_num, modem_signals );
- spin_lock( &oob_priv->dp_port_lock );
- spin_lock( &port_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+ spin_lock_irqsave( &port_priv->dp_port_lock, flags );
while( oob_port->write_urb->status == -EINPROGRESS ) {
- spin_unlock( &port_priv->dp_port_lock );
- spin_unlock( &oob_priv->dp_port_lock );
- interruptible_sleep_on_timeout( &oob_port->write_wait,
- DIGI_RETRY_TIMEOUT );
+ spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ cond_wait_interruptible_timeout_irqrestore(
+ &oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+ &oob_priv->dp_port_lock, flags );
if( signal_pending(current) ) {
return( -EINTR );
}
- spin_lock( &oob_priv->dp_port_lock );
- spin_lock( &port_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+ spin_lock_irqsave( &port_priv->dp_port_lock, flags );
}
- /* command is 4 bytes: command, line, argument, pad */
data[0] = DIGI_CMD_SET_DTR_SIGNAL;
- data[1] = port->number;
+ data[1] = port_priv->dp_port_num;
data[2] = (modem_signals&TIOCM_DTR) ?
DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE;
data[3] = 0;
data[4] = DIGI_CMD_SET_RTS_SIGNAL;
- data[5] = port->number;
+ data[5] = port_priv->dp_port_num;
data[6] = (modem_signals&TIOCM_RTS) ?
DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE;
data[7] = 0;
@@ -413,18 +684,77 @@
ret );
}
- spin_unlock( &port_priv->dp_port_lock );
- spin_unlock( &oob_priv->dp_port_lock );
+ spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
return( ret );
}
+/*
+* Digi Transmit Idle
+*
+* Digi transmit idle waits, up to timeout ticks, for the transmitter
+* to go idle. It returns 0 if successful or a negative error.
+*
+* There are race conditions here if more than one process is calling
+* digi_transmit_idle on the same port at the same time. However, this
+* is only called from close, and only one process can be in close on a
+* port at a time, so its ok.
+*/
+
+static int digi_transmit_idle( struct usb_serial_port *port,
+ unsigned long timeout )
+{
+
+ int ret;
+ unsigned char buf[2];
+ digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned long flags = 0;
+
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ priv->dp_transmit_idle = 0;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ buf[0] = DIGI_CMD_TRANSMIT_IDLE;
+ buf[1] = 0;
+
+ if( (ret=digi_write_inb_command( port, buf, 2 )) != 0 )
+ return( ret );
+
+ timeout += jiffies;
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+ while( jiffies < timeout && !priv->dp_transmit_idle ) {
+ cond_wait_interruptible_timeout_irqrestore(
+ &transmit_idle_wait, DIGI_RETRY_TIMEOUT,
+ &priv->dp_port_lock, flags );
+ if( signal_pending(current) ) {
+ return( -EINTR );
+ }
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ }
+
+ priv->dp_transmit_idle = 0;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ return( 0 );
+
+}
+
+
static void digi_rx_throttle( struct usb_serial_port *port )
{
-dbg( "digi_rx_throttle: TOP: port=%d", port->number );
+#ifdef DEBUG
+ digi_private_t *priv = (digi_private_t *)(port->private);
+#endif
+
+
+dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num );
/* stop receiving characters. We just turn off the URB request, and
let chars pile up in the device. If we're doing hardware
@@ -441,7 +771,12 @@
static void digi_rx_unthrottle( struct usb_serial_port *port )
{
-dbg( "digi_rx_unthrottle: TOP: port=%d", port->number );
+#ifdef DEBUG
+ digi_private_t *priv = (digi_private_t *)(port->private);
+#endif
+
+
+dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num );
/* just restart the receive interrupt URB */
//if (usb_submit_urb(port->interrupt_in_urb))
@@ -454,6 +789,7 @@
struct termios *old_termios )
{
+ digi_private_t *priv = (digi_private_t *)(port->private);
unsigned int iflag = port->tty->termios->c_iflag;
unsigned int cflag = port->tty->termios->c_cflag;
unsigned int old_iflag = old_termios->c_iflag;
@@ -463,7 +799,7 @@
int i = 0;
-dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", port->number, iflag, old_iflag, cflag, old_cflag );
+dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag );
/* set baud rate */
if( (cflag&CBAUD) != (old_cflag&CBAUD) ) {
@@ -506,7 +842,7 @@
if( arg != -1 ) {
buf[i++] = DIGI_CMD_SET_BAUD_RATE;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
}
@@ -526,7 +862,7 @@
}
buf[i++] = DIGI_CMD_SET_PARITY;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
@@ -550,7 +886,7 @@
if( arg != -1 ) {
buf[i++] = DIGI_CMD_SET_WORD_SIZE;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
}
@@ -566,7 +902,7 @@
arg = DIGI_STOP_BITS_1;
buf[i++] = DIGI_CMD_SET_STOP_BITS;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
@@ -589,7 +925,7 @@
arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS;
buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
@@ -612,7 +948,7 @@
arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS;
buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
@@ -627,13 +963,13 @@
arg = DIGI_DISABLE;
buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
}
- if( (ret=digi_write_oob( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( buf, i )) != 0 )
dbg( "digi_set_termios: write oob failed, ret=%d", ret );
}
@@ -641,7 +977,14 @@
static void digi_break_ctl( struct usb_serial_port *port, int break_state )
{
-dbg( "digi_break_ctl: TOP: port=%d", port->number );
+
+#ifdef DEBUG
+ digi_private_t *priv = (digi_private_t *)(port->private);
+#endif
+
+
+dbg( "digi_break_ctl: TOP: port=%d", priv->dp_port_num );
+
}
@@ -651,16 +994,17 @@
digi_private_t *priv = (digi_private_t *)(port->private);
unsigned int val;
+ unsigned long flags = 0;
-dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd );
+dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd );
switch (cmd) {
case TIOCMGET:
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
val = priv->dp_modem_signals;
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) )
return( -EFAULT );
return( 0 );
@@ -670,12 +1014,12 @@
case TIOCMBIC:
if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) )
return( -EFAULT );
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
if( cmd == TIOCMBIS )
val = priv->dp_modem_signals | val;
else if( cmd == TIOCMBIC )
val = priv->dp_modem_signals & ~val;
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( digi_set_modem_signals( port, val ) );
case TIOCMIWAIT:
@@ -701,15 +1045,17 @@
int ret,data_len,new_len;
digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned long flags = 0;
dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d",
-port->number, count, from_user, in_interrupt() );
+priv->dp_port_num, count, from_user, in_interrupt() );
/* be sure only one write proceeds at a time */
/* there are races on the port private buffer */
/* and races to check write_urb->status */
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
/* wait for urb status clear to submit another urb */
if( port->write_urb->status == -EINPROGRESS ) {
@@ -724,7 +1070,7 @@
new_len = 0;
}
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( new_len );
@@ -736,43 +1082,41 @@
data_len = new_len + priv->dp_buf_len;
if( data_len == 0 ) {
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( 0 );
}
- *((unsigned char *)(port->write_urb->transfer_buffer))
- = (unsigned char)DIGI_CMD_SEND_DATA;
- *((unsigned char *)(port->write_urb->transfer_buffer)+1)
- = (unsigned char)data_len;
-
port->write_urb->transfer_buffer_length = data_len+2;
+ *data++ = DIGI_CMD_SEND_DATA;
+ *data++ = data_len;
+
/* copy in buffered data first */
- memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf,
- priv->dp_buf_len );
+ memcpy( data, priv->dp_buf, priv->dp_buf_len );
+ data += priv->dp_buf_len;
/* copy in new data */
if( from_user ) {
- copy_from_user(
- port->write_urb->transfer_buffer+2+priv->dp_buf_len,
- buf, new_len );
+ if( copy_from_user( data, buf, new_len ) ) {
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return( -EFAULT );
+ }
} else {
- memcpy( port->write_urb->transfer_buffer+2+priv->dp_buf_len,
- buf, new_len );
+ memcpy( data, buf, new_len );
}
#ifdef DEBUG_DATA
-{
+ {
int i;
printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
- port->number, port->write_urb->transfer_buffer_length );
+ 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 ) {
@@ -781,13 +1125,11 @@
} else {
dbg( "digi_write: usb_submit_urb failed, ret=%d",
ret );
- /* no bytes written - should we return the error code or 0? */
- ret = 0;
}
/* return length of new data written, or error */
dbg( "digi_write: returning %d", ret );
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( ret );
}
@@ -798,17 +1140,18 @@
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 ret;
-dbg( "digi_write_bulk_callback: TOP: port=%d", port->number );
+dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
/* handle callback on out-of-band port */
- if( port->number == oob_port_num ) {
+ if( priv->dp_port_num == oob_port_num ) {
dbg( "digi_write_bulk_callback: oob callback" );
+ spin_lock( &priv->dp_port_lock );
wake_up_interruptible( &port->write_wait );
+ spin_unlock( &priv->dp_port_lock );
return;
}
@@ -833,17 +1176,17 @@
priv->dp_buf_len );
#ifdef DEBUG_DATA
-{
+ {
int i;
printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=",
- port->number, port->write_urb->transfer_buffer_length );
+ 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 ) {
@@ -853,19 +1196,17 @@
}
}
- spin_unlock( &priv->dp_port_lock );
- /* wake up port processes */
- wake_up_interruptible( &port->write_wait );
+ /* wake up processes sleeping on writes immediately */
+ digi_wakeup_write( port );
- /* wake up line discipline */
- tty = port->tty;
- if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
- && tty->ldisc.write_wakeup )
- (tty->ldisc.write_wakeup)(tty);
+ spin_unlock( &priv->dp_port_lock );
- /* wake up other tty processes */
- wake_up_interruptible( &tty->write_wait );
+ /* 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 );
}
@@ -875,20 +1216,21 @@
int room;
digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned long flags = 0;
-dbg( "digi_write_room: TOP: port=%d", port->number );
+dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num );
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
if( port->write_urb->status == -EINPROGRESS )
room = 0;
else
room = port->bulk_out_size - 2 - priv->dp_buf_len;
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
-dbg( "digi_write_room: port=%d, room=%d", port->number, room );
+dbg( "digi_write_room: port=%d, room=%d", priv->dp_port_num, room );
return( room );
}
@@ -900,13 +1242,14 @@
digi_private_t *priv = (digi_private_t *)(port->private);
-dbg( "digi_chars_in_buffer: TOP: port=%d", port->number );
+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", port->number, port->bulk_out_size - 2 );
- return( port->bulk_out_size - 2 );
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 );
+ /* return( port->bulk_out_size - 2 ); */
+ return( 256 );
} else {
-dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len );
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_buf_len );
return( priv->dp_buf_len );
}
@@ -916,43 +1259,66 @@
static int digi_open( struct usb_serial_port *port, struct file *filp )
{
- int i = 0;
int ret;
unsigned char buf[32];
digi_private_t *priv = (digi_private_t *)(port->private);
struct termios not_termios;
+ unsigned long flags = 0;
-dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active );
+dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active );
/* be sure the device is started up */
if( digi_startup_device( port->serial ) != 0 )
return( -ENXIO );
- MOD_INC_USE_COUNT;
-
/* if port is already open, just return */
/* be sure exactly one open proceeds */
- spin_lock( &priv->dp_port_lock );
- if( port->active++ ) {
- spin_unlock( &priv->dp_port_lock );
+ 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 );
}
- spin_unlock( &priv->dp_port_lock );
+
+ /* 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 ) {
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return( -EAGAIN );
+ }
+
+ /* prevent other opens from proceeding, before giving up lock */
+ ++port->active;
+
+ /* wait for close 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 );
+ MOD_INC_USE_COUNT;
/* read modem signals automatically whenever they change */
- buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS;
- buf[i++] = port->number;
- buf[i++] = DIGI_ENABLE;
- buf[i++] = 0;
+ buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
+ buf[1] = priv->dp_port_num;
+ buf[2] = DIGI_ENABLE;
+ buf[3] = 0;
/* flush fifos */
- buf[i++] = DIGI_CMD_IFLUSH_FIFO;
- buf[i++] = port->number;
- buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
- buf[i++] = 0;
+ buf[4] = DIGI_CMD_IFLUSH_FIFO;
+ buf[5] = priv->dp_port_num;
+ buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+ buf[7] = 0;
- if( (ret=digi_write_oob( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( buf, 8 )) != 0 )
dbg( "digi_open: write oob failed, ret=%d", ret );
/* set termios settings */
@@ -971,58 +1337,83 @@
static void digi_close( struct usb_serial_port *port, struct file *filp )
{
- int i = 0;
int ret;
unsigned char buf[32];
+ struct tty_struct *tty = port->tty;
digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned long flags = 0;
-dbg( "digi_close: TOP: port %d, active:%d", port->number, port->active );
+dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active );
/* do cleanup only after final close on this port */
- spin_lock( &priv->dp_port_lock );
- if( --port->active ) {
- spin_unlock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ if( port->active > 1 ) {
+ --port->active;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
MOD_DEC_USE_COUNT;
return;
+ } else if( port->active <= 0 ) {
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return;
}
- spin_unlock( &priv->dp_port_lock );
-
+ priv->dp_in_close = 1;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ /* tell line discipline to process only XON/XOFF */
+ tty->closing = 1;
+
+ /* wait for output to drain */
+ if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+ tty_wait_until_sent( tty, DIGI_CLOSE_TIMEOUT );
+ }
+
+ /* flush driver and line discipline buffers */
+ if( tty->driver.flush_buffer )
+ tty->driver.flush_buffer( tty );
+ if( tty->ldisc.flush_buffer )
+ tty->ldisc.flush_buffer( tty );
+
+ /* wait for transmit idle */
+ if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+ digi_transmit_idle( port, DIGI_CLOSE_TIMEOUT );
+ }
+
/* drop DTR and RTS */
digi_set_modem_signals( port, 0 );
/* disable input flow control */
- buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
+ buf[1] = priv->dp_port_num;
+ buf[2] = DIGI_DISABLE;
+ buf[3] = 0;
/* disable output flow control */
- buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
+ buf[5] = priv->dp_port_num;
+ buf[6] = DIGI_DISABLE;
+ buf[7] = 0;
/* disable reading modem signals automatically */
- buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[8] = DIGI_CMD_READ_INPUT_SIGNALS;
+ buf[9] = priv->dp_port_num;
+ buf[10] = DIGI_DISABLE;
+ buf[11] = 0;
/* flush fifos */
- buf[i++] = DIGI_CMD_IFLUSH_FIFO;
- buf[i++] = port->number;
- buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
- buf[i++] = 0;
+ buf[12] = DIGI_CMD_IFLUSH_FIFO;
+ buf[13] = priv->dp_port_num;
+ buf[14] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+ buf[15] = 0;
/* disable receive */
- buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[16] = DIGI_CMD_RECEIVE_ENABLE;
+ buf[17] = priv->dp_port_num;
+ buf[18] = DIGI_DISABLE;
+ buf[19] = 0;
- if( (ret=digi_write_oob( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( buf, 20 )) != 0 )
dbg( "digi_close: write oob failed, ret=%d", ret );
/* wait for final commands on oob port to complete */
@@ -1033,12 +1424,21 @@
break;
}
}
-
+
/* shutdown any outstanding bulk writes */
usb_unlink_urb (port->write_urb);
+ tty->closing = 0;
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ --port->active;
+ priv->dp_in_close = 0;
+ wake_up_interruptible( &priv->dp_close_wait );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
MOD_DEC_USE_COUNT;
+dbg( "digi_close: done" );
}
@@ -1093,6 +1493,8 @@
dbg( "digi_startup: TOP" );
spin_lock_init( &startup_lock );
+ init_waitqueue_head( &modem_change_wait );
+ init_waitqueue_head( &transmit_idle_wait );
/* allocate the private data structures for all ports */
/* number of regular ports + 1 for the out-of-band port */
@@ -1108,8 +1510,14 @@
return( 1 ); /* error */
/* initialize private structure */
+ priv->dp_port_num = i;
priv->dp_buf_len = 0;
priv->dp_modem_signals = 0;
+ 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;
spin_lock_init( &priv->dp_port_lock );
/* initialize write wait queue for this port */
@@ -1157,6 +1565,7 @@
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];
@@ -1164,11 +1573,11 @@
int ret,i;
-dbg( "digi_read_bulk_callback: TOP: port=%d", port->number );
+dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num );
/* handle oob callback */
- if( port->number == oob_port_num ) {
- digi_read_oob( urb );
+ if( priv->dp_port_num == oob_port_num ) {
+ digi_read_oob_callback( urb );
return;
}
@@ -1181,13 +1590,15 @@
if( urb->status ) {
dbg( "digi_read_bulk_callback: nonzero read bulk status: %d",
urb->status );
+ if( urb->status == -ENOENT )
+ return;
goto resubmit;
}
#ifdef DEBUG_DATA
if( urb->actual_length ) {
printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=",
- port->number, urb->actual_length );
+ priv->dp_port_num, urb->actual_length );
for( i=0; i<urb->actual_length; ++i ) {
printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] );
}
@@ -1196,7 +1607,7 @@
#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", port->number, opcode, len, urb->actual_length, status );
+ 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 );
/* receive data */
if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) {
@@ -1209,67 +1620,89 @@
/* continue read */
resubmit:
- if( (ret=usb_submit_urb(urb)) != 0 )
+ if( (ret=usb_submit_urb(urb)) != 0 ) {
dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d",
ret );
+ }
}
-static void digi_read_oob( struct urb *urb )
+static void 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;
- int oob_opcode = ((unsigned char *)urb->transfer_buffer)[0];
- int oob_line = ((unsigned char *)urb->transfer_buffer)[1];
- int oob_status = ((unsigned char *)urb->transfer_buffer)[2];
- int oob_ret = ((unsigned char *)urb->transfer_buffer)[3];
- int ret;
+ int opcode, line, status, val;
+ int i,ret;
-dbg( "digi_read_oob: opcode=%d, line=%d, status=%d, ret=%d", oob_opcode, oob_line, oob_status, oob_ret );
+dbg( "digi_read_oob_callback: len=%d", urb->actual_length );
if( urb->status ) {
- dbg( "digi_read_oob: nonzero read bulk status on oob: %d",
+ dbg( "digi_read_oob_callback: nonzero read bulk status on oob: %d",
urb->status );
+ if( urb->status == -ENOENT )
+ return;
goto resubmit;
}
- if( oob_opcode == DIGI_CMD_READ_INPUT_SIGNALS && oob_status == 0 ) {
+ for( i=0; i<urb->actual_length-3; ) {
- priv = serial->port[oob_line].private;
+ opcode = ((unsigned char *)urb->transfer_buffer)[i++];
+ line = ((unsigned char *)urb->transfer_buffer)[i++];
+ status = ((unsigned char *)urb->transfer_buffer)[i++];
+ val = ((unsigned char *)urb->transfer_buffer)[i++];
- spin_lock( &priv->dp_port_lock );
+dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, line, status, val );
- /* convert from digi flags to termiox flags */
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_CTS )
- priv->dp_modem_signals |= TIOCM_CTS;
- else
- priv->dp_modem_signals &= ~TIOCM_CTS;
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_DSR )
- priv->dp_modem_signals |= TIOCM_DSR;
- else
- priv->dp_modem_signals &= ~TIOCM_DSR;
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_RI )
- priv->dp_modem_signals |= TIOCM_RI;
- else
- priv->dp_modem_signals &= ~TIOCM_RI;
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_DCD )
- priv->dp_modem_signals |= TIOCM_CD;
- else
- priv->dp_modem_signals &= ~TIOCM_CD;
+ if( status != 0 )
+ continue;
- spin_unlock( &priv->dp_port_lock );
+ priv = serial->port[line].private;
+
+ if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) {
+
+ spin_lock( &priv->dp_port_lock );
+
+ /* convert from digi flags to termiox flags */
+ if( val & DIGI_READ_INPUT_SIGNALS_CTS )
+ priv->dp_modem_signals |= TIOCM_CTS;
+ else
+ priv->dp_modem_signals &= ~TIOCM_CTS;
+ if( val & DIGI_READ_INPUT_SIGNALS_DSR )
+ priv->dp_modem_signals |= TIOCM_DSR;
+ else
+ priv->dp_modem_signals &= ~TIOCM_DSR;
+ if( val & DIGI_READ_INPUT_SIGNALS_RI )
+ priv->dp_modem_signals |= TIOCM_RI;
+ else
+ priv->dp_modem_signals &= ~TIOCM_RI;
+ if( val & DIGI_READ_INPUT_SIGNALS_DCD )
+ priv->dp_modem_signals |= TIOCM_CD;
+ else
+ priv->dp_modem_signals &= ~TIOCM_CD;
+
+ wake_up_interruptible( &modem_change_wait );
+ spin_unlock( &priv->dp_port_lock );
+
+ } else if( opcode == DIGI_CMD_TRANSMIT_IDLE ) {
+
+ spin_lock( &priv->dp_port_lock );
+ priv->dp_transmit_idle = 1;
+ wake_up_interruptible( &transmit_idle_wait );
+ spin_unlock( &priv->dp_port_lock );
+
+ }
}
+
resubmit:
if( (ret=usb_submit_urb(urb)) != 0 ) {
- dbg( "digi_read_oob: failed resubmitting oob urb, ret=%d",
+ dbg( "digi_read_oob_callback: failed resubmitting oob urb, ret=%d",
ret );
}
}
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)