diff options
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r-- | drivers/usb/serial/Makefile | 22 | ||||
-rw-r--r-- | drivers/usb/serial/digi_acceleport.c | 1164 | ||||
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/keyspan_pda.c | 4 | ||||
-rw-r--r-- | drivers/usb/serial/omninet.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/usbserial.c | 4 | ||||
-rw-r--r-- | drivers/usb/serial/visor.c | 7 | ||||
-rw-r--r-- | drivers/usb/serial/whiteheat.c | 6 |
8 files changed, 756 insertions, 463 deletions
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index fa69fa52a..7d24c34f9 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -4,8 +4,26 @@ O_TARGET := usb-serial.o M_OBJS := usb-serial.o -O_OBJS := usbserial.o visor.o whiteheat.o ftdi_sio.o keyspan_pda.o omninet.o digi_acceleport.o +O_OBJS := usbserial.o MOD_LIST_NAME := USB_SERIAL_MODULES +ifeq ($(CONFIG_USB_SERIAL_VISOR),y) + O_OBJS += visor.o +endif +ifeq ($(CONFIG_USB_SERIAL_WHITEHEAT),y) + O_OBJS += whiteheat.o +endif +ifeq ($(CONFIG_USB_SERIAL_FTDI_SIO),y) + O_OBJS += ftdi_sio.o +endif +ifeq ($(CONFIG_USB_SERIAL_KEYSPAN_PDA),y) + O_OBJS += keyspan_pda.o +endif +ifeq ($(CONFIG_USB_SERIAL_OMNINET),y) + O_OBJS += omninet.o +endif +ifeq ($(CONFIG_USB_SERIAL_DIGI_ACCELEPORT),y) + O_OBJS += digi_acceleport.o +endif + include $(TOPDIR)/Rules.make - diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6700f9e01..fe2d2013b 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -14,16 +14,39 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (5/16/2000) pberger and borchers +* -- added timeouts to sleeps +* -- handle transition to/from B0 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 +* -- 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) +* +* (5/10/2000) pberger and borchers +* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls +* -- 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. +* -- Added more port conditioning calls in digi_open and digi_close. +* -- Convert port->active to a use count so that we can deal with multiple +* opens and closes properly. +* -- Fixed some problems with the locking code. +* * (5/3/2000) pberger and borchers -* First alpha version of the driver--many known limitations and bugs. +* -- First alpha version of the driver--many known limitations and bugs. * -* $Id: digi_acceleport.c,v 1.28 2000/05/04 01:47:08 root Exp root $ +* $Id: digi_acceleport.c,v 1.43 2000/05/17 03:21:38 root Exp root $ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -37,45 +60,57 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/usb.h> +#include "usb-serial.h" #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG #else #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 16 + +/* retry timeout while waiting for urb->status to go to 0 */ +#define DIGI_RETRY_TIMEOUT (HZ/10) /* AccelePort USB Defines */ /* ids */ -#define DIGI_VENDOR_ID 0x05c5 -#define DIGI_ID 0x0004 - -/* commands */ -#define DIGI_CMD_SET_BAUD_RATE 0 -#define DIGI_CMD_SET_WORD_SIZE 1 -#define DIGI_CMD_SET_PARITY 2 -#define DIGI_CMD_SET_STOP_BITS 3 -#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 -#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 -#define DIGI_CMD_SET_DTR_SIGNAL 6 -#define DIGI_CMD_SET_RTS_SIGNAL 7 -#define DIGI_CMD_RECEIVE_ENABLE 10 -#define DIGI_CMD_BREAK_CONTROL 11 -#define DIGI_CMD_LOCAL_LOOPBACK 12 -#define DIGI_CMD_TRANSMIT_IDLE 13 -#define DIGI_CMD_WRITE_UART_REGISTER 15 -#define DIGI_CMD_AND_UART_REGISTER 16 -#define DIGI_CMD_OR_UART_REGISTER 17 -#define DIGI_CMD_SEND_DATA 18 +#define DIGI_VENDOR_ID 0x05c5 +#define DIGI_ID 0x0004 + +/* commands + * "INB": can be used on the in-band endpoint + * "OOB": can be used on the out-of-band endpoint + */ +#define DIGI_CMD_SET_BAUD_RATE 0 /* INB, OOB */ +#define DIGI_CMD_SET_WORD_SIZE 1 /* INB, OOB */ +#define DIGI_CMD_SET_PARITY 2 /* INB, OOB */ +#define DIGI_CMD_SET_STOP_BITS 3 /* INB, OOB */ +#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 /* INB, OOB */ +#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 /* INB, OOB */ +#define DIGI_CMD_SET_DTR_SIGNAL 6 /* INB, OOB */ +#define DIGI_CMD_SET_RTS_SIGNAL 7 /* INB, OOB */ +#define DIGI_CMD_READ_INPUT_SIGNALS 8 /* OOB */ +#define DIGI_CMD_IFLUSH_FIFO 9 /* OOB */ +#define DIGI_CMD_RECEIVE_ENABLE 10 /* INB, OOB */ +#define DIGI_CMD_BREAK_CONTROL 11 /* INB, OOB */ +#define DIGI_CMD_LOCAL_LOOPBACK 12 /* INB, OOB */ +#define DIGI_CMD_TRANSMIT_IDLE 13 /* INB, OOB */ +#define DIGI_CMD_READ_UART_REGISTER 14 /* OOB */ +#define DIGI_CMD_WRITE_UART_REGISTER 15 /* INB, OOB */ +#define DIGI_CMD_AND_UART_REGISTER 16 /* INB, OOB */ +#define DIGI_CMD_OR_UART_REGISTER 17 /* INB, OOB */ +#define DIGI_CMD_SEND_DATA 18 /* INB */ +#define DIGI_CMD_RECEIVE_DATA 19 /* INB */ +#define DIGI_CMD_RECEIVE_DISABLE 20 /* INB */ +#define DIGI_CMD_GET_PORT_TYPE 21 /* OOB */ /* baud rates */ #define DIGI_BAUD_50 0 @@ -102,10 +137,71 @@ #define DIGI_BAUD_230400 21 #define DIGI_BAUD_460800 22 -/* flow control arguments */ -#define DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL 1 -#define DIGI_ENABLE_RTS_CTS_FLOW_CONTROL 2 -#define DIGI_ENABLE_DTR_DSR_FLOW_CONTROL 4 +/* arguments */ +#define DIGI_WORD_SIZE_5 0 +#define DIGI_WORD_SIZE_6 1 +#define DIGI_WORD_SIZE_7 2 +#define DIGI_WORD_SIZE_8 3 + +#define DIGI_PARITY_NONE 0 +#define DIGI_PARITY_ODD 1 +#define DIGI_PARITY_EVEN 2 +#define DIGI_PARITY_MARK 3 +#define DIGI_PARITY_SPACE 4 + +#define DIGI_STOP_BITS_1 0 +#define DIGI_STOP_BITS_2 1 + +#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_INPUT_FLOW_CONTROL_RTS 2 +#define DIGI_INPUT_FLOW_CONTROL_DTR 4 + +#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_OUTPUT_FLOW_CONTROL_CTS 2 +#define DIGI_OUTPUT_FLOW_CONTROL_DSR 4 + +#define DIGI_DTR_INACTIVE 0 +#define DIGI_DTR_ACTIVE 1 +#define DIGI_DTR_INPUT_FLOW_CONTROL 2 + +#define DIGI_RTS_INACTIVE 0 +#define DIGI_RTS_ACTIVE 1 +#define DIGI_RTS_INPUT_FLOW_CONTROL 2 +#define DIGI_RTS_TOGGLE 3 + +#define DIGI_FLUSH_TX 1 +#define DIGI_FLUSH_RX 2 +#define DIGI_RESUME_TX 4 /* clears xoff condition */ + +#define DIGI_DISABLE 0 +#define DIGI_ENABLE 1 + +#define DIGI_DEASSERT 0 +#define DIGI_ASSERT 1 + +/* in band status codes */ +#define DIGI_OVERRUN_ERROR 4 +#define DIGI_PARITY_ERROR 8 +#define DIGI_FRAMING_ERROR 16 +#define DIGI_BREAK_ERROR 32 + +/* out of band status */ +#define DIGI_NO_ERROR 0 +#define DIGI_BAD_FIRST_PARAMETER 1 +#define DIGI_BAD_SECOND_PARAMETER 2 +#define DIGI_INVALID_LINE 3 +#define DIGI_INVALID_OPCODE 4 + +/* input signals */ +#define DIGI_READ_INPUT_SIGNALS_SLOT 1 +#define DIGI_READ_INPUT_SIGNALS_ERR 2 +#define DIGI_READ_INPUT_SIGNALS_BUSY 4 +#define DIGI_READ_INPUT_SIGNALS_PE 8 +#define DIGI_READ_INPUT_SIGNALS_CTS 16 +#define DIGI_READ_INPUT_SIGNALS_DSR 32 +#define DIGI_READ_INPUT_SIGNALS_RI 64 +#define DIGI_READ_INPUT_SIGNALS_DCD 128 + /* macros */ #define MAX(a,b) (((a)>(b))?(a):(b)) @@ -117,32 +213,21 @@ typedef struct digi_private { spinlock_t dp_port_lock; int dp_buf_len; - char dp_buf[32]; + unsigned char dp_buf[DIGI_PORT_BUF_LEN]; + unsigned int dp_modem_signals; } digi_private_t; -struct s_digiusb { - u8 opcode; - u8 length; - u8 val; - u8 pad; -}; - /* Local Function Declarations */ -static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, - int length, int val ); -static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ); +static int digi_write_oob( unsigned char *buf, int count ); +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ); static void digi_rx_throttle (struct usb_serial_port *port); static void digi_rx_unthrottle (struct usb_serial_port *port); -static int digi_setbaud( struct usb_serial_port *port, int baud ); static void digi_set_termios( struct usb_serial_port *port, struct termios *old_termios ); static void digi_break_ctl( struct usb_serial_port *port, int break_state ); -static int digi_get_modem_info( struct usb_serial *serial, - unsigned char *value ); -static int digi_set_modem_info( struct usb_serial *serial, - unsigned char value ); static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ); static int digi_write( struct usb_serial_port *port, int from_user, @@ -152,138 +237,186 @@ static int digi_write_room( struct usb_serial_port *port ); static int digi_chars_in_buffer( struct usb_serial_port *port ); static int digi_open( struct usb_serial_port *port, struct file *filp ); static void digi_close( struct usb_serial_port *port, struct file *filp ); -static int digi_startup (struct usb_serial *serial); +static int digi_startup_device( struct usb_serial *serial ); +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 ); /* 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 control port */ -static int oob_read_started = 0; +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; -/* config lock -- used to protect digi statics and globals, like oob vars */ -spinlock_t config_lock; +/* startup lock -- used to by digi_startup_device */ +spinlock_t startup_lock; /* Globals */ struct usb_serial_device_type digi_acceleport_device = { - name: "Digi USB", - idVendor: &digi_vendor_id, - idProduct: &digi_product_id, - needs_interrupt_in: DONT_CARE, - needs_bulk_in: MUST_HAVE, - needs_bulk_out: MUST_HAVE, - num_interrupt_in: 0, - num_bulk_in: 5, - num_bulk_out: 5, - num_ports: 4, - 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, + name: "Digi USB", + idVendor: &digi_vendor_id, + idProduct: &digi_product_id, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 0, + num_bulk_in: 5, + num_bulk_out: 5, + num_ports: 4, + 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, }; /* Functions */ -/* Send message on the out-of-Band endpoint */ -static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ) +/* +* Digi Write OOB +* +* 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. +*/ + +static int digi_write_oob( unsigned char *buf, int count ) { - int ret; - struct s_digiusb digiusb; - digi_private_t *priv = (digi_private_t *)(oob_port->private); + int ret = 0; + int len; + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); -dbg( "digi_send_oob: TOP: from '%s', opcode: %d, linenum:%d, data1: %d, data2: %d", mes, opcode, linenum, data1, data2 ); - digiusb.opcode = (u8)opcode; - digiusb.length = (u8)linenum; - digiusb.val = (u8)data1; - digiusb.pad = (u8)data2; +dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count ); - spin_lock( &priv->dp_port_lock ); + spin_lock( &oob_priv->dp_port_lock ); - while (oob_port->write_urb->status == -EINPROGRESS) { -dbg( "digi_send_oob: opcode:%d already writing...", opcode ); - spin_unlock( &priv->dp_port_lock ); - interruptible_sleep_on(&oob_port->write_wait); - if (signal_pending(current)) { - return; + 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 ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock( &oob_priv->dp_port_lock ); + } + + /* len must be a multiple of 4, so commands are not split */ + len = MIN( count, oob_port->bulk_out_size ); + if( len > 4 ) + len &= ~3; + + memcpy( oob_port->write_urb->transfer_buffer, buf, len ); + oob_port->write_urb->transfer_buffer_length = len; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + count -= len; + buf += len; + } else { + dbg( "digi_write_oob: usb_submit_urb failed, ret=%d", + ret ); + break; } - spin_lock( &priv->dp_port_lock ); - } - memcpy( oob_port->write_urb->transfer_buffer, &digiusb, sizeof(digiusb) ); - oob_port->write_urb->transfer_buffer_length = sizeof(digiusb); - if( (ret=usb_submit_urb(oob_port->write_urb)) != 0 ) { - dbg( - "digi_send_oob: usb_submit_urb(write bulk) failed, opcode=%d, ret=%d", - opcode, ret ); } - spin_unlock( &priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); -dbg( "digi_send_oob: opcode %d done", opcode ); + return( ret ); } -static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, - int length, int val ) +/* +* Digi Set Modem Signals +* +* Sets or clears DTR and RTS on the port, according to the +* modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags +* for the modem_signals argument. Returns 0 if successful, +* -EINTR if interrupted while sleeping, or a non-zero error +* returned by usb_submit_urb. +*/ + +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ) { int ret; - struct s_digiusb digiusb; - digi_private_t *priv = (digi_private_t *)(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); -dbg( "digi_send_cmd: TOP: from '%s', opcode: %d, val: %d", mes, opcode, val ); +dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x", +port->number, modem_signals ); - digiusb.opcode = (u8)opcode; - digiusb.length = (u8)length; - digiusb.val = (u8)val; - digiusb.pad = 0; + spin_lock( &oob_priv->dp_port_lock ); + spin_lock( &port_priv->dp_port_lock ); - spin_lock( &priv->dp_port_lock ); - - while( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_send_cmd: opcode=%d already writing...", opcode ); - spin_unlock( &priv->dp_port_lock ); - interruptible_sleep_on( &port->write_wait ); + 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 ); if( signal_pending(current) ) { - return; + return( -EINTR ); } - spin_lock( &priv->dp_port_lock ); + spin_lock( &oob_priv->dp_port_lock ); + spin_lock( &port_priv->dp_port_lock ); } - memcpy( port->write_urb->transfer_buffer, &digiusb, sizeof(digiusb) ); - port->write_urb->transfer_buffer_length = sizeof(digiusb); - if( (ret=usb_submit_urb(port->write_urb)) != 0 ) - dbg( - "digi_send_cmd: usb_submit_urb(write bulk) failed, opcode=%d, ret=%d", - opcode, ret ); + /* command is 4 bytes: command, line, argument, pad */ + data[0] = DIGI_CMD_SET_DTR_SIGNAL; + data[1] = port->number; + 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[6] = (modem_signals&TIOCM_RTS) ? + DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; + data[7] = 0; + + oob_port->write_urb->transfer_buffer_length = 8; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + port_priv->dp_modem_signals = + (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) + | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); + } else { + dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", + ret ); + } -dbg( "digi_send_cmd: opcode %d done", opcode ); + spin_unlock( &port_priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); - spin_unlock( &priv->dp_port_lock ); + return( ret ); } @@ -312,52 +445,7 @@ dbg( "digi_rx_unthrottle: TOP: port=%d", port->number ); /* just restart the receive interrupt URB */ //if (usb_submit_urb(port->interrupt_in_urb)) - // dbg( "digi_rx_unthrottle: usb_submit_urb(read urb) failed" ); - -} - - -static int digi_setbaud( struct usb_serial_port *port, int baud ) -{ - - int bindex; - - -dbg( "digi_setbaud: TOP: port=%d", port->number ); - - switch( baud ) { - case 50: bindex = DIGI_BAUD_50; break; - case 75: bindex = DIGI_BAUD_75; break; - case 110: bindex = DIGI_BAUD_110; break; - case 150: bindex = DIGI_BAUD_150; break; - case 200: bindex = DIGI_BAUD_200; break; - case 300: bindex = DIGI_BAUD_300; break; - case 600: bindex = DIGI_BAUD_600; break; - case 1200: bindex = DIGI_BAUD_1200; break; - case 1800: bindex = DIGI_BAUD_1800; break; - case 2400: bindex = DIGI_BAUD_2400; break; - case 4800: bindex = DIGI_BAUD_4800; break; - case 7200: bindex = DIGI_BAUD_7200; break; - case 9600: bindex = DIGI_BAUD_9600; break; - case 14400: bindex = DIGI_BAUD_14400; break; - case 19200: bindex = DIGI_BAUD_19200; break; - case 28800: bindex = DIGI_BAUD_28800; break; - case 38400: bindex = DIGI_BAUD_38400; break; - case 57600: bindex = DIGI_BAUD_57600; break; - case 76800: bindex = DIGI_BAUD_76800; break; - case 115200: bindex = DIGI_BAUD_115200; break; - case 153600: bindex = DIGI_BAUD_153600; break; - case 230400: bindex = DIGI_BAUD_230400; break; - case 460800: bindex = DIGI_BAUD_460800; break; - default: - dbg( "digi_setbaud: can't handle requested baud rate %d", baud ); - return( -EINVAL ); - break; - } - - digi_send_cmd( "digi_setbaud:", port, DIGI_CMD_SET_BAUD_RATE, 2, bindex ); - - return( 0 ); /* FIX -- send_cmd should return a value??, return it */ + // dbg( "digi_rx_unthrottle: usb_submit_urb failed" ); } @@ -367,167 +455,243 @@ static void digi_set_termios( struct usb_serial_port *port, { unsigned int iflag = port->tty->termios->c_iflag; - unsigned int old_iflag = old_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; - int arg; + unsigned char buf[32]; + int arg,ret; + 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 ); /* set baud rate */ - /* if( (cflag&CBAUD) != (old_cflag&CBAUD) ) */ { + if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { + + arg = -1; + + /* 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 ); + } + switch( (cflag&CBAUD) ) { - case B50: digi_setbaud(port, 50); break; - case B75: digi_setbaud(port, 75); break; - case B110: digi_setbaud(port, 110); break; - case B150: digi_setbaud(port, 150); break; - case B200: digi_setbaud(port, 200); break; - case B300: digi_setbaud(port, 300); break; - case B600: digi_setbaud(port, 600); break; - case B1200: digi_setbaud(port, 1200); break; - case B1800: digi_setbaud(port, 1800); break; - case B2400: digi_setbaud(port, 2400); break; - case B4800: digi_setbaud(port, 4800); break; - case B9600: digi_setbaud(port, 9600); break; - case B19200: digi_setbaud(port, 19200); break; - case B38400: digi_setbaud(port, 38400); break; - case B57600: digi_setbaud(port, 57600); break; - case B115200: digi_setbaud(port, 115200); break; - default: - dbg( "digi_set_termios: can't handle baud rate 0x%x", - (cflag&CBAUD) ); - break; + /* drop DTR and RTS on transition to B0 */ + case B0: digi_set_modem_signals( port, 0 ); break; + case B50: arg = DIGI_BAUD_50; break; + case B75: arg = DIGI_BAUD_75; break; + case B110: arg = DIGI_BAUD_110; break; + case B150: arg = DIGI_BAUD_150; break; + case B200: arg = DIGI_BAUD_200; break; + case B300: arg = DIGI_BAUD_300; break; + case B600: arg = DIGI_BAUD_600; break; + case B1200: arg = DIGI_BAUD_1200; break; + case B1800: arg = DIGI_BAUD_1800; break; + case B2400: arg = DIGI_BAUD_2400; break; + case B4800: arg = DIGI_BAUD_4800; break; + case B9600: arg = DIGI_BAUD_9600; break; + case B19200: arg = DIGI_BAUD_19200; break; + case B38400: arg = DIGI_BAUD_38400; break; + case B57600: arg = DIGI_BAUD_57600; break; + case B115200: arg = DIGI_BAUD_115200; break; + case B230400: arg = DIGI_BAUD_230400; break; + case B460800: arg = DIGI_BAUD_460800; break; + default: + dbg( "digi_set_termios: can't handle baud rate 0x%x", + (cflag&CBAUD) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_BAUD_RATE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set parity */ + if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { + + if( (cflag&PARENB) ) { + if( (cflag&PARODD) ) + arg = DIGI_PARITY_ODD; + else + arg = DIGI_PARITY_EVEN; + } else { + arg = DIGI_PARITY_NONE; } + + buf[i++] = DIGI_CMD_SET_PARITY; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set word size */ + if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { + + arg = -1; + + switch( (cflag&CSIZE) ) { + case CS5: arg = DIGI_WORD_SIZE_5; break; + case CS6: arg = DIGI_WORD_SIZE_6; break; + case CS7: arg = DIGI_WORD_SIZE_7; break; + case CS8: arg = DIGI_WORD_SIZE_8; break; + default: + dbg( "digi_set_termios: can't handle word size %d", + (cflag&CSIZE) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_WORD_SIZE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set stop bits */ + if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) { + + if( (cflag&CSTOPB) ) + arg = DIGI_STOP_BITS_2; + else + arg = DIGI_STOP_BITS_1; + + buf[i++] = DIGI_CMD_SET_STOP_BITS; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } /* set input flow control */ - /* if( (iflag&IXOFF) != (old_iflag&IXOFF) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + if( (iflag&IXOFF) != (old_iflag&IXOFF) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { arg = 0; if( (iflag&IXOFF) ) - arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + if( (cflag&CRTSCTS) ) - arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + arg |= DIGI_INPUT_FLOW_CONTROL_RTS; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS; - digi_send_cmd( "digi_termios: set input flow control:", port, - DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, arg ); + buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; } /* 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; if( (iflag&IXON) ) - arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + if( (cflag&CRTSCTS) ) - arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; - digi_send_cmd( "digi_set_termios: set output flow control:", port, - DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, arg ); + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; } -} + /* set receive enable/disable */ + if( (cflag&CREAD) != (old_cflag&CREAD) ) { + if( (cflag&CREAD) ) + arg = DIGI_ENABLE; + else + arg = DIGI_DISABLE; -static void digi_break_ctl( struct usb_serial_port *port, int break_state ) -{ -dbg( "digi_break_ctl: TOP: port=%d", port->number ); -} + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } -/* modem control pins: DTR and RTS are outputs and can be controlled; - DCD, RI, DSR, CTS are inputs and can be read */ + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_set_termios: write oob failed, ret=%d", ret ); -static int digi_get_modem_info( struct usb_serial *serial, - unsigned char *value ) -{ -dbg( "digi_get_modem_info: TOP" ); - return( 0 ); } -static int digi_set_modem_info( struct usb_serial *serial, - unsigned char value ) +static void digi_break_ctl( struct usb_serial_port *port, int break_state ) { -dbg( "digi_set_modem_info: TOP" ); - return( 0 ); +dbg( "digi_break_ctl: TOP: port=%d", port->number ); } static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ) { - struct usb_serial *serial = port->serial; - int rc; - unsigned int value; - unsigned char status, mask; + + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned int val; + dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd ); -return( -ENOIOCTLCMD ); switch (cmd) { - case TIOCMGET: /* get modem pins state */ - rc = digi_get_modem_info(serial, &status); - if (rc < 0) - return rc; - value = - ((status & (1<<7)) ? TIOCM_DTR : 0) | - ((status & (1<<6)) ? TIOCM_CAR : 0) | - ((status & (1<<5)) ? TIOCM_RNG : 0) | - ((status & (1<<4)) ? TIOCM_DSR : 0) | - ((status & (1<<3)) ? TIOCM_CTS : 0) | - ((status & (1<<2)) ? TIOCM_RTS : 0); - if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) - return -EFAULT; - return 0; - case TIOCMSET: /* set a state as returned by MGET */ - if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) - return -EFAULT; - status = - ((value & TIOCM_DTR) ? (1<<7) : 0) | - ((value & TIOCM_CAR) ? (1<<6) : 0) | - ((value & TIOCM_RNG) ? (1<<5) : 0) | - ((value & TIOCM_DSR) ? (1<<4) : 0) | - ((value & TIOCM_CTS) ? (1<<3) : 0) | - ((value & TIOCM_RTS) ? (1<<2) : 0); - rc = digi_set_modem_info(serial, status); - if (rc < 0) - return rc; - return 0; - case TIOCMBIS: /* set bits in bitmask <arg> */ - case TIOCMBIC: /* clear bits from bitmask <arg> */ - if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) - return -EFAULT; - rc = digi_get_modem_info(serial, &status); - if (rc < 0) - return rc; - mask = - ((value & TIOCM_RTS) ? (1<<2) : 0) | - ((value & TIOCM_DTR) ? (1<<7) : 0); - if (cmd == TIOCMBIS) - status |= mask; - else - status &= ~mask; - rc = digi_set_modem_info(serial, status); - if (rc < 0) - return rc; - return 0; + + case TIOCMGET: + spin_lock( &priv->dp_port_lock ); + val = priv->dp_modem_signals; + spin_unlock( &priv->dp_port_lock ); + if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) ) + return( -EFAULT ); + return( 0 ); + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) ) + return( -EFAULT ); + spin_lock( &priv->dp_port_lock ); + 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 ); + return( digi_set_modem_signals( port, val ) ); + case TIOCMIWAIT: /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ /* TODO */ + return( 0 ); + case TIOCGICOUNT: /* return count of modemline transitions */ - return 0; /* TODO */ + /* TODO */ + return 0; + } - - return -ENOIOCTLCMD; + + return( -ENOIOCTLCMD ); + } @@ -535,7 +699,7 @@ static int digi_write( struct usb_serial_port *port, int from_user, const unsigned char *buf, int count ) { - int i,ret,data_len,new_len; + int ret,data_len,new_len; digi_private_t *priv = (digi_private_t *)(port->private); @@ -550,16 +714,18 @@ port->number, count, from_user, in_interrupt() ); /* wait for urb status clear to submit another urb */ if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_write: -EINPROGRESS set" ); - - /* buffer the data if possible */ - 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; + /* 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; + } else { + new_len = 0; + } - /* unlock and return number of bytes buffered */ spin_unlock( &priv->dp_port_lock ); -dbg( "digi_write: buffering, return %d", new_len ); + return( new_len ); } @@ -569,19 +735,16 @@ dbg( "digi_write: buffering, return %d", new_len ); new_len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); data_len = new_len + priv->dp_buf_len; -dbg( "digi_write: counts: new data %d, buf data %d, total data %d (max %d)", new_len, priv->dp_buf_len, data_len, port->bulk_out_size-2 ); - - /* nothing to send */ if( data_len == 0 ) { spin_unlock( &priv->dp_port_lock ); return( 0 ); } - /* set command and length bytes */ - *((u8 *)(port->write_urb->transfer_buffer)) = (u8)DIGI_CMD_SEND_DATA; - *((u8 *)(port->write_urb->transfer_buffer)+1) = (u8)data_len; + *((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; - /* set total transfer buffer length */ port->write_urb->transfer_buffer_length = data_len+2; /* copy in buffered data first */ @@ -590,33 +753,34 @@ dbg( "digi_write: counts: new data %d, buf data %d, total data %d (max %d)", new /* copy in new data */ if( from_user ) { - copy_from_user( port->write_urb->transfer_buffer+2+priv->dp_buf_len, + copy_from_user( + port->write_urb->transfer_buffer+2+priv->dp_buf_len, buf, new_len ); - } - else { + } else { memcpy( port->write_urb->transfer_buffer+2+priv->dp_buf_len, buf, new_len ); } -#ifdef DEBUG - printk( KERN_DEBUG __FILE__ ": digi_write: length=%d, data=", - port->write_urb->transfer_buffer_length ); +#ifdef DEBUG_DATA +{ + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", + port->number, 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] ); + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); } printk( "\n" ); +} #endif - /* submit urb */ if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - /* submit successful, return length of new data written */ ret = new_len; - /* clear buffer */ priv->dp_buf_len = 0; - } - else { - dbg( "digi_write: usb_submit_urb(write bulk) failed, ret=%d", ret ); + } else { + dbg( "digi_write: usb_submit_urb failed, ret=%d", + ret ); /* no bytes written - should we return the error code or 0? */ ret = 0; } @@ -658,28 +822,34 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); spin_lock( &priv->dp_port_lock ); if( port->write_urb->status != -EINPROGRESS && priv->dp_buf_len > 0 ) { - /* set command and length bytes */ - *((u8 *)(port->write_urb->transfer_buffer)) - = (u8)DIGI_CMD_SEND_DATA; - *((u8 *)(port->write_urb->transfer_buffer)+1) - = (u8)priv->dp_buf_len; + *((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; - /* set total transfer buffer length */ port->write_urb->transfer_buffer_length = priv->dp_buf_len+2; - /* copy in buffered data */ memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, priv->dp_buf_len ); - /* submit urb */ -dbg( "digi_write_bulk_callback: submit urb to write buffer, data len=%d", -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 ); + 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 ) { - /* successful, clear buffer */ priv->dp_buf_len = 0; - } - else { - dbg( "digi_write_bulk_callback: usb_submit_urb(write bulk) failed, ret=%d", ret ); + } else { + dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret ); } } @@ -690,7 +860,8 @@ priv->dp_buf_len ); /* wake up line discipline */ tty = port->tty; - if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty); /* wake up other tty processes */ @@ -717,7 +888,7 @@ dbg( "digi_write_room: TOP: port=%d", port->number ); spin_unlock( &priv->dp_port_lock ); -dbg( "digi_write_room: return room=%d", room ); +dbg( "digi_write_room: port=%d, room=%d", port->number, room ); return( room ); } @@ -732,11 +903,10 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) dbg( "digi_chars_in_buffer: TOP: port=%d", port->number ); if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_chars_in_buffer: return=%d", port->bulk_out_size ); - return( port->bulk_out_size ); - } - else { -dbg( "digi_chars_in_buffer: return=%d", priv->dp_buf_len ); +dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, port->bulk_out_size - 2 ); + return( port->bulk_out_size - 2 ); + } else { +dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len ); return( priv->dp_buf_len ); } @@ -746,61 +916,53 @@ dbg( "digi_chars_in_buffer: return=%d", priv->dp_buf_len ); 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; + + +dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active ); + /* be sure the device is started up */ + if( digi_startup_device( port->serial ) != 0 ) + return( -ENXIO ); -dbg( "digi_open: TOP: port %d", port->number ); + MOD_INC_USE_COUNT; /* if port is already open, just return */ - /* be sure exactly one open succeeds */ + /* be sure exactly one open proceeds */ spin_lock( &priv->dp_port_lock ); - if( port->active ) { + if( port->active++ ) { + spin_unlock( &priv->dp_port_lock ); return( 0 ); } - port->active = 1; spin_unlock( &priv->dp_port_lock ); - /* start reading from the out-of-band port for the device */ - /* be sure this happens exactly once */ - spin_lock( &config_lock ); - if( !oob_read_started ) { - if( (ret=usb_submit_urb(oob_port->read_urb)) != 0 ) { - dbg( "digi_open: usb_submit_urb(read bulk) for oob failed, ret=%d", - ret ); - spin_unlock( &config_lock ); - return( -ENXIO ); - } - else { -dbg( "digi_open: usb_submit_urb(read bulk) for oob succeeded" ); - oob_read_started = 1; - } - } - spin_unlock( &config_lock ); - - /* initialize port */ -dbg( "digi_open: init..." ); - /* set 9600, 8N1, DTR, RTS, RX enable, no input or output flow control */ - digi_setbaud( port, 9600 ); - digi_send_cmd( "digi_open: wordsize", port, DIGI_CMD_SET_WORD_SIZE, 2, 3 ); - digi_send_cmd( "digi_open: parity", port, DIGI_CMD_SET_PARITY, 2, 0 ); - digi_send_cmd( "digi_open: stopbits", port, DIGI_CMD_SET_STOP_BITS, 2, 0 ); - digi_send_cmd( "digi_open: DTR on", port, DIGI_CMD_SET_DTR_SIGNAL, 2, 1 ); - digi_send_cmd( "digi_open: RTS on", port, DIGI_CMD_SET_RTS_SIGNAL, 2, 1 ); - digi_send_cmd( "digi_open: RX enable on", port, DIGI_CMD_RECEIVE_ENABLE, 2, - 1 ); - digi_send_cmd( "digi_open: input flow control off", port, - DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, 0 ); - digi_send_cmd( "digi_open: output flow control off", port, - DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, 0 ); - - /* start reading from the device */ - if( (ret=usb_submit_urb(port->read_urb)) != 0 ) { - dbg( "digi_open: usb_submit_urb(read bulk) failed, ret=%d", ret ); - return( -ENXIO ); - } + /* 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; + + /* flush fifos */ + buf[i++] = DIGI_CMD_IFLUSH_FIFO; + buf[i++] = port->number; + buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[i++] = 0; + + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_open: write oob failed, ret=%d", ret ); + + /* set termios settings */ + not_termios.c_cflag = ~port->tty->termios->c_cflag; + not_termios.c_iflag = ~port->tty->termios->c_iflag; + digi_set_termios( port, ¬_termios ); + + /* set DTR and RTS */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); -dbg( "digi_open: done" ); return( 0 ); } @@ -809,39 +971,119 @@ dbg( "digi_open: done" ); static void digi_close( struct usb_serial_port *port, struct file *filp ) { -dbg( "digi_close: TOP: port %d", port->number ); - - /* Need to change the control lines here */ - /* TODO */ -dbg( "digi_close: wanna clear DTR and RTS..." ); + int i = 0; + int ret; + unsigned char buf[32]; + digi_private_t *priv = (digi_private_t *)(port->private); -//digi_send_cmd( "digi_close DTR off", port, 6, 2, 0); // clear DTR -//digi_send_cmd( "digi_close RTS off", port, 7, 2, 0); // clear RTS -//digi_send_cmd( "digi_close RX disable", port, 10, 2, 0); // Rx Disable -digi_send_oob( "digi_close RTS off", DIGI_CMD_SET_RTS_SIGNAL, - port->number, 0, 0 ); // clear RTS -digi_send_oob( "digi_close DTR off", DIGI_CMD_SET_DTR_SIGNAL, - port->number, 0, 0 ); // clear DTR +dbg( "digi_close: TOP: port %d, active:%d", port->number, 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 ); + MOD_DEC_USE_COUNT; + return; + } + spin_unlock( &priv->dp_port_lock ); + + /* 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; + + /* disable output flow control */ + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* disable reading modem signals automatically */ + buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* flush fifos */ + buf[i++] = DIGI_CMD_IFLUSH_FIFO; + buf[i++] = port->number; + buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[i++] = 0; + + /* disable receive */ + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + if( (ret=digi_write_oob( buf, i )) != 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 ) { -dbg ("digi_close: waiting for final writes to complete on oob port %d...", oob_port->number ); - interruptible_sleep_on( &oob_port->write_wait ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); if( signal_pending(current) ) { break; } } - /* shutdown our bulk reads and writes */ + /* shutdown any outstanding bulk writes */ usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->active = 0; + MOD_DEC_USE_COUNT; + +} + + +/* +* Digi Startup Device +* +* Starts reads on all ports. Must be called AFTER startup, with +* urbs initialized. Returns 0 if successful, non-zero error otherwise. +*/ + +static int digi_startup_device( struct usb_serial *serial ) +{ + + int i,ret = 0; + + + spin_lock( &startup_lock ); + + /* be sure this happens exactly once */ + if( device_startup ) { + spin_unlock( &startup_lock ); + return( 0 ); + } + + /* start reading from each bulk in endpoint for the device */ + for( i=0; i<digi_acceleport_device.num_ports+1; 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 ); + break; + } + + } + + device_startup = 1; + + spin_unlock( &startup_lock ); + + return( ret ); } -static int digi_startup (struct usb_serial *serial) +static int digi_startup( struct usb_serial *serial ) { int i; @@ -850,22 +1092,24 @@ static int digi_startup (struct usb_serial *serial) dbg( "digi_startup: TOP" ); - /* initialize config lock */ - spin_lock_init( &config_lock ); + spin_lock_init( &startup_lock ); /* 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++ ) { + serial->port[i].active = 0; + /* allocate private structure */ priv = serial->port[i].private = - (digi_private_t *)kmalloc( sizeof(struct digi_private), + (digi_private_t *)kmalloc( sizeof(digi_private_t), GFP_KERNEL ); if( priv == (digi_private_t *)0 ) - return( 1 ); /* error */ + return( 1 ); /* error */ /* initialize private structure */ priv->dp_buf_len = 0; + priv->dp_modem_signals = 0; spin_lock_init( &priv->dp_port_lock ); /* initialize write wait queue for this port */ @@ -876,7 +1120,7 @@ dbg( "digi_startup: TOP" ); /* initialize out of band port info */ oob_port_num = digi_acceleport_device.num_ports; oob_port = &serial->port[oob_port_num]; - oob_read_started = 0; + device_startup = 0; return( 0 ); @@ -891,10 +1135,13 @@ static void digi_shutdown( struct usb_serial *serial ) dbg( "digi_shutdown: TOP" ); - /* stop writing and reading from the out-of-band port */ - usb_unlink_urb( oob_port->write_urb ); - usb_unlink_urb( oob_port->read_urb ); - oob_read_started = 0; + /* 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); + } + + device_startup = 0; /* free the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ @@ -910,7 +1157,10 @@ static void digi_read_bulk_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; - unsigned char *data = urb->transfer_buffer; + 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; @@ -918,58 +1168,108 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", port->number ); /* handle oob callback */ if( port->number == oob_port_num ) { -dbg( "digi_read_bulk_callback: oob_port callback, opcode=%d, line=%d, status=%d, ret=%d", data[0], data[1], data[2], data[3] ); - if( urb->status ) { - dbg( "digi_read_bulk_callback: nonzero read bulk status on oob: %d", - urb->status ); - } - if( (ret=usb_submit_urb(urb)) != 0 ) { - dbg( "digi_read_bulk_callback: failed resubmitting oob urb, ret=%d", - ret ); - } + digi_read_oob( urb ); return; } /* sanity checks */ if( port_paranoia_check( port, "digi_read_bulk_callback" ) || serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) { - return; + goto resubmit; } - /* check status */ if( urb->status ) { dbg( "digi_read_bulk_callback: nonzero read bulk status: %d", urb->status ); - return; + goto resubmit; } -#ifdef DEBUG - if (urb->actual_length) { - printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: length=%d, data=", - urb->actual_length ); - for( i=0; i<urb->actual_length; ++i ) { - printk( "%.2x ", data[i] ); - } - printk( "\n" ); +#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 ); + for( i=0; i<urb->actual_length; ++i ) { + printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] ); } + printk( "\n" ); +} #endif - /* Digi read packets are: */ - /* 0 1 2 3 4 ... 3+length-1 == 2+length*/ - /* opcode, length, status, data[0], data[1]...data[length-2] */ - if( urb->actual_length > 3 ) { - for( i=3; i<2+data[1]; ++i ) { + 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 ); + + /* 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); } - /* Continue trying to read */ + /* continue read */ +resubmit: if( (ret=usb_submit_urb(urb)) != 0 ) - dbg( "digi_read_bulk_callback: failed resubmitting read urb, ret=%d", + dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d", ret ); } -#endif /* CONFIG_USB_SERIAL_DIGI_ACCELEPORT */ + +static void digi_read_oob( 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; + + +dbg( "digi_read_oob: opcode=%d, line=%d, status=%d, ret=%d", oob_opcode, oob_line, oob_status, oob_ret ); + + if( urb->status ) { + dbg( "digi_read_oob: nonzero read bulk status on oob: %d", + urb->status ); + goto resubmit; + } + + if( oob_opcode == DIGI_CMD_READ_INPUT_SIGNALS && oob_status == 0 ) { + + priv = serial->port[oob_line].private; + + spin_lock( &priv->dp_port_lock ); + + /* 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; + + spin_unlock( &priv->dp_port_lock ); + + } + +resubmit: + if( (ret=usb_submit_urb(urb)) != 0 ) { + dbg( "digi_read_oob: failed resubmitting oob urb, ret=%d", + ret ); + } + +} diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index cdad3cabe..8aec73569 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -32,9 +32,6 @@ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_FTDI_SIO - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -723,6 +720,3 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns return 0; } /* ftdi_sio_ioctl */ -#endif /* CONFIG_USB_SERIAL_FTDI_SIO */ - - diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index f91e1b592..c18137ffa 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -18,9 +18,6 @@ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -700,4 +697,3 @@ struct usb_serial_device_type keyspan_pda_device = { shutdown: keyspan_pda_shutdown, }; -#endif /* CONFIG_USB_SERIAL_KEYSPAN_PDA */ diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 9eb6767f0..2e5e50849 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -13,9 +13,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_OMNINET - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -336,6 +333,3 @@ static void omninet_write_bulk_callback (struct urb *urb) return; } -#endif /* CONFIG_USB_SERIAL_OMNINET */ - - diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 9fe65ae8d..b2e18bd8e 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/22/2000) gkh + * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be + * removed from the individual device source files. + * * (05/03/2000) gkh * Added the Digi Acceleport driver from Al Borchers and Peter Berger. * diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 979a56f01..5cfb4c42a 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -20,9 +20,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_VISOR - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -207,7 +204,3 @@ static int visor_startup (struct usb_serial *serial) return (0); } - -#endif /* CONFIG_USB_SERIAL_VISOR*/ - - diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 58f761fbf..c168efa02 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -21,9 +21,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_WHITEHEAT - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -407,6 +404,3 @@ static void whiteheat_shutdown (struct usb_serial *serial) return; } -#endif /* CONFIG_USB_SERIAL_WHITEHEAT */ - - |