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

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)