patch-2.4.5 linux/drivers/usb/serial/ftdi_sio.c
Next file: linux/drivers/usb/serial/ftdi_sio.h
Previous file: linux/drivers/usb/serial/empeg.c
Back to the patch index
Back to the overall index
- Lines: 330
- Date:
Thu May 24 14:55:51 2001
- Orig file:
v2.4.4/linux/drivers/usb/serial/ftdi_sio.c
- Orig date:
Wed Apr 18 11:49:12 2001
diff -u --recursive --new-file v2.4.4/linux/drivers/usb/serial/ftdi_sio.c linux/drivers/usb/serial/ftdi_sio.c
@@ -14,7 +14,22 @@
*
* See http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date testing info
* and extra documentation
+ *
+ * (23/May/2001) Bill Ryder
+ * Added runtime debug patch (thanx Tyson D Sawyer).
+ * Cleaned up comments for 8U232
+ * Added parity, framing and overrun error handling
+ * Added receive break handling.
+ *
+ * (04/08/2001) gb
+ * Identify version on module load.
*
+ * (18/March/2001) Bill Ryder
+ * (Not released)
+ * Added send break handling. (requires kernel patch too)
+ * Fixed 8U232AM hardware RTS/CTS etc status reporting.
+ * Added flipbuf fix copied from generic device
+ *
* (12/3/2000) Bill Ryder
* Added support for 8U232AM device.
* Moved PID and VIDs into header file only.
@@ -90,14 +105,24 @@
#include "ftdi_sio.h"
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.1.0"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>"
+#define DRIVER_DESC "USB FTDI RS232 Converters Driver"
+
static __devinitdata struct usb_device_id id_table_sio [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ } /* Terminating entry */
};
-/* THe 8U232AM has the same API as the sio - but it can support MUCH
- higher baudrates (921600 at 48MHz/230400 at 12MHz
- so .. it's baudrate setting codes are different */
+/* THe 8U232AM has the same API as the sio except for:
+ - it can support MUCH higher baudrates (921600 at 48MHz/230400
+ at 12MHz so .. it's baudrate setting codes are different
+ - it has a two byte status code.
+ - it returns characters very 16ms (the FTDI does it every 40ms)
+ */
static __devinitdata struct usb_device_id id_table_8U232AM [] = {
@@ -117,9 +142,7 @@
struct ftdi_private {
ftdi_type_t ftdi_type;
- char last_status_byte; /* device sends this every 40ms when open */
-
-
+ __u16 last_set_data_urb_value ; /* the last data state set - needed for doing a break */
};
/* function prototypes for a FTDI serial converter */
static int ftdi_sio_startup (struct usb_serial *serial);
@@ -132,6 +155,7 @@
static void ftdi_sio_read_bulk_callback (struct urb *urb);
static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old);
static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+static void ftdi_sio_break_ctl (struct usb_serial_port *port, int break_state );
/* Should rename most ftdi_sio's to ftdi_ now since there are two devices
which share common code */
@@ -153,6 +177,7 @@
write_bulk_callback: ftdi_sio_write_bulk_callback,
ioctl: ftdi_sio_ioctl,
set_termios: ftdi_sio_set_termios,
+ break_ctl: ftdi_sio_break_ctl,
startup: ftdi_sio_startup,
shutdown: ftdi_sio_shutdown,
};
@@ -174,6 +199,7 @@
write_bulk_callback: ftdi_sio_write_bulk_callback,
ioctl: ftdi_sio_ioctl,
set_termios: ftdi_sio_set_termios,
+ break_ctl: ftdi_sio_break_ctl,
startup: ftdi_8U232AM_startup,
shutdown: ftdi_sio_shutdown,
};
@@ -227,7 +253,7 @@
priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv){
- err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct ftdi_private));
+ err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private));
return -ENOMEM;
}
@@ -245,7 +271,7 @@
priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv){
- err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct ftdi_private));
+ err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private));
return -ENOMEM;
}
@@ -291,7 +317,8 @@
spin_unlock_irqrestore (&port->port_lock, flags);
- /* do not allow a task to be queued to deliver received data */
+ /* This will push the characters through immediately rather
+ than queue a task to deliver them */
port->tty->low_latency = 1;
/* No error checking for this (will get errors later anyway) */
@@ -422,7 +449,7 @@
unsigned char *first_byte = port->write_urb->transfer_buffer;
/* Was seeing a race here, got a read callback, then write callback before
- hitting interruptible_sleep_on - so wrapping in a wait_queue */
+ hitting interuptible_sleep_on - so wrapping in a wait_queue */
add_wait_queue(&port->write_wait, &wait);
set_current_state (TASK_INTERRUPTIBLE);
@@ -520,9 +547,9 @@
static void ftdi_sio_read_bulk_callback (struct urb *urb)
{ /* ftdi_sio_serial_buld_callback */
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct ftdi_private *priv = (struct ftdi_private *)port->private;
struct usb_serial *serial;
struct tty_struct *tty = port->tty ;
+ char error_flag;
unsigned char *data = urb->transfer_buffer;
const int data_offset = 2;
@@ -549,23 +576,76 @@
if (urb->actual_length > 2) {
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
} else {
- dbg("Just status");
+ dbg("Just status 0o%03o0o%03o",data[0],data[1]);
}
- priv->last_status_byte = data[0]; /* this has modem control lines */
/* TO DO -- check for hung up line and handle appropriately: */
/* send hangup */
/* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */
/* if CD is dropped and the line is not CLOCAL then we should hangup */
-
+ /* Handle errors and break */
+ error_flag = TTY_NORMAL;
+ /* Although the device uses a bitmask and hence can have multiple */
+ /* errors on a packet - the order here sets the priority the */
+ /* error is returned to the tty layer */
+
+ if ( data[1] & FTDI_RS_OE ) {
+ error_flag = TTY_OVERRUN;
+ dbg("OVERRRUN error");
+ }
+ if ( data[1] & FTDI_RS_BI ) {
+ error_flag = TTY_BREAK;
+ dbg("BREAK received");
+ }
+ if ( data[1] & FTDI_RS_PE ) {
+ error_flag = TTY_PARITY;
+ dbg("PARITY error");
+ }
+ if ( data[1] & FTDI_RS_FE ) {
+ error_flag = TTY_FRAME;
+ dbg("FRAMING error");
+ }
if (urb->actual_length > data_offset) {
+
for (i = data_offset ; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
+ /* have to make sure we don't overflow the buffer
+ with tty_insert_flip_char's */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* Note that the error flag is duplicated for
+ every character received since we don't know
+ which character it applied to */
+ tty_insert_flip_char(tty, data[i], error_flag);
+ }
tty_flip_buffer_push(tty);
+
+
+ }
+
+#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW
+ /* if a parity error is detected you get status packets forever
+ until a character is sent without a parity error.
+ This doesn't work well since the application receives a never
+ ending stream of bad data - even though new data hasn't been sent.
+ Therefore I (bill) have taken this out.
+ However - this might make sense for framing errors and so on
+ so I am leaving the code in for now.
+ */
+ else {
+ if (error_flag != TTY_NORMAL){
+ dbg("error_flag is not normal");
+ /* In this case it is just status - if that is an error send a bad character */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ tty_insert_flip_char(tty, 0xff, error_flag);
+ tty_flip_buffer_push(tty);
+ }
}
+#endif
/* Continue trying to always read */
FILL_BULK_URB(urb, serial->dev,
@@ -627,6 +707,38 @@
return(urb_value);
}
+static void ftdi_sio_break_ctl( struct usb_serial_port *port, int break_state )
+{
+ struct usb_serial *serial = port->serial;
+ struct ftdi_private *priv = (struct ftdi_private *)port->private;
+ __u16 urb_value = 0;
+ char buf[1];
+
+ /* break_state = -1 to turn on break, and 0 to turn off break */
+ /* see drivers/char/tty_io.c to see it used */
+ /* last_set_data_urb_value NEVER has the break bit set in it */
+
+ if (break_state) {
+ urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
+ } else {
+ urb_value = priv->last_set_data_urb_value;
+ }
+
+
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_DATA_REQUEST,
+ FTDI_SIO_SET_DATA_REQUEST_TYPE,
+ urb_value , 0,
+ buf, 0, WDR_TIMEOUT) < 0) {
+ err(__FUNCTION__ " FAILED to enable/disable break state (state was %d)",break_state);
+ }
+
+ dbg(__FUNCTION__ " break state is %d - urb is %d",break_state, urb_value);
+
+}
+
+
+
/* As I understand this - old_termios contains the original termios settings */
/* and tty->termios contains the new setting to be used */
/* */
@@ -670,6 +782,11 @@
err("CSIZE was set but not CS5-CS8");
}
}
+
+ /* This is needed by the break command since it uses the same command - but is
+ * or'ed with this value */
+ priv->last_set_data_urb_value = urb_value;
+
if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
@@ -743,7 +860,7 @@
struct usb_serial *serial = port->serial;
struct ftdi_private *priv = (struct ftdi_private *)port->private;
__u16 urb_value=0; /* Will hold the new flags */
- char buf[1];
+ char buf[2];
int ret, mask;
dbg(__FUNCTION__ " cmd 0x%04x", cmd);
@@ -753,12 +870,7 @@
case TIOCMGET:
dbg(__FUNCTION__ " TIOCMGET");
- /* The MODEM_STATUS_REQUEST works for the sio but not the 232 */
if (priv->ftdi_type == sio){
- /* TO DECIDE - use the 40ms status packets or not? */
- /* PRO: No need to send urb */
- /* CON: Could be 40ms out of date */
-
/* Request the status from the device */
if ((ret = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
@@ -771,8 +883,18 @@
return(ret);
}
} else {
- /* This gets updated every 40ms - so just copy it in */
- buf[0] = priv->last_status_byte;
+ /* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
+ format as the data returned from the in point */
+ if ((ret = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ FTDI_SIO_GET_MODEM_STATUS_REQUEST,
+ FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+ 0, 0,
+ buf, 2, WDR_TIMEOUT)) < 0 ) {
+ err(__FUNCTION__ " Could not get modem status of device - err: %d",
+ ret);
+ return(ret);
+ }
}
return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
@@ -863,6 +985,8 @@
dbg(__FUNCTION__);
usb_serial_register (&ftdi_sio_device);
usb_serial_register (&ftdi_8U232AM_device);
+ info(DRIVER_VERSION " " DRIVER_AUTHOR);
+ info(DRIVER_DESC);
return 0;
}
@@ -878,8 +1002,8 @@
module_init(ftdi_sio_init);
module_exit(ftdi_sio_exit);
-MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>");
-MODULE_DESCRIPTION("USB FTDI RS232 converters driver");
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)