diff options
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r-- | drivers/usb/serial/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/serial/digi_acceleport.c | 975 | ||||
-rw-r--r-- | drivers/usb/serial/ezusb_convert.pl | 4 | ||||
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/keyspan_pda.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/omninet.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.h | 5 | ||||
-rw-r--r-- | drivers/usb/serial/usbserial.c | 309 | ||||
-rw-r--r-- | drivers/usb/serial/visor.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/visor.h | 2 | ||||
-rw-r--r-- | drivers/usb/serial/whiteheat.c | 169 | ||||
-rw-r--r-- | drivers/usb/serial/whiteheat.h | 169 |
12 files changed, 1440 insertions, 207 deletions
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index bd4d51a6a..fa69fa52a 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -4,7 +4,7 @@ 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 +O_OBJS := usbserial.o visor.o whiteheat.o ftdi_sio.o keyspan_pda.o omninet.o digi_acceleport.o MOD_LIST_NAME := USB_SERIAL_MODULES include $(TOPDIR)/Rules.make diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c new file mode 100644 index 000000000..6700f9e01 --- /dev/null +++ b/drivers/usb/serial/digi_acceleport.c @@ -0,0 +1,975 @@ +/* +* Digi AccelePort USB-4 Serial Converter +* +* Copyright 2000 by Digi International +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's +* usb-serial driver. +* +* Peter Berger (pberger@brimson.com) +* Al Borchers (borchers@steinerpoint.com) +* +* (5/3/2000) pberger and borchers +* 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 $ +*/ + +#include <linux/config.h> + +#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/fcntl.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/spinlock.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 + +/* 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 + +/* baud rates */ +#define DIGI_BAUD_50 0 +#define DIGI_BAUD_75 1 +#define DIGI_BAUD_110 2 +#define DIGI_BAUD_150 3 +#define DIGI_BAUD_200 4 +#define DIGI_BAUD_300 5 +#define DIGI_BAUD_600 6 +#define DIGI_BAUD_1200 7 +#define DIGI_BAUD_1800 8 +#define DIGI_BAUD_2400 9 +#define DIGI_BAUD_4800 10 +#define DIGI_BAUD_7200 11 +#define DIGI_BAUD_9600 12 +#define DIGI_BAUD_14400 13 +#define DIGI_BAUD_19200 14 +#define DIGI_BAUD_28800 15 +#define DIGI_BAUD_38400 16 +#define DIGI_BAUD_57600 17 +#define DIGI_BAUD_76800 18 +#define DIGI_BAUD_115200 19 +#define DIGI_BAUD_153600 20 +#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 + +/* macros */ +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + + +/* Structures */ + +typedef struct digi_private { + spinlock_t dp_port_lock; + int dp_buf_len; + char dp_buf[32]; +} 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 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, + const unsigned char *buf, int count ); +static void digi_write_bulk_callback( struct urb *urb ); +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 void digi_shutdown( struct usb_serial *serial ); +static void digi_read_bulk_callback( struct urb *urb ); + + +/* Statics */ + +/* device info needed for the Digi serial converter */ +static __u16 digi_vendor_id = DIGI_VENDOR_ID; +static __u16 digi_product_id = DIGI_ID; + +/* 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; + +/* config lock -- used to protect digi statics and globals, like oob vars */ +spinlock_t config_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, +}; + + +/* Functions */ + +/* Send message on the out-of-Band endpoint */ +static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ) +{ + int ret; + struct s_digiusb digiusb; + digi_private_t *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; + + spin_lock( &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; + } + 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 ); + +dbg( "digi_send_oob: opcode %d done", opcode ); + +} + + +static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, + int length, int val ) +{ + + int ret; + struct s_digiusb digiusb; + digi_private_t *priv = (digi_private_t *)(port->private); + + +dbg( "digi_send_cmd: TOP: from '%s', opcode: %d, val: %d", mes, opcode, val ); + + digiusb.opcode = (u8)opcode; + digiusb.length = (u8)length; + digiusb.val = (u8)val; + digiusb.pad = 0; + + 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 ); + if( signal_pending(current) ) { + return; + } + spin_lock( &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 ); + +dbg( "digi_send_cmd: opcode %d done", opcode ); + + spin_unlock( &priv->dp_port_lock ); + +} + + +static void digi_rx_throttle( struct usb_serial_port *port ) +{ + +dbg( "digi_rx_throttle: TOP: port=%d", port->number ); + + /* 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); + +} + + +static void digi_rx_unthrottle( struct usb_serial_port *port ) +{ + +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 */ + +} + + +static void digi_set_termios( struct usb_serial_port *port, + struct termios *old_termios ) +{ + + 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_cflag = old_termios->c_cflag; + int arg; + + +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) ) */ { + 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; + } + } + + /* set input flow control */ + /* if( (iflag&IXOFF) != (old_iflag&IXOFF) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + + arg = 0; + + if( (iflag&IXOFF) ) + arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + if( (cflag&CRTSCTS) ) + arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + + digi_send_cmd( "digi_termios: set input flow control:", port, + DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, arg ); + + } + + /* set output flow control */ + /* if( (iflag&IXON) != (old_iflag&IXON) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + + arg = 0; + + if( (iflag&IXON) ) + arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + if( (cflag&CRTSCTS) ) + arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + + digi_send_cmd( "digi_set_termios: set output flow control:", port, + DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, arg ); + + } + +} + + +static void digi_break_ctl( struct usb_serial_port *port, int break_state ) +{ +dbg( "digi_break_ctl: TOP: port=%d", port->number ); +} + + +/* modem control pins: DTR and RTS are outputs and can be controlled; + DCD, RI, DSR, CTS are inputs and can be read */ + +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 ) +{ +dbg( "digi_set_modem_info: TOP" ); + return( 0 ); +} + + +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; + +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 TIOCMIWAIT: + /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ + /* TODO */ + case TIOCGICOUNT: + /* return count of modemline transitions */ + return 0; /* TODO */ + } + + return -ENOIOCTLCMD; +} + + +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; + digi_private_t *priv = (digi_private_t *)(port->private); + + +dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d", +port->number, count, from_user, in_interrupt() ); + + /* be sure only one write proceeds at a time */ + /* there are races on the port private buffer */ + /* and races to check write_urb->status */ + spin_lock( &priv->dp_port_lock ); + + /* 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; + + /* unlock and return number of bytes buffered */ + spin_unlock( &priv->dp_port_lock ); +dbg( "digi_write: buffering, return %d", new_len ); + return( new_len ); + + } + + /* 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; + +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; + + /* set total transfer buffer length */ + port->write_urb->transfer_buffer_length = data_len+2; + + /* copy in buffered data first */ + memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, + priv->dp_buf_len ); + + /* copy in new data */ + if( from_user ) { + copy_from_user( port->write_urb->transfer_buffer+2+priv->dp_buf_len, + buf, new_len ); + } + 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 ); + for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((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 ); + /* no bytes written - should we return the error code or 0? */ + ret = 0; + } + + /* return length of new data written, or error */ +dbg( "digi_write: returning %d", ret ); + spin_unlock( &priv->dp_port_lock ); + return( ret ); + +} + + +static void digi_write_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; + digi_private_t *priv = (digi_private_t *)(port->private); + int ret; + + +dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); + + /* handle callback on out-of-band port */ + if( port->number == oob_port_num ) { + dbg( "digi_write_bulk_callback: oob callback" ); + wake_up_interruptible( &port->write_wait ); + return; + } + + /* sanity checks */ + if( port_paranoia_check( port, "digi_write_bulk_callback" ) + || serial_paranoia_check( serial, "digi_write_bulk_callback" ) ) { + return; + } + + /* try to send any buffered data on this port */ + 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; + + /* 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 ); + 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 ); + } + + } + spin_unlock( &priv->dp_port_lock ); + + /* wake up port processes */ + wake_up_interruptible( &port->write_wait ); + + /* wake up line discipline */ + tty = port->tty; + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) + (tty->ldisc.write_wakeup)(tty); + + /* wake up other tty processes */ + wake_up_interruptible( &tty->write_wait ); + +} + + +static int digi_write_room( struct usb_serial_port *port ) +{ + + int room; + digi_private_t *priv = (digi_private_t *)(port->private); + + +dbg( "digi_write_room: TOP: port=%d", port->number ); + + spin_lock( &priv->dp_port_lock ); + + if( port->write_urb->status == -EINPROGRESS ) + room = 0; + else + room = port->bulk_out_size - 2 - priv->dp_buf_len; + + spin_unlock( &priv->dp_port_lock ); + +dbg( "digi_write_room: return room=%d", room ); + return( room ); + +} + + +static int digi_chars_in_buffer( struct usb_serial_port *port ) +{ + + digi_private_t *priv = (digi_private_t *)(port->private); + + +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 ); + return( priv->dp_buf_len ); + } + +} + + +static int digi_open( struct usb_serial_port *port, struct file *filp ) +{ + + int ret; + digi_private_t *priv = (digi_private_t *)(port->private); + + +dbg( "digi_open: TOP: port %d", port->number ); + + /* if port is already open, just return */ + /* be sure exactly one open succeeds */ + spin_lock( &priv->dp_port_lock ); + if( port->active ) { + 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 ); + } + +dbg( "digi_open: done" ); + return( 0 ); + +} + + +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..." ); + +//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 + + 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 ); + if( signal_pending(current) ) { + break; + } + } + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + + port->active = 0; + +} + + +static int digi_startup (struct usb_serial *serial) +{ + + int i; + digi_private_t *priv; + + +dbg( "digi_startup: TOP" ); + + /* initialize config lock */ + spin_lock_init( &config_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++ ) { + + /* allocate private structure */ + priv = serial->port[i].private = + (digi_private_t *)kmalloc( sizeof(struct digi_private), + GFP_KERNEL ); + if( priv == (digi_private_t *)0 ) + return( 1 ); /* error */ + + /* initialize private structure */ + priv->dp_buf_len = 0; + spin_lock_init( &priv->dp_port_lock ); + + /* initialize write wait queue for this port */ + 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]; + oob_read_started = 0; + + return( 0 ); + +} + + +static void digi_shutdown( struct usb_serial *serial ) +{ + + int i; + + +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; + + /* 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++ ) + kfree( serial->port[i].private ); + +} + + +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 ret,i; + + +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 ); + } + return; + } + + /* sanity checks */ + if( port_paranoia_check( port, "digi_read_bulk_callback" ) + || serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) { + return; + } + + /* check status */ + if( urb->status ) { + dbg( "digi_read_bulk_callback: nonzero read bulk status: %d", + urb->status ); + return; + } + +#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" ); + } +#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 ) { + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + /* Continue trying to read */ + if( (ret=usb_submit_urb(urb)) != 0 ) + dbg( "digi_read_bulk_callback: failed resubmitting read urb, ret=%d", + ret ); + +} + +#endif /* CONFIG_USB_SERIAL_DIGI_ACCELEPORT */ + diff --git a/drivers/usb/serial/ezusb_convert.pl b/drivers/usb/serial/ezusb_convert.pl index 3c69e4c1c..13f114691 100644 --- a/drivers/usb/serial/ezusb_convert.pl +++ b/drivers/usb/serial/ezusb_convert.pl @@ -16,8 +16,8 @@ while (<STDIN>) { # ':' <len> <addr> <type> <len-data> <crc> '\r' # len, type, crc are 2-char hex, addr is 4-char hex. type is 00 for # normal records, 01 for EOF - my($lenstring, $addrstring, $typestring, $reststring) = - /^:(\w\w)(\w\w\w\w)(\w\w)(\w+)$/; + my($lenstring, $addrstring, $typestring, $reststring, $doscrap) = + /^:(\w\w)(\w\w\w\w)(\w\w)(\w+)(\r?)$/; die "malformed line: $_" unless $reststring; last if $typestring eq '01'; my($len) = hex($lenstring); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index a305283bb..cdad3cabe 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1,7 +1,7 @@ /* * USB FTDI SIO driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * Bill Ryder (bryder@sgi.com) * diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index c63f1b856..f91e1b592 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -1,7 +1,7 @@ /* * USB Keyspan PDA Converter driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 2edcb6e86..9eb6767f0 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -8,6 +8,8 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * Please report both successes and troubles to the author at omninet@kroah.com + * */ #include <linux/config.h> diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index 8991f4be9..b8e2e1d74 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -1,7 +1,7 @@ /* * USB Serial Converter driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #include <linux/config.h> #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ -#define SERIAL_TTY_MINORS 16 /* Actually we are allowed 255, but this is good for now */ +#define SERIAL_TTY_MINORS 255 /* loads of devices :) */ #define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */ @@ -123,6 +123,7 @@ extern struct usb_serial_device_type ftdi_sio_device; extern struct usb_serial_device_type keyspan_pda_fake_device; extern struct usb_serial_device_type keyspan_pda_device; extern struct usb_serial_device_type zyxel_omninet_device; +extern struct usb_serial_device_type digi_acceleport_device; /* determine if we should include the EzUSB loader functions */ diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 588fed5d1..9fe65ae8d 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -1,7 +1,7 @@ /* * USB Serial Converter driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -14,6 +14,16 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/03/2000) gkh + * Added the Digi Acceleport driver from Al Borchers and Peter Berger. + * + * (05/02/2000) gkh + * Changed devfs and tty register code to work properly now. This was based on + * the ACM driver changes by Vojtech Pavlik. + * + * (04/27/2000) Ryan VanderBijl + * Put calls to *_paranoia_checks into one function. + * * (04/23/2000) gkh * Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports. * Moved when the startup code printed out the devices that are supported. @@ -282,15 +292,13 @@ static struct usb_serial_device_type *usb_serial_devices[] = { #ifdef CONFIG_USB_SERIAL_OMNINET &zyxel_omninet_device, #endif +#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT + &digi_acceleport_device, +#endif NULL }; -/* variables needed for the tty_driver structure */ -static char *driver_name = "usb"; -static char *tty_driver_name = "usb/tty/%d"; - - /* local function prototypes */ static int serial_open (struct tty_struct *tty, struct file * filp); static void serial_close (struct tty_struct *tty, struct file * filp); @@ -312,12 +320,27 @@ static struct usb_driver usb_serial_driver = { }; static int serial_refcount; +static struct tty_driver serial_tty_driver; static struct tty_struct * serial_tty[SERIAL_TTY_MINORS]; static struct termios * serial_termios[SERIAL_TTY_MINORS]; static struct termios * serial_termios_locked[SERIAL_TTY_MINORS]; static struct usb_serial *serial_table[SERIAL_TTY_MINORS] = {NULL, }; +static inline struct usb_serial* get_usb_serial (struct usb_serial_port *port, const char *function) +{ + /* if no port was specified, or it fails a paranoia check */ + if (!port || + port_paranoia_check (port, function) || + serial_paranoia_check (port->serial, function)) { + /* then say that we dont have a valid usb_serial thing, which will + * end up genrating -ENODEV return values */ + return NULL; + } + + return port->serial; +} + static struct usb_serial *get_serial_by_minor (int minor) { @@ -357,7 +380,7 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor) for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) serial_table[i] = serial; return serial; - } + } return NULL; } @@ -454,16 +477,9 @@ static int serial_open (struct tty_struct *tty, struct file * filp) static void serial_close(struct tty_struct *tty, struct file * filp) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; + struct usb_serial *serial = get_usb_serial (port, "serial_close"); - dbg("serial_close"); - - if (port_paranoia_check (port, "serial_close")) { - return; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "serial_close")) { + if (!serial) { return; } @@ -486,16 +502,9 @@ static void serial_close(struct tty_struct *tty, struct file * filp) static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_write"); + struct usb_serial *serial = get_usb_serial (port, "serial_write"); - if (port_paranoia_check (port, "serial_write")) { - return -ENODEV; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "serial_write")) { + if (!serial) { return -ENODEV; } @@ -518,26 +527,19 @@ static int serial_write (struct tty_struct * tty, int from_user, const unsigned static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_write_room"); - - if (port_paranoia_check (port, "serial_write")) { - return -ENODEV; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "serial_write")) { + struct usb_serial *serial = get_usb_serial (port, "serial_write_room"); + + if (!serial) { return -ENODEV; } - + dbg("serial_write_room port %d", port->number); if (!port->active) { dbg ("port not open"); return -EINVAL; } - + /* pass on to the driver specific version of this function if it is available */ if (serial->type->write_room) { return (serial->type->write_room(port)); @@ -550,24 +552,17 @@ static int serial_write_room (struct tty_struct *tty) static int serial_chars_in_buffer (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_chars_in_buffer"); - - if (port_paranoia_check (port, "serial_chars_in_buffer")) { - return -ENODEV; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "serial_chars_in_buffer")) { + struct usb_serial *serial = get_usb_serial (port, "serial_chars_in_buffer"); + + if (!serial) { return -ENODEV; } - + if (!port->active) { dbg ("port not open"); return -EINVAL; } - + /* pass on to the driver specific version of this function if it is available */ if (serial->type->chars_in_buffer) { return (serial->type->chars_in_buffer(port)); @@ -580,21 +575,14 @@ static int serial_chars_in_buffer (struct tty_struct *tty) static void serial_throttle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_throttle"); - - if (port_paranoia_check (port, "serial_throttle")) { - return; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "serial_throttle")) { + struct usb_serial *serial = get_usb_serial (port, "serial_throttle"); + + if (!serial) { return; } - + dbg("serial_throttle port %d", port->number); - + if (!port->active) { dbg ("port not open"); return; @@ -612,21 +600,14 @@ static void serial_throttle (struct tty_struct * tty) static void serial_unthrottle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_unthrottle"); - - if (port_paranoia_check (port, "serial_unthrottle")) { - return; - } - - serial = port->serial; - if (serial_paranoia_check (serial, "serial_unthrottle")) { + struct usb_serial *serial = get_usb_serial (port, "serial_unthrottle"); + + if (!serial) { return; } - + dbg("serial_unthrottle port %d", port->number); - + if (!port->active) { dbg ("port not open"); return; @@ -644,21 +625,14 @@ static void serial_unthrottle (struct tty_struct * tty) static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_ioctl"); - - if (port_paranoia_check (port, "serial_ioctl")) { - return -ENODEV; - } + struct usb_serial *serial = get_usb_serial (port, "serial_ioctl"); - serial = port->serial; - if (serial_paranoia_check (serial, "serial_ioctl")) { + if (!serial) { return -ENODEV; } - + dbg("serial_ioctl port %d", port->number); - + if (!port->active) { dbg ("port not open"); return -ENODEV; @@ -676,16 +650,9 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in static void serial_set_termios (struct tty_struct *tty, struct termios * old) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_set_termios"); - - if (port_paranoia_check (port, "serial_set_termios")) { - return; - } + struct usb_serial *serial = get_usb_serial (port, "serial_set_termios"); - serial = port->serial; - if (serial_paranoia_check (serial, "serial_set_termios")) { + if (!serial) { return; } @@ -708,16 +675,9 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old) static void serial_break (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial; - - dbg("serial_break"); - - if (port_paranoia_check (port, "serial_break")) { - return; - } + struct usb_serial *serial = get_usb_serial (port, "serial_break"); - serial = port->serial; - if (serial_paranoia_check (serial, "serial_break")) { + if (!serial) { return; } @@ -861,22 +821,15 @@ static int generic_chars_in_buffer (struct usb_serial_port *port) static void generic_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; + struct usb_serial *serial = get_usb_serial (port, "generic_read_bulk_callback"); + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; int i; - dbg("generic_read_bulk_callback"); - - if (port_paranoia_check (port, "generic_read_bulk_callback")) { + if (!serial) { return; } - serial = port->serial; - if (serial_paranoia_check (serial, "generic_read_bulk_callback")) { - return; - } - if (urb->status) { dbg("nonzero read bulk status received: %d", urb->status); return; @@ -911,20 +864,13 @@ static void generic_read_bulk_callback (struct urb *urb) static void generic_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial; - struct tty_struct *tty; - - dbg("generic_write_bulk_callback"); + struct usb_serial *serial = get_usb_serial (port, "generic_write_bulk_callback"); + struct tty_struct *tty; - if (port_paranoia_check (port, "generic_write_bulk_callback")) { + if (!serial) { return; } - serial = port->serial; - if (serial_paranoia_check (serial, "generic_write_bulk_callback")) { - return; - } - if (urb->status) { dbg("nonzero write bulk status received: %d", urb->status); return; @@ -940,48 +886,6 @@ static void generic_write_bulk_callback (struct urb *urb) } -static struct tty_driver * usb_serial_tty_driver_init (struct usb_serial *serial) -{ - struct tty_driver *serial_tty_driver; - - if (!(serial_tty_driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL))) { - err("Out of memory"); - return NULL; - } - - memset (serial_tty_driver, 0x00, sizeof(struct tty_driver)); - - /* initialize the entries that we don't want to be NULL */ - serial_tty_driver->magic = TTY_DRIVER_MAGIC; - serial_tty_driver->driver_name = driver_name; - serial_tty_driver->name = tty_driver_name; - serial_tty_driver->major = SERIAL_TTY_MAJOR; - serial_tty_driver->minor_start = serial->minor; - serial_tty_driver->num = serial->num_ports; - serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; - serial_tty_driver->flags = TTY_DRIVER_REAL_RAW; - serial_tty_driver->refcount = &serial_refcount; - serial_tty_driver->table = serial_tty; - serial_tty_driver->termios = serial_termios; - serial_tty_driver->termios_locked = serial_termios_locked; - serial_tty_driver->open = serial_open; - serial_tty_driver->close = serial_close; - serial_tty_driver->write = serial_write; - serial_tty_driver->write_room = serial_write_room; - serial_tty_driver->ioctl = serial_ioctl; - serial_tty_driver->set_termios = serial_set_termios; - serial_tty_driver->throttle = serial_throttle; - serial_tty_driver->unthrottle = serial_unthrottle; - serial_tty_driver->break_ctl = serial_break; - serial_tty_driver->chars_in_buffer = serial_chars_in_buffer; - serial_tty_driver->init_termios = tty_std_termios; - serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - - return serial_tty_driver; -} - - static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_serial *serial = NULL; @@ -1101,18 +1005,6 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) serial->num_bulk_out = num_bulk_out; serial->num_interrupt_in = num_interrupt_in; - /* initialize a tty_driver for this device */ - serial->tty_driver = usb_serial_tty_driver_init (serial); - if (serial->tty_driver == NULL) { - err("Can't create a tty_serial_driver"); - goto probe_error; - } - - if (tty_register_driver (serial->tty_driver)) { - err("failed to register tty driver"); - goto probe_error; - } - /* if this device type has a startup function, call it */ if (type->startup) { if (type->startup (serial)) { @@ -1196,14 +1088,16 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) max_endpoints = MAX(max_endpoints, num_interrupt_in); for (i = 0; i < max_endpoints; ++i) { port = &serial->port[i]; - port->number = i; + port->number = i + serial->minor; port->serial = serial; port->magic = USB_SERIAL_PORT_MAGIC; } + /* initialize the devfs nodes for this device and let the user know what ports we are bound to */ for (i = 0; i < serial->num_ports; ++i) { - info("%s converter now attached to ttyUSB%d", - type->name, serial->minor + i); + tty_register_devfs (&serial_tty_driver, 0, serial->port[i].number); + info("%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", + type->name, serial->port[i].number, serial->port[i].number); } return serial; /* success */ @@ -1235,13 +1129,6 @@ probe_error: /* return the minor range that this device had */ return_serial (serial); - /* if this device has a tty_driver, then unregister it and free it */ - if (serial->tty_driver) { - tty_unregister_driver (serial->tty_driver); - kfree (serial->tty_driver); - serial->tty_driver = NULL; - } - /* free up any memory that we allocated */ kfree (serial); MOD_DEC_USE_COUNT; @@ -1291,18 +1178,13 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr) } for (i = 0; i < serial->num_ports; ++i) { - info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->minor + i); + tty_unregister_devfs (&serial_tty_driver, serial->port[i].number); + info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->port[i].number); } /* return the minor range that this device had */ return_serial (serial); - /* if this device has a tty_driver, then unregister it and free it */ - if (serial->tty_driver) { - tty_unregister_driver (serial->tty_driver); - kfree (serial->tty_driver); - serial->tty_driver = NULL; - } /* free up any memory that we allocated */ kfree (serial); @@ -1314,6 +1196,35 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr) } +static struct tty_driver serial_tty_driver = { + magic: TTY_DRIVER_MAGIC, + driver_name: "usb-serial", + name: "usb/tts/%d", + major: SERIAL_TTY_MAJOR, + minor_start: 0, + num: SERIAL_TTY_MINORS, + type: TTY_DRIVER_TYPE_SERIAL, + subtype: SERIAL_TYPE_NORMAL, + flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + refcount: &serial_refcount, + table: serial_tty, + termios: serial_termios, + termios_locked: serial_termios_locked, + + open: serial_open, + close: serial_close, + write: serial_write, + write_room: serial_write_room, + ioctl: serial_ioctl, + set_termios: serial_set_termios, + throttle: serial_throttle, + unthrottle: serial_unthrottle, + break_ctl: serial_break, + chars_in_buffer: serial_chars_in_buffer, +}; + + int usb_serial_init(void) { int i; @@ -1336,9 +1247,18 @@ int usb_serial_init(void) if (!something) info ("USB Serial driver is not configured for any devices!"); + /* register the tty driver */ + serial_tty_driver.init_termios = tty_std_termios; + serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + if (tty_register_driver (&serial_tty_driver)) { + err("failed to register tty driver"); + return -1; + } + /* register the USB driver */ result = usb_register(&usb_serial_driver); if (result < 0) { + tty_unregister_driver(&serial_tty_driver); err("usb_register failed for the usb-serial driver. Error number %d", result); return -1; } @@ -1350,6 +1270,7 @@ int usb_serial_init(void) void usb_serial_exit(void) { usb_deregister(&usb_serial_driver); + tty_unregister_driver(&serial_tty_driver); } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 6832814da..979a56f01 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -1,7 +1,7 @@ /* * USB HandSpring Visor driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (04/27/2000) Ryan VanderBijl + * Fixed memory leak in visor_close + * * (03/26/2000) gkh * Split driver up into device specific pieces. * @@ -110,6 +113,7 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) /* send a shutdown message to the device */ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); + kfree (transfer_buffer); } /* shutdown our bulk reads and writes */ diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index a1173697d..6407a7811 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -1,7 +1,7 @@ /* * USB HandSpring Visor driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index aa6c14b0b..58f761fbf 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1,7 +1,7 @@ /* * USB ConnectTech WhiteHEAT driver * - * (C) Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -11,6 +11,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/04/2000) gkh + * First cut at open and close commands. Data can flow through the ports at + * default speeds now. + * * (03/26/2000) gkh * Split driver up into device specific pieces. * @@ -45,6 +49,7 @@ #include "whiteheat_fw.h" /* firmware for the ConnectTech WhiteHEAT device */ +#include "whiteheat.h" /* WhiteHEAT specific commands */ #define CONNECT_TECH_VENDOR_ID 0x0710 #define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 @@ -57,6 +62,7 @@ static void whiteheat_set_termios (struct usb_serial_port *port, struct termios static void whiteheat_throttle (struct usb_serial_port *port); static void whiteheat_unthrottle (struct usb_serial_port *port); static int whiteheat_startup (struct usb_serial *serial); +static void whiteheat_shutdown (struct usb_serial *serial); /* All of the device info needed for the Connect Tech WhiteHEAT */ static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID; @@ -91,14 +97,124 @@ struct usb_serial_device_type whiteheat_device = { throttle: whiteheat_throttle, unthrottle: whiteheat_unthrottle, set_termios: whiteheat_set_termios, + shutdown: whiteheat_shutdown, +}; + +struct whiteheat_private { + __u8 command_finished; + wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ }; +#define COMMAND_PORT 4 +#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ + /***************************************************************************** * Connect Tech's White Heat specific driver functions *****************************************************************************/ +static void command_port_write_callback (struct urb *urb) +{ + unsigned char *data = urb->transfer_buffer; +#ifdef DEBUG + int i; +#endif + + dbg ("command_port_write_callback"); + + if (urb->status) { + dbg ("nonzero urb status: %d", urb->status); + return; + } + +#ifdef DEBUG + if (urb->actual_length) { + printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); + } +#endif + + return; +} + + +static void command_port_read_callback (struct urb *urb) +{ + struct whiteheat_private *info = (struct whiteheat_private *)urb->context; + unsigned char *data = urb->transfer_buffer; +#ifdef DEBUG + int i; +#endif + + dbg ("command_port_write_callback"); + + if (urb->status) { + dbg ("nonzero urb status: %d", urb->status); + return; + } + +#ifdef DEBUG + if (urb->actual_length) { + printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); + } +#endif + + /* right now, if the command is COMMAND_COMPLETE, just flip the bit saying the command finished */ + /* in the future we're going to have to pay attention to the actual command that completed */ + if (data[0] == WHITEHEAT_CMD_COMPLETE) { + info->command_finished = TRUE; + } + + return; +} + + +static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *data, __u8 datasize) +{ + struct whiteheat_private *info; + struct usb_serial_port *port; + int timeout; + __u8 *transfer_buffer; + + dbg("whiteheat_send_cmd: %d", command); + + port = &serial->port[COMMAND_PORT]; + info = (struct whiteheat_private *)port->private; + info->command_finished = FALSE; + + transfer_buffer = (__u8 *)port->write_urb->transfer_buffer; + transfer_buffer[0] = command; + memcpy (&transfer_buffer[1], data, datasize); + port->write_urb->transfer_buffer_length = datasize + 1; + if (usb_submit_urb (port->write_urb)) + dbg ("submit urb failed"); + + /* wait for the command to complete */ + timeout = COMMAND_TIMEOUT; + while (timeout && (info->command_finished == FALSE)) { + timeout = interruptible_sleep_on_timeout (&info->wait_command, timeout); + } + + if (info->command_finished == FALSE) { + return -1; + } + + return 0; +} + + static int whiteheat_open (struct usb_serial_port *port, struct file *filp) { + struct whiteheat_min_set open_command; + struct usb_serial_port *command_port; + struct whiteheat_private *info; + dbg("whiteheat_open port %d", port->number); if (port->active) { @@ -106,22 +222,50 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) return -EINVAL; } port->active = 1; - - /*Start reading from the device*/ + + /* set up some stuff for our command port */ + command_port = &port->serial->port[COMMAND_PORT]; + if (command_port->private == NULL) { + info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); + if (info == NULL) { + err("out of memory"); + return -ENOMEM; + } + + init_waitqueue_head(&info->wait_command); + command_port->private = info; + command_port->write_urb->complete = command_port_write_callback; + command_port->read_urb->complete = command_port_read_callback; + usb_submit_urb (command_port->read_urb); + } + + /* Start reading from the device */ if (usb_submit_urb(port->read_urb)) dbg("usb_submit_urb(read bulk) failed"); + /* send an open port command */ + open_command.port = port->number - port->minor; + whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + /* Need to do device specific setup here (control lines, baud rate, etc.) */ /* FIXME!!! */ + dbg("whiteheat_open exit"); + return (0); } static void whiteheat_close(struct usb_serial_port *port, struct file * filp) { + struct whiteheat_min_set close_command; + dbg("whiteheat_close port %d", port->number); + /* send a close command to the port */ + close_command.port = port->number - port->minor; + whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); + /* Need to change the control lines here */ /* FIXME */ @@ -243,7 +387,24 @@ static int whiteheat_startup (struct usb_serial *serial) response = ezusb_set_reset (serial, 0); /* we want this device to fail to have a driver assigned to it. */ - return (1); + return 1; +} + + +static void whiteheat_shutdown (struct usb_serial *serial) +{ + struct usb_serial_port *command_port; + + dbg("whiteheat_shutdown"); + + /* set up some stuff for our command port */ + command_port = &serial->port[COMMAND_PORT]; + if (command_port->private != NULL) { + kfree (command_port->private); + command_port->private = NULL; + } + + return; } #endif /* CONFIG_USB_SERIAL_WHITEHEAT */ diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h new file mode 100644 index 000000000..970a555c0 --- /dev/null +++ b/drivers/usb/serial/whiteheat.h @@ -0,0 +1,169 @@ +/* + * USB ConnectTech WhiteHEAT driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + */ + +#ifndef __LINUX_USB_SERIAL_WHITEHEAT_H +#define __LINUX_USB_SERIAL_WHITEHEAT_H + + +#define FALSE 0 +#define TRUE 1 + +/* WhiteHEAT commands */ +#define WHITEHEAT_OPEN 1 /* open the port */ +#define WHITEHEAT_CLOSE 2 /* close the port */ +#define WHITEHEAT_SETUP_PORT 3 /* change port settings */ +#define WHITEHEAT_SET_RTS 4 /* turn RTS on or off */ +#define WHITEHEAT_SET_DTR 5 /* turn DTR on or off */ +#define WHITEHEAT_SET_BREAK 6 /* turn BREAK on or off */ +#define WHITEHEAT_DUMP 7 /* dump memory */ +#define WHITEHEAT_STATUS 8 /* get status */ +#define WHITEHEAT_PURGE 9 /* clear the UART fifos */ +#define WHITEHEAT_GET_DTR_RTS 10 /* get the state of DTR and RTS for a port */ +#define WHITEHEAT_GET_HW_INFO 11 /* get EEPROM info and hardware ID */ +#define WHITEHEAT_REPORT_TX_DONE 12 /* get the next TX done */ +#define WHITEHEAT_EVENT 13 /* unsolicited status events */ +#define WHITEHEAT_ECHO 14 /* send data to the indicated IN endpoint */ +#define WHITEHEAT_DO_TEST 15 /* perform the specified test */ +#define WHITEHEAT_CMD_COMPLETE 16 /* reply for certain commands */ + +/* Data for the WHITEHEAT_SETUP_PORT command */ +#define WHITEHEAT_CTS_FLOW 0x08 +#define WHITEHEAT_RTS_FLOW 0x80 +#define WHITEHEAT_DSR_FLOW 0x10 +#define WHITEHEAT_DTR_FLOW 0x02 +struct whiteheat_port_settings { + __u8 port; /* port number (1 to N) */ + __u32 baud; /* any value allowed, default 9600, arrives little endian, range is 7 - 460800 */ + __u8 bits; /* 5, 6, 7, or 8, default 8 */ + __u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */ + __u8 parity; /* 'n, e, o, 0, or 1' (ascii), default 'n' + * n = none e = even o = odd + * 0 = force 0 1 = force 1 */ + __u8 sflow; /* 'n, r, t, or b' (ascii), default 'n' + * n = none + * r = receive (XOFF/XON transmitted when receiver fills / empties) + * t = transmit (XOFF/XON received will stop/start TX) + * b = both */ + __u8 xoff; /* XOFF byte value, default 0x13 */ + __u8 xon; /* XON byte value, default 0x11 */ + __u8 hflow; /* bits indicate mode as follows: + * CTS (0x08) (CTS off/on will control/cause TX off/on) + * DSR (0x10) (DSR off/on will control/cause TX off/on) + * RTS (0x80) (RTS off/on when receiver fills/empties) + * DTR (0x02) (DTR off/on when receiver fills/empties) */ + __u8 lloop; /* local loopback 0 or 1, default 0 */ +}; + +/* data for WHITEHEAT_SET_RTS, WHITEHEAT_SET_DTR, and WHITEHEAT_SET_BREAK commands */ +struct whiteheat_rdb_set { + __u8 port; /* port number (1 to N) */ + __u8 state; /* 0 = off, non-zero = on */ +}; + +/* data for: + WHITEHEAT_OPEN + WHITEHEAT_CLOSE + WHITEHEAT_STATUS + WHITEHEAT_GET_DTR_RTS + WHITEHEAT_REPORT_TX_DONE */ +struct whiteheat_min_set { + __u8 port; /* port number (1 to N) */ +}; + +/* data for WHITEHEAT_PURGE command */ +#define WHITEHEAT_PURGE_INPUT 0x01 +#define WHITEHEAT_PURGE_OUTPUT 0x02 +struct whiteheat_purge_set { + __u8 port; /* port number (1 to N) */ + __u8 what; /* bit pattern of what to purge */ +}; + +/* data for WHITEHEAT_DUMP command */ +struct whiteheat_dump_info { + __u8 mem_type; /* memory type: 'd' = data, 'i' = idata, 'b' = bdata, 'x' = xdata */ + __u16 addr; /* memory address to dump, address range depends on the above mem_type: + * 'd' = 0 to ff (80 to FF is SFR's) + * 'i' = 80 to ff + * 'b' = 20 to 2f (bits returned as bytes) + * 'x' = 0000 to ffff (also code space) */ + __u16 length; /* number of bytes to dump, max 64 */ +}; + +/* data for WHITEHEAT_ECHO command */ +struct whiteheat_echo_set { + __u8 port; /* port number (1 to N) */ + __u8 length; /* length of message to echo */ + __u8 echo_data[61]; /* data to echo */ +}; + +/* data returned from WHITEHEAT_STATUS command */ +#define WHITEHEAT_OVERRUN_ERROR 0x02 +#define WHITEHEAT_PARITY_ERROR 0x04 +#define WHITEHEAT_FRAMING_ERROR 0x08 +#define WHITEHEAT_BREAK_ERROR 0x10 + +#define WHITEHEAT_OHFLOW 0x01 /* TX is stopped by CTS (waiting for CTS to go ON) */ +#define WHITEHEAT_IHFLOW 0x02 /* remote TX is stopped by RTS */ +#define WHITEHEAT_OSFLOW 0x04 /* TX is stopped by XOFF received (waiting for XON to occur) */ +#define WHITEHEAT_ISFLOW 0x08 /* remote TX is stopped by XOFF transmitted */ +#define WHITEHEAT_TX_DONE 0x80 /* TX has completed */ + +#define WHITEHEAT_MODEM_EVENT 0x01 +#define WHITEHEAT_ERROR_EVENT 0x02 +#define WHITEHEAT_FLOW_EVENT 0x04 +#define WHITEHEAT_CONNECT_EVENT 0x08 +struct whiteheat_status_info { + __u8 port; /* port number (1 to N) */ + __u8 event; /* indicates which of the following bytes are the current event */ + __u8 modem; /* modem signal status (copy of UART MSR register) */ + __u8 error; /* PFO and RX break (copy of UART LSR register) */ + __u8 flow; /* flow control state */ + __u8 connect; /* connect state, non-zero value indicates connected */ +}; + +/* data returned from WHITEHEAT_EVENT command */ +struct whiteheat_event { + __u8 port; /* port number (1 to N) */ + __u8 event; /* indicates which of the following bytes are the current event */ + __u8 info; /* either modem, error, flow, or connect information */ +}; + +/* data retured by the WHITEHEAT_GET_HW_INFO command */ +struct whiteheat_hw_info { + __u8 hw_id; /* hardware id number, WhiteHEAT = 0 */ + __u8 sw_major_rev; /* major version number */ + __u8 sw_minor_rev; /* minor version number */ + struct whiteheat_hw_eeprom_info { + __u8 b0; /* B0 */ + __u8 vendor_id_low; /* vendor id (low byte) */ + __u8 vendor_id_high; /* vendor id (high byte) */ + __u8 product_id_low; /* product id (low byte) */ + __u8 product_id_high; /* product id (high byte) */ + __u8 device_id_low; /* device id (low byte) */ + __u8 device_id_high; /* device id (high byte) */ + __u8 not_used_1; + __u8 serial_number_0; /* serial number (low byte) */ + __u8 serial_number_1; /* serial number */ + __u8 serial_number_2; /* serial number */ + __u8 serial_number_3; /* serial number (high byte) */ + __u8 not_used_2; + __u8 not_used_3; + __u8 checksum_low; /* checksum (low byte) */ + __u8 checksum_high; /* checksum (high byte */ + } hw_eeprom_info; /* EEPROM contents */ +}; + +#endif + |