patch-2.4.0-test7 linux/drivers/usb/serial/digi_acceleport.c
Next file: linux/drivers/usb/serial/keyspan_pda.c
Previous file: linux/drivers/usb/printer.c
Back to the patch index
Back to the overall index
- Lines: 1408
- Date:
Sun Aug 13 19:23:19 2000
- Orig file:
v2.4.0-test6/linux/drivers/usb/serial/digi_acceleport.c
- Orig date:
Wed Aug 9 19:19:51 2000
diff -u --recursive --new-file v2.4.0-test6/linux/drivers/usb/serial/digi_acceleport.c linux/drivers/usb/serial/digi_acceleport.c
@@ -14,6 +14,58 @@
* Peter Berger (pberger@brimson.com)
* Al Borchers (borchers@steinerpoint.com)
*
+* (8/8/2000) pberger and borchers
+* -- Fixed close so that
+* - it can timeout while waiting for transmit idle, if needed;
+* - it ignores interrupts when flushing the port, turning
+* of modem signalling, and so on;
+* - it waits for the flush to really complete before returning.
+* -- Read_bulk_callback and write_bulk_callback check for a closed
+* port before using the tty struct or writing to the port.
+* -- The two changes above fix the oops caused by interrupted closes.
+* -- Added interruptible args to write_oob_command and set_modem_signals
+* and added a timeout arg to transmit_idle; needed for fixes to
+* close.
+* -- Added code for rx_throttle and rx_unthrottle so that input flow
+* control works.
+* -- Added code to set overrun, parity, framing, and break errors
+* (untested).
+* -- Set USB_DISABLE_SPD flag for write bulk urbs, so no 0 length
+* bulk writes are done. These hung the Digi USB device. The
+* 0 length bulk writes were a new feature of usb-uhci added in
+* the 2.4.0-test6 kernels.
+* -- Fixed mod inc race in open; do mod inc before sleeping to wait
+* for a close to finish.
+*
+* (7/31/2000) pberger
+* -- Fixed bugs with hardware handshaking:
+* - Added code to set/clear tty->hw_stopped in digi_read_oob_callback()
+* and digi_set_termios()
+* -- Added code in digi_set_termios() to
+* - add conditional in code handling transition from B0 to only
+* set RTS if RTS/CTS flow control is either not in use or if
+* the port is not currently throttled.
+* - handle turning off CRTSCTS.
+*
+* (7/30/2000) borchers
+* -- Added support for more than one Digi USB device by moving
+* globals to a private structure in the pointed to from the
+* usb_serial structure.
+* -- Moved the modem change and transmit idle wait queues into
+* the port private structure, so each port has its own queue
+* rather than sharing global queues.
+* -- Added support for break signals.
+*
+* (7/25/2000) pberger
+* -- Added USB-2 support. Note: the USB-2 supports 3 devices: two
+* serial and a parallel port. The parallel port is implemented
+* as a serial-to-parallel converter. That is, the driver actually
+* presents all three USB-2 interfaces as serial ports, but the third
+* one physically connects to a parallel device. Thus, for example,
+* one could plug a parallel printer into the USB-2's third port,
+* but from the kernel's (and userland's) point of view what's
+* actually out there is a serial device.
+*
* (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
@@ -24,8 +76,8 @@
* 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
+* -- Port->active is once again a flag as usb-serial intended it
+* to be, not a count. 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.
@@ -51,7 +103,7 @@
*
* (6/4/2000) pberger and borchers
* -- Replaced separate calls to spin_unlock_irqrestore and
-* interruptible_sleep_on_interruptible with a new function
+* interruptible_sleep_on_timeout 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.
@@ -157,7 +209,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.5 2000/07/18 04:52:43 root Exp $
+* $Id: digi_acceleport.c,v 1.80 2000/08/09 06:36:18 root Exp $
*/
#include <linux/config.h>
@@ -188,9 +240,13 @@
/* Defines */
-/* port buffer length -- must be <= transfer buffer length - 2 */
+/* port output 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 8
+#define DIGI_OUT_BUF_SIZE 8
+
+/* port input buffer length -- must be >= transfer buffer length - 3 */
+/* so we can be sure to hold at least one full buffer from one urb */
+#define DIGI_IN_BUF_SIZE 64
/* retry timeout while sleeping */
#define DIGI_RETRY_TIMEOUT (HZ/10)
@@ -205,7 +261,8 @@
/* ids */
#define DIGI_VENDOR_ID 0x05c5
-#define DIGI_ID 0x0004
+#define DIGI_2_ID 0x0002 /* USB-2 */
+#define DIGI_4_ID 0x0004 /* USB-4 */
/* commands
* "INB": can be used on the in-band endpoint
@@ -335,29 +392,45 @@
/* Structures */
-typedef struct digi_private {
- int dp_port_num;
+typedef struct digi_serial {
+ spinlock_t ds_serial_lock;
+ struct usb_serial_port *ds_oob_port; /* out-of-band port */
+ int ds_oob_port_num; /* index of out-of-band port */
+ int ds_device_started;
+} digi_serial_t;
+
+typedef struct digi_port {
spinlock_t dp_port_lock;
- int dp_buf_len;
- unsigned char dp_buf[DIGI_PORT_BUF_LEN];
+ int dp_port_num;
+ int dp_out_buf_len;
+ unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE];
+ int dp_in_buf_len;
+ unsigned char dp_in_buf[DIGI_IN_BUF_SIZE];
+ unsigned char dp_in_flag_buf[DIGI_IN_BUF_SIZE];
unsigned int dp_modem_signals;
+ wait_queue_head_t dp_modem_change_wait;
int dp_open_count; /* inc on open, dec on close */
int dp_transmit_idle;
+ wait_queue_head_t dp_transmit_idle_wait;
+ int dp_throttled;
+ int dp_throttle_restart;
+ wait_queue_head_t dp_flush_wait;
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;
+} digi_port_t;
/* Local Function Declarations */
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_oob_command( struct usb_serial_port *port,
+ unsigned char *buf, int count, int interruptible );
static int digi_write_inb_command( struct usb_serial_port *port,
- unsigned char *buf, int count ) __attribute__((unused));
+ unsigned char *buf, int count, unsigned long timeout );
static int digi_set_modem_signals( struct usb_serial_port *port,
- unsigned int modem_signals );
+ unsigned int modem_signals, int interruptible );
static int digi_transmit_idle( struct usb_serial_port *port,
unsigned long timeout );
static void digi_rx_throttle (struct usb_serial_port *port);
@@ -386,25 +459,40 @@
/* 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_product_2_id = DIGI_2_ID; /* USB 2 */
+static u16 digi_product_4_id = DIGI_4_ID; /* USB 4 */
-/* 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;
-
-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 */
+static struct usb_serial_device_type digi_acceleport_2_device = {
+ name: "Digi USB",
+ idVendor: &digi_vendor_id,
+ idProduct: &digi_product_2_id,
+ needs_interrupt_in: DONT_CARE,
+ needs_bulk_in: MUST_HAVE,
+ needs_bulk_out: MUST_HAVE,
+ num_interrupt_in: 0,
+ num_bulk_in: 4,
+ num_bulk_out: 4,
+ num_ports: 3,
+ open: digi_open,
+ close: digi_close,
+ write: digi_write,
+ write_room: digi_write_room,
+ write_bulk_callback: digi_write_bulk_callback,
+ read_bulk_callback: digi_read_bulk_callback,
+ chars_in_buffer: digi_chars_in_buffer,
+ throttle: digi_rx_throttle,
+ unthrottle: digi_rx_unthrottle,
+ ioctl: digi_ioctl,
+ set_termios: digi_set_termios,
+ break_ctl: digi_break_ctl,
+ startup: digi_startup,
+ shutdown: digi_shutdown,
+};
-struct usb_serial_device_type digi_acceleport_device = {
+static struct usb_serial_device_type digi_acceleport_4_device = {
name: "Digi USB",
idVendor: &digi_vendor_id,
- idProduct: &digi_product_id,
+ idProduct: &digi_product_4_id,
needs_interrupt_in: DONT_CARE,
needs_bulk_in: MUST_HAVE,
needs_bulk_out: MUST_HAVE,
@@ -479,7 +567,7 @@
{
unsigned long flags;
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
spin_lock_irqsave( &priv->dp_port_lock, flags );
@@ -515,20 +603,23 @@
* Write commands on the out of band 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, -EINTR if interrupted while sleeping, or
-* a negative error returned by usb_submit_urb.
+* if successful, -EINTR if interrupted while sleeping and
+* the interruptible flag is true, or a negative error
+* returned by usb_submit_urb.
*/
-static int digi_write_oob_command( unsigned char *buf, int count )
+static int digi_write_oob_command( struct usb_serial_port *port,
+ unsigned char *buf, int count, int interruptible )
{
int ret = 0;
int len;
- digi_private_t *oob_priv = (digi_private_t *)(oob_port->private);
+ struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port;
+ digi_port_t *oob_priv = (digi_port_t *)oob_port->private;
unsigned long flags = 0;
-dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count );
+dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, count );
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
@@ -538,7 +629,7 @@
cond_wait_interruptible_timeout_irqrestore(
&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
&oob_priv->dp_port_lock, flags );
- if( signal_pending(current) ) {
+ if( interruptible && signal_pending(current) ) {
return( -EINTR );
}
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
@@ -562,8 +653,8 @@
spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
if( ret ) {
- dbg( "digi_write_oob_command: usb_submit_urb failed, ret=%d",
- ret );
+ err( __FUNCTION__ ": usb_submit_urb failed, ret=%d",
+ ret );
}
return( ret );
@@ -576,17 +667,20 @@
*
* 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.
+* no command will be split across USB packets. If timeout
+* is non-zero, write in band command will return after
+* waiting unsuccessfully for the URB status to clear for
+* timeout ticks. 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 )
+ unsigned char *buf, int count, unsigned long timeout )
{
int ret = 0;
int len;
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
unsigned char *data = port->write_urb->transfer_buffer;
unsigned long flags = 0;
@@ -594,11 +688,17 @@
dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num,
count );
+ if( timeout )
+ timeout += jiffies;
+ else
+ timeout = ULONG_MAX;
+
spin_lock_irqsave( &priv->dp_port_lock, flags );
- while( count > 0 ) {
+ while( count > 0 && ret == 0 ) {
- while( port->write_urb->status == -EINPROGRESS ) {
+ while( port->write_urb->status == -EINPROGRESS
+ && jiffies < timeout ) {
cond_wait_interruptible_timeout_irqrestore(
&port->write_wait, DIGI_RETRY_TIMEOUT,
&priv->dp_port_lock, flags );
@@ -611,25 +711,26 @@
/* len must be a multiple of 4 and small enough to */
/* 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 );
+ len = MIN( count, port->bulk_out_size-2-priv->dp_out_buf_len );
if( len > 4 )
len &= ~3;
/* write any buffered data first */
- if( priv->dp_buf_len > 0 ) {
+ if( priv->dp_out_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 );
+ data[1] = priv->dp_out_buf_len;
+ memcpy( data+2, priv->dp_out_buf,
+ priv->dp_out_buf_len );
+ memcpy( data+2+priv->dp_out_buf_len, buf, len );
port->write_urb->transfer_buffer_length
- = priv->dp_buf_len+2+len;
+ = priv->dp_out_buf_len+2+len;
} else {
memcpy( data, buf, len );
port->write_urb->transfer_buffer_length = len;
}
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
- priv->dp_buf_len = 0;
+ priv->dp_out_buf_len = 0;
count -= len;
buf += len;
}
@@ -639,8 +740,8 @@
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
if( ret ) {
- dbg( "digi_write_inb_command: usb_submit_urb failed, ret=%d",
- ret );
+ err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+ ret, priv->dp_port_num );
}
return( ret );
@@ -659,13 +760,14 @@
*/
static int digi_set_modem_signals( struct usb_serial_port *port,
- unsigned int modem_signals )
+ unsigned int modem_signals, int interruptible )
{
int ret;
+ digi_port_t *port_priv = (digi_port_t *)port->private;
+ struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port;
+ digi_port_t *oob_priv = (digi_port_t *)oob_port->private;
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;
@@ -680,7 +782,7 @@
cond_wait_interruptible_timeout_irqrestore(
&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
&oob_priv->dp_port_lock, flags );
- if( signal_pending(current) ) {
+ if( interruptible && signal_pending(current) ) {
return( -EINTR );
}
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
@@ -711,7 +813,7 @@
spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
if( ret ) {
- dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d",
+ err( __FUNCTION__ ": usb_submit_urb failed, ret=%d",
ret );
}
@@ -738,7 +840,7 @@
int ret;
unsigned char buf[2];
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
unsigned long flags = 0;
@@ -749,16 +851,16 @@
buf[0] = DIGI_CMD_TRANSMIT_IDLE;
buf[1] = 0;
- if( (ret=digi_write_inb_command( port, buf, 2 )) != 0 )
- return( ret );
-
timeout += jiffies;
+ if( (ret=digi_write_inb_command( port, buf, 2, timeout-jiffies )) != 0 )
+ return( ret );
+
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_transmit_idle_wait, DIGI_RETRY_TIMEOUT,
&priv->dp_port_lock, flags );
if( signal_pending(current) ) {
return( -EINTR );
@@ -777,21 +879,18 @@
static void digi_rx_throttle( struct usb_serial_port *port )
{
-#ifdef DEBUG
- digi_private_t *priv = (digi_private_t *)(port->private);
-#endif
+ unsigned long flags;
+ digi_port_t *priv = (digi_port_t *)(port->private);
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
- flowcontrol, the device will signal the other end when its buffer
- fills up. If we're doing XON/XOFF, this would be a good time to
- send an XOFF, although it might make sense to foist that off
- upon the device too. */
-
- // usb_unlink_urb(port->interrupt_in_urb);
+ /* stop receiving characters by not resubmitting the read urb */
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ priv->dp_throttled = 1;
+ priv->dp_throttle_restart = 0;
+ priv->dp_in_buf_len = 0;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
}
@@ -799,16 +898,43 @@
static void digi_rx_unthrottle( struct usb_serial_port *port )
{
-#ifdef DEBUG
- digi_private_t *priv = (digi_private_t *)(port->private);
-#endif
+ int ret = 0;
+ int len;
+ unsigned long flags;
+ digi_port_t *priv = (digi_port_t *)(port->private);
+ struct tty_struct *tty = port->tty;
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))
- // dbg( "digi_rx_unthrottle: usb_submit_urb failed" );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+ /* send any buffered chars from throttle time on to tty subsystem */
+ len = MIN( priv->dp_in_buf_len, TTY_FLIPBUF_SIZE - tty->flip.count );
+ if( len > 0 ) {
+ memcpy( tty->flip.char_buf_ptr, priv->dp_in_buf, len );
+ memcpy( tty->flip.flag_buf_ptr, priv->dp_in_flag_buf, len );
+ tty->flip.char_buf_ptr += len;
+ tty->flip.flag_buf_ptr += len;
+ tty->flip.count += len;
+ tty_flip_buffer_push( tty );
+ }
+
+ /* restart read chain */
+ if( priv->dp_throttle_restart )
+ ret = usb_submit_urb( port->read_urb );
+
+ /* turn throttle off */
+ priv->dp_throttled = 0;
+ priv->dp_in_buf_len = 0;
+ priv->dp_throttle_restart = 0;
+
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ if( ret ) {
+ err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+ ret, priv->dp_port_num );
+ }
}
@@ -817,12 +943,13 @@
struct termios *old_termios )
{
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_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;
unsigned int old_cflag = old_termios->c_cflag;
unsigned char buf[32];
+ unsigned int modem_signals;
int arg,ret;
int i = 0;
@@ -837,13 +964,18 @@
/* reassert DTR and (maybe) RTS on transition from B0 */
if( (old_cflag&CBAUD) == B0 ) {
/* don't set RTS if using hardware flow control */
- /* and throttling input -- not implemented yet */
- digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS );
+ /* and throttling input */
+ modem_signals = TIOCM_DTR;
+ if( !(port->tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &port->tty->flags) ) {
+ modem_signals |= TIOCM_RTS;
+ }
+ digi_set_modem_signals( port, modem_signals, 1 );
}
switch( (cflag&CBAUD) ) {
/* drop DTR and RTS on transition to B0 */
- case B0: digi_set_modem_signals( port, 0 ); break;
+ case B0: digi_set_modem_signals( port, 0, 1 ); break;
case B50: arg = DIGI_BAUD_50; break;
case B75: arg = DIGI_BAUD_75; break;
case B110: arg = DIGI_BAUD_110; break;
@@ -947,10 +1079,20 @@
else
arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
- if( (cflag&CRTSCTS) )
+ if( (cflag&CRTSCTS) ) {
+
arg |= DIGI_INPUT_FLOW_CONTROL_RTS;
- else
+
+ /* On USB-4 it is necessary to assert RTS prior */
+ /* to selecting RTS input flow control. */
+ buf[i++] = DIGI_CMD_SET_RTS_SIGNAL;
+ buf[i++] = priv->dp_port_num;
+ buf[i++] = DIGI_RTS_ACTIVE;
+ buf[i++] = 0;
+
+ } else {
arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS;
+ }
buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
buf[i++] = priv->dp_port_num;
@@ -960,8 +1102,8 @@
}
/* set output flow control */
- /*if( (iflag&IXON) != (old_iflag&IXON)
- || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) )*/ {
+ if( (iflag&IXON) != (old_iflag&IXON)
+ || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) {
arg = 0;
@@ -970,10 +1112,12 @@
else
arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
- if( (cflag&CRTSCTS) )
+ if( (cflag&CRTSCTS) ) {
arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS;
- else
+ } else {
arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS;
+ port->tty->hw_stopped = 0;
+ }
buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
buf[i++] = priv->dp_port_num;
@@ -997,7 +1141,7 @@
}
- if( (ret=digi_write_oob_command( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( port, buf, i, 1 )) != 0 )
dbg( "digi_set_termios: write oob failed, ret=%d", ret );
}
@@ -1006,12 +1150,15 @@
static void digi_break_ctl( struct usb_serial_port *port, int break_state )
{
-#ifdef DEBUG
- digi_private_t *priv = (digi_private_t *)(port->private);
-#endif
+ unsigned char buf[4];
+
+ buf[0] = DIGI_CMD_BREAK_CONTROL;
+ buf[1] = 2; /* length */
+ buf[2] = break_state ? 1 : 0;
+ buf[3] = 0; /* pad */
-dbg( "digi_break_ctl: TOP: port=%d", priv->dp_port_num );
+ digi_write_inb_command( port, buf, 4, 0 );
}
@@ -1020,7 +1167,7 @@
unsigned int cmd, unsigned long arg )
{
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
unsigned int val;
unsigned long flags = 0;
@@ -1048,7 +1195,7 @@
else if( cmd == TIOCMBIC )
val = priv->dp_modem_signals & ~val;
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
- return( digi_set_modem_signals( port, val ) );
+ return( digi_set_modem_signals( port, val, 1 ) );
case TIOCMIWAIT:
/* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
@@ -1072,7 +1219,7 @@
{
int ret,data_len,new_len;
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_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;
@@ -1098,9 +1245,10 @@
/* buffer data if count is 1 (probably put_char) if possible */
if( count == 1 ) {
new_len = MIN( count,
- DIGI_PORT_BUF_LEN-priv->dp_buf_len );
- memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len );
- priv->dp_buf_len += new_len;
+ DIGI_OUT_BUF_SIZE-priv->dp_out_buf_len );
+ memcpy( priv->dp_out_buf+priv->dp_out_buf_len, buf,
+ new_len );
+ priv->dp_out_buf_len += new_len;
} else {
new_len = 0;
}
@@ -1113,8 +1261,8 @@
/* allow space for any buffered data and for new data, up to */
/* transfer buffer size - 2 (for command and length bytes) */
- new_len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len );
- data_len = new_len + priv->dp_buf_len;
+ new_len = MIN( count, port->bulk_out_size-2-priv->dp_out_buf_len );
+ data_len = new_len + priv->dp_out_buf_len;
if( data_len == 0 ) {
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
@@ -1127,22 +1275,24 @@
*data++ = data_len;
/* copy in buffered data first */
- memcpy( data, priv->dp_buf, priv->dp_buf_len );
- data += priv->dp_buf_len;
+ memcpy( data, priv->dp_out_buf, priv->dp_out_buf_len );
+ data += priv->dp_out_buf_len;
/* copy in new data */
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;
+ priv->dp_out_buf_len = 0;
}
/* return length of new data written, or error */
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
if( ret < 0 ) {
- dbg( "digi_write: usb_submit_urb failed, ret=%d", ret );
+ err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+ ret, priv->dp_port_num );
}
+
dbg( "digi_write: returning %d", ret );
return( ret );
@@ -1154,21 +1304,27 @@
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial;
- digi_private_t *priv;
+ digi_port_t *priv;
int ret = 0;
-dbg( "digi_write_bulk_callback: TOP" );
+dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status );
- /* port sanity check */
- if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) {
+ /* port and serial sanity check */
+ if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) {
err( __FUNCTION__ ": port or port->private is NULL, status=%d",
urb->status );
return;
}
+ serial = port->serial;
+ if( serial == NULL || serial->private == NULL ) {
+ err( __FUNCTION__ ": serial or serial->private is NULL, status=%d", urb->status );
+ return;
+ }
/* handle oob callback */
- if( priv->dp_port_num == oob_port_num ) {
+ if( priv->dp_port_num
+ == ((digi_serial_t *)(serial->private))->ds_oob_port_num ) {
dbg( "digi_write_bulk_callback: oob callback" );
spin_lock( &priv->dp_port_lock );
wake_up_interruptible( &port->write_wait );
@@ -1176,29 +1332,29 @@
return;
}
- /* sanity checks */
- if( port_paranoia_check( port, "digi_write_bulk_callback" ) )
- return;
- serial = port->serial;
- if( serial_paranoia_check( serial, "digi_write_bulk_callback" ) )
+ /* further sanity checks */
+ if( port_paranoia_check( port, __FUNCTION__ )
+ || serial_paranoia_check( serial, __FUNCTION__ ) )
return;
- /* try to send any buffered data on this port */
+ /* try to send any buffered data on this port, if it is open */
spin_lock( &priv->dp_port_lock );
- if( port->write_urb->status != -EINPROGRESS && priv->dp_buf_len > 0 ) {
+ if( priv->dp_open_count && port->write_urb->status != -EINPROGRESS
+ && priv->dp_out_buf_len > 0 ) {
*((unsigned char *)(port->write_urb->transfer_buffer))
= (unsigned char)DIGI_CMD_SEND_DATA;
*((unsigned char *)(port->write_urb->transfer_buffer)+1)
- = (unsigned char)priv->dp_buf_len;
+ = (unsigned char)priv->dp_out_buf_len;
- port->write_urb->transfer_buffer_length = priv->dp_buf_len+2;
+ port->write_urb->transfer_buffer_length
+ = priv->dp_out_buf_len+2;
- memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf,
- priv->dp_buf_len );
+ memcpy( port->write_urb->transfer_buffer+2, priv->dp_out_buf,
+ priv->dp_out_buf_len );
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
- priv->dp_buf_len = 0;
+ priv->dp_out_buf_len = 0;
}
}
@@ -1224,7 +1380,7 @@
{
int room;
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
unsigned long flags = 0;
@@ -1233,7 +1389,7 @@
if( port->write_urb->status == -EINPROGRESS )
room = 0;
else
- room = port->bulk_out_size - 2 - priv->dp_buf_len;
+ room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
@@ -1246,7 +1402,7 @@
static int digi_chars_in_buffer( struct usb_serial_port *port )
{
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
if( port->write_urb->status == -EINPROGRESS ) {
@@ -1254,8 +1410,8 @@
/* return( port->bulk_out_size - 2 ); */
return( 256 );
} else {
-dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_buf_len );
- return( priv->dp_buf_len );
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_out_buf_len );
+ return( priv->dp_out_buf_len );
}
}
@@ -1266,7 +1422,7 @@
int ret;
unsigned char buf[32];
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)(port->private);
struct termios not_termios;
unsigned long flags = 0;
@@ -1285,12 +1441,18 @@
return( -EAGAIN );
}
+ /* inc module use count before sleeping to wait for closes */
+ ++priv->dp_open_count;
+ MOD_INC_USE_COUNT;
+
/* 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) ) {
+ --priv->dp_open_count;
+ MOD_DEC_USE_COUNT;
return( -EINTR );
}
spin_lock_irqsave( &priv->dp_port_lock, flags );
@@ -1299,16 +1461,12 @@
/* 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 */
+ /* first open, mark port as active */
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 */
@@ -1323,7 +1481,7 @@
buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
buf[7] = 0;
- if( (ret=digi_write_oob_command( buf, 8 )) != 0 )
+ if( (ret=digi_write_oob_command( port, buf, 8, 1 )) != 0 )
dbg( "digi_open: write oob failed, ret=%d", ret );
/* set termios settings */
@@ -1332,7 +1490,7 @@
digi_set_termios( port, ¬_termios );
/* set DTR and RTS */
- digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS );
+ digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS, 1 );
return( 0 );
@@ -1345,7 +1503,7 @@
int ret;
unsigned char buf[32];
struct tty_struct *tty = port->tty;
- digi_private_t *priv = (digi_private_t *)(port->private);
+ digi_port_t *priv = (digi_port_t *)port->private;
unsigned long flags = 0;
@@ -1386,7 +1544,7 @@
}
/* drop DTR and RTS */
- digi_set_modem_signals( port, 0 );
+ digi_set_modem_signals( port, 0, 0 );
/* disable input flow control */
buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
@@ -1406,29 +1564,24 @@
buf[10] = DIGI_DISABLE;
buf[11] = 0;
- /* flush fifos */
- buf[12] = DIGI_CMD_IFLUSH_FIFO;
+ /* disable receive */
+ buf[12] = DIGI_CMD_RECEIVE_ENABLE;
buf[13] = priv->dp_port_num;
- buf[14] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+ buf[14] = DIGI_DISABLE;
buf[15] = 0;
- /* disable receive */
- buf[16] = DIGI_CMD_RECEIVE_ENABLE;
+ /* flush fifos */
+ buf[16] = DIGI_CMD_IFLUSH_FIFO;
buf[17] = priv->dp_port_num;
- buf[18] = DIGI_DISABLE;
+ buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
buf[19] = 0;
- if( (ret=digi_write_oob_command( buf, 20 )) != 0 )
+ if( (ret=digi_write_oob_command( port, buf, 20, 0 )) != 0 )
dbg( "digi_close: write oob failed, ret=%d", ret );
/* wait for final commands on oob port to complete */
- while( oob_port->write_urb->status == -EINPROGRESS ) {
- interruptible_sleep_on_timeout( &oob_port->write_wait,
- DIGI_RETRY_TIMEOUT );
- if( signal_pending(current) ) {
- break;
- }
- }
+ interruptible_sleep_on_timeout( &priv->dp_flush_wait,
+ DIGI_CLOSE_TIMEOUT );
/* shutdown any outstanding bulk writes */
usb_unlink_urb (port->write_urb);
@@ -1458,23 +1611,31 @@
{
int i,ret = 0;
+ digi_serial_t *serial_priv = (digi_serial_t *)serial->private;
+ struct usb_serial_port *port;
/* be sure this happens exactly once */
- spin_lock( &startup_lock );
- if( device_startup ) {
- spin_unlock( &startup_lock );
+ spin_lock( &serial_priv->ds_serial_lock );
+ if( serial_priv->ds_device_started ) {
+ spin_unlock( &serial_priv->ds_serial_lock );
return( 0 );
}
- device_startup = 1;
- spin_unlock( &startup_lock );
+ serial_priv->ds_device_started = 1;
+ spin_unlock( &serial_priv->ds_serial_lock );
/* start reading from each bulk in endpoint for the device */
- for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) {
+ /* set USB_DISABLE_SPD flag for write bulk urbs */
+ for( i=0; i<serial->type->num_ports+1; i++ ) {
+
+ port = &serial->port[i];
- if( (ret=usb_submit_urb(serial->port[i].read_urb)) != 0 ) {
- dbg( "digi_startup_device: usb_submit_urb failed, port=%d, ret=%d",
- i, ret );
+ port->write_urb->transfer_flags |= USB_DISABLE_SPD;
+
+ if( (ret=usb_submit_urb(port->read_urb)) != 0 ) {
+ err(
+ __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+ ret, i );
break;
}
@@ -1489,51 +1650,68 @@
{
int i;
- digi_private_t *priv;
+ digi_port_t *priv;
+ digi_serial_t *serial_priv;
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 */
- for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) {
+ for( i=0; i<serial->type->num_ports+1; i++ ) {
serial->port[i].active = 0;
- /* allocate private structure */
+ /* allocate port private structure */
priv = serial->port[i].private =
- (digi_private_t *)kmalloc( sizeof(digi_private_t),
+ (digi_port_t *)kmalloc( sizeof(digi_port_t),
GFP_KERNEL );
- if( priv == (digi_private_t *)0 )
+ if( priv == (digi_port_t *)0 ) {
+ while( --i >= 0 )
+ kfree( serial->port[i].private );
return( 1 ); /* error */
+ }
- /* initialize private structure */
+ /* initialize port private structure */
+ spin_lock_init( &priv->dp_port_lock );
priv->dp_port_num = i;
- priv->dp_buf_len = 0;
+ priv->dp_out_buf_len = 0;
+ priv->dp_in_buf_len = 0;
priv->dp_modem_signals = 0;
+ init_waitqueue_head( &priv->dp_modem_change_wait );
priv->dp_open_count = 0;
priv->dp_transmit_idle = 0;
+ init_waitqueue_head( &priv->dp_transmit_idle_wait );
+ priv->dp_throttled = 0;
+ priv->dp_throttle_restart = 0;
+ init_waitqueue_head( &priv->dp_flush_wait );
priv->dp_in_close = 0;
init_waitqueue_head( &priv->dp_close_wait );
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 */
- init_waitqueue_head(&serial->port[i].write_wait);
+ init_waitqueue_head( &serial->port[i].write_wait );
}
- /* initialize out of band port info */
- oob_port_num = digi_acceleport_device.num_ports;
- oob_port = &serial->port[oob_port_num];
- device_startup = 0;
+ /* allocate serial private structure */
+ serial_priv = serial->private =
+ (digi_serial_t *)kmalloc( sizeof(digi_serial_t),
+ GFP_KERNEL );
+ if( serial_priv == (digi_serial_t *)0 ) {
+ for( i=0; i<serial->type->num_ports+1; i++ )
+ kfree( serial->port[i].private );
+ return( 1 ); /* error */
+ }
+
+ /* initialize serial private structure */
+ spin_lock_init( &serial_priv->ds_serial_lock );
+ serial_priv->ds_oob_port_num = serial->type->num_ports;
+ serial_priv->ds_oob_port = &serial->port[serial_priv->ds_oob_port_num];
+ serial_priv->ds_device_started = 0;
return( 0 );
@@ -1544,22 +1722,20 @@
{
int i;
- digi_private_t *priv;
+ digi_port_t *priv;
unsigned long flags;
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++ ) {
+ for( i=0; i<serial->type->num_ports+1; i++ ) {
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++ ) {
+ for( i=0; i<serial->type->num_ports; i++ ) {
priv = serial->port[i].private;
spin_lock_irqsave( &priv->dp_port_lock, flags );
while( priv->dp_open_count > 0 ) {
@@ -1571,8 +1747,9 @@
/* 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++ )
+ for( i=0; i<serial->type->num_ports+1; i++ )
kfree( serial->port[i].private );
+ kfree( serial->private );
}
@@ -1581,18 +1758,24 @@
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- digi_private_t *priv;
+ digi_port_t *priv;
int ret;
dbg( "digi_read_bulk_callback: TOP" );
/* port sanity check, do not resubmit if port is not valid */
- if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) {
+ if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) {
err( __FUNCTION__ ": port or port->private is NULL, status=%d",
urb->status );
return;
}
+ if( port->serial == NULL
+ || serial_paranoia_check( port->serial, __FUNCTION__ )
+ || port->serial->private == NULL ) {
+ err( __FUNCTION__ ": serial is bad or serial->private is NULL, status=%d", urb->status );
+ return;
+ }
/* do not resubmit urb if it has any status error */
if( urb->status ) {
@@ -1601,7 +1784,8 @@
}
/* handle oob or inb callback, do not resubmit if error */
- if( priv->dp_port_num == oob_port_num ) {
+ if( priv->dp_port_num
+ == ((digi_serial_t *)(port->serial->private))->ds_oob_port_num ) {
if( digi_read_oob_callback( urb ) != 0 )
return;
} else {
@@ -1623,45 +1807,110 @@
*
* 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.
+* port->private are not NULL and port->serial has been validated.
+* It returns 0 if successful, 1 if successful but the port is
+* throttled, 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);
+ digi_port_t *priv = (digi_port_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;
+ int flag,throttled;
- /* sanity checks */
- if( port_paranoia_check( port, __FUNCTION__ )
- || serial_paranoia_check( serial, __FUNCTION__ ) )
+ /* sanity check */
+ if( port_paranoia_check( port, __FUNCTION__ ) )
return( -1 );
- /* short packet check */
+ /* do not process callbacks on closed ports */
+ /* but do continue the read chain */
+ if( priv->dp_open_count == 0 )
+ return( 0 );
+
+ /* short/multiple 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 );
+ err( __FUNCTION__ ": INCOMPLETE OR MULTIPLE 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 );
}
+ spin_lock( &priv->dp_port_lock );
+
+ /* check for throttle; if set, do not resubmit read urb */
+ /* indicate the read chain needs to be restarted on unthrottle */
+ throttled = priv->dp_throttled;
+ if( throttled )
+ priv->dp_throttle_restart = 1;
+
/* receive data */
- if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) {
- len = MIN( len, urb->actual_length-3 );
- for( i=0; i<len; ++i ) {
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
+ if( opcode == DIGI_CMD_RECEIVE_DATA ) {
+
+ /* get flag from status */
+ flag = 0;
+
+ /* overrun is special, not associated with a char */
+ if( status & DIGI_OVERRUN_ERROR ) {
+ tty_insert_flip_char( tty, 0, TTY_OVERRUN );
+ }
+
+ /* break takes precedence over parity, */
+ /* which takes precedence over framing errors */
+ if( status & DIGI_BREAK_ERROR ) {
+ flag = TTY_BREAK;
+ } else if( status & DIGI_PARITY_ERROR ) {
+ flag = TTY_PARITY;
+ } else if( status & DIGI_FRAMING_ERROR ) {
+ flag = TTY_FRAME;
+ }
+
+ /* data length is len-1 (one byte of len is status) */
+ --len;
+
+ if( throttled ) {
+
+ len = MIN( len,
+ DIGI_IN_BUF_SIZE - priv->dp_in_buf_len );
+
+ if( len > 0 ) {
+ memcpy( priv->dp_in_buf + priv->dp_in_buf_len,
+ data, len );
+ memset( priv->dp_in_flag_buf
+ + priv->dp_in_buf_len, flag, len );
+ priv->dp_in_buf_len += len;
+ }
+
+ } else {
+
+ len = MIN( len, TTY_FLIPBUF_SIZE - tty->flip.count );
+
+ if( len > 0 ) {
+ memcpy( tty->flip.char_buf_ptr, data, len );
+ memset( tty->flip.flag_buf_ptr, flag, len );
+ tty->flip.char_buf_ptr += len;
+ tty->flip.flag_buf_ptr += len;
+ tty->flip.count += len;
+ tty_flip_buffer_push( tty );
+ }
+
+ }
+
}
- return( 0 );
+ spin_unlock( &priv->dp_port_lock );
+
+ if( opcode == DIGI_CMD_RECEIVE_DISABLE ) {
+ dbg( __FUNCTION__ ": got RECEIVE_DISABLE" );
+ } else if( opcode != DIGI_CMD_RECEIVE_DATA ) {
+ dbg( __FUNCTION__ ": unknown opcode: %d", opcode );
+ }
+
+ return( throttled ? 1 : 0 );
}
@@ -1670,8 +1919,9 @@
* 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.
+* When called we know port and port->private are not NULL and
+* the port->serial is valid. It returns 0 if successful, and
+* -1 if the sanity checks failed.
*/
static int digi_read_oob_callback( struct urb *urb )
@@ -1679,19 +1929,13 @@
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);
+ digi_port_t *priv = (digi_port_t *)(port->private);
int opcode, line, status, val;
int i;
-dbg( "digi_read_oob_callback: len=%d", urb->actual_length );
-
- /* sanity check */
- if( serial == NULL ) {
- err( __FUNCTION__ ": port->serial is NULL, status=%d, port=%d",
- urb->status, priv->dp_port_num );
- return( -1 );
- }
+dbg( "digi_read_oob_callback: port=%d, len=%d", priv->dp_port_num,
+urb->actual_length );
/* handle each oob command */
for( i=0; i<urb->actual_length-3; ) {
@@ -1701,25 +1945,39 @@
status = ((unsigned char *)urb->transfer_buffer)[i++];
val = ((unsigned char *)urb->transfer_buffer)[i++];
-dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, line, status, val );
+dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d",
+opcode, line, status, val );
- if( status != 0 )
+ if( status != 0 || line >= serial->type->num_ports )
continue;
- if( (priv=serial->port[line].private) == NULL ) {
- dbg( __FUNCTION__ ": port[%d].private is NULL!", line );
- continue;
- }
+ port = &serial->port[line];
+
+ if( port_paranoia_check( port, __FUNCTION__ )
+ || (priv=port->private) == NULL )
+ return( -1 );
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 )
+ if( val & DIGI_READ_INPUT_SIGNALS_CTS ) {
priv->dp_modem_signals |= TIOCM_CTS;
- else
+ /* port must be open to use tty struct */
+ if( priv->dp_open_count
+ && port->tty->termios->c_cflag & CRTSCTS ) {
+ port->tty->hw_stopped = 0;
+ digi_wakeup_write( port );
+ }
+ } else {
priv->dp_modem_signals &= ~TIOCM_CTS;
+ /* port must be open to use tty struct */
+ if( priv->dp_open_count
+ && port->tty->termios->c_cflag & CRTSCTS ) {
+ port->tty->hw_stopped = 1;
+ }
+ }
if( val & DIGI_READ_INPUT_SIGNALS_DSR )
priv->dp_modem_signals |= TIOCM_DSR;
else
@@ -1733,16 +1991,20 @@
else
priv->dp_modem_signals &= ~TIOCM_CD;
- wake_up_interruptible( &modem_change_wait );
+ wake_up_interruptible( &priv->dp_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 );
+ wake_up_interruptible( &priv->dp_transmit_idle_wait );
spin_unlock( &priv->dp_port_lock );
+ } else if( opcode == DIGI_CMD_IFLUSH_FIFO ) {
+
+ wake_up_interruptible( &priv->dp_flush_wait );
+
}
}
@@ -1754,19 +2016,22 @@
int digi_init (void)
{
- usb_serial_register (&digi_acceleport_device);
+ usb_serial_register (&digi_acceleport_2_device);
+ usb_serial_register (&digi_acceleport_4_device);
return 0;
}
void digi_exit (void)
{
- usb_serial_deregister (&digi_acceleport_device);
+ usb_serial_deregister (&digi_acceleport_2_device);
+ usb_serial_deregister (&digi_acceleport_4_device);
}
module_init(digi_init);
module_exit(digi_exit);
+
MODULE_AUTHOR("Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>");
MODULE_DESCRIPTION("Digi AccelePort USB-4 Serial Converter driver");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)