Description:

Setting the event character and latency timer can greatly improve performance for some applications which use the ftdi_sio module (a serial->USB converter). The following patch adds sysfs attributes, exposing these configuration registers.

This is a patch against 2.6.10-rc2, but it also applies cleanly to 2.6.9. It works very nicely on 2.6.9, however I can't get my machine to boot 2.6.10-rc2, so I have been unable to test it on 2.6.10-rc2.

Signed-off-by: Edwin Olson ([EMAIL PROTECTED])

diff -uprbB -X dontdiff linux-2.6.10-rc2-orig/drivers/usb/serial/ftdi_sio.c linux-2.6.10-rc2-ftdi/drivers/usb/serial/ftdi_sio.c
--- linux-2.6.10-rc2-orig/drivers/usb/serial/ftdi_sio.c	2004-11-30 11:42:33.000000000 -0500
+++ linux-2.6.10-rc2-ftdi/drivers/usb/serial/ftdi_sio.c	2004-11-30 20:41:48.091730197 -0500
@@ -1173,6 +1171,135 @@ check_and_exit:
 
 } /* set_serial_info */
 
+
+/*
+ * ***************************************************************************
+ * Sysfs Attribute
+ * ***************************************************************************
+ */
+
+ssize_t show_latency_timer(struct device *dev, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *udev;
+	unsigned short latency = 0;
+	int rv = 0;
+	
+	udev = to_usb_device(dev);
+	
+	dbg("%s",__FUNCTION__);
+	
+	rv = usb_control_msg(udev,
+			     usb_rcvctrlpipe(udev, 0),
+			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
+			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
+			     0, priv->interface, 
+			     (char*) &latency, 1, WDR_TIMEOUT);
+	
+	if (rv < 0) {
+		dev_err(dev, "Unable to read latency timer: %i", rv);
+		return -EIO;
+	}
+	return sprintf(buf, "%i\n", latency);
+}
+
+/* Write a new value of the latency timer, in units of milliseconds. */
+ssize_t store_latency_timer(struct device *dev, const char *valbuf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *udev;
+	char buf[1];
+	int v = simple_strtoul(valbuf, NULL, 10);
+	int rv = 0;
+	
+	udev = to_usb_device(dev);
+	
+	dbg("%s: setting latency timer = %i", __FUNCTION__, v);
+	
+	rv = usb_control_msg(udev,
+			     usb_sndctrlpipe(udev, 0),
+			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+			     v, priv->interface, 
+			     buf, 0, WDR_TIMEOUT);
+	
+	if (rv < 0) {
+		dev_err(dev, "Unable to write latency timer: %i", rv);
+		return -EIO;
+	}
+	
+	return count;
+}
+
+/* Write an event character directly to the FTDI register.  The ASCII
+   value is in the low 8 bits, with the enable bit in the 9th bit. */
+ssize_t store_event_char(struct device *dev, const char *valbuf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct ftdi_private *priv = usb_get_serial_port_data(port);
+	struct usb_device *udev;
+	char buf[1];
+	int v = simple_strtoul(valbuf, NULL, 10);
+	int rv = 0;
+	
+	udev = to_usb_device(dev);
+	
+	dbg("%s: setting event char = %i", __FUNCTION__, v);
+	
+	rv = usb_control_msg(udev,
+			     usb_sndctrlpipe(udev, 0),
+			     FTDI_SIO_SET_EVENT_CHAR_REQUEST,
+			     FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
+			     v, priv->interface, 
+			     buf, 0, WDR_TIMEOUT);
+	
+	if (rv < 0) {
+		dbg("Unable to write event character: %i", rv);
+		return -EIO;
+	}
+	
+	return count;
+}
+
+static DEVICE_ATTR(latency_timer, S_IWUGO | S_IRUGO, show_latency_timer, store_latency_timer);
+static DEVICE_ATTR(event_char, S_IWUGO, NULL, store_event_char);
+
+void create_sysfs_attrs(struct usb_serial *serial)
+{	
+	struct ftdi_private *priv;
+	struct usb_device *udev;
+
+	dbg("%s",__FUNCTION__);
+	
+	priv = usb_get_serial_port_data(serial->port[0]);
+	udev = serial->dev;
+	
+	if (priv->chip_type == FT232BM) {
+		dbg("sysfs attributes for FT232BM");
+		device_create_file(&udev->dev, &dev_attr_event_char);
+		device_create_file(&udev->dev, &dev_attr_latency_timer);
+	}
+}
+
+void remove_sysfs_attrs(struct usb_serial *serial)
+{
+	struct ftdi_private *priv;
+	struct usb_device *udev;
+
+	dbg("%s",__FUNCTION__);	
+
+	priv = usb_get_serial_port_data(serial->port[0]);
+	udev = serial->dev;
+	
+	if (priv->chip_type == FT232BM) {
+		device_remove_file(&udev->dev, &dev_attr_event_char);
+		device_remove_file(&udev->dev, &dev_attr_latency_timer);
+	}
+	
+}
+
 /*
  * ***************************************************************************
  * FTDI driver specific functions
@@ -1290,6 +1418,8 @@ static int ftdi_FT232BM_startup (struct 
 	priv->chip_type = FT232BM;
 	priv->baud_base = 48000000 / 2; /* Would be / 16, but FT232BM supports multiple of 0.125 divisor fractions! */
 	
+	create_sysfs_attrs(serial);
+
 	return (0);
 } /* ftdi_FT232BM_startup */
 
@@ -1383,6 +1513,8 @@ static void ftdi_shutdown (struct usb_se
 
 	dbg("%s", __FUNCTION__);
 
+	remove_sysfs_attrs(serial);
+	
 	/* all open ports are closed at this point 
          *    (by usbserial.c:__serial_close, which calls ftdi_close)  
 	 */
diff -uprbB -X dontdiff linux-2.6.10-rc2-orig/drivers/usb/serial/ftdi_sio.h linux-2.6.10-rc2-ftdi/drivers/usb/serial/ftdi_sio.h
--- linux-2.6.10-rc2-orig/drivers/usb/serial/ftdi_sio.h	2004-11-30 11:42:33.000000000 -0500
+++ linux-2.6.10-rc2-ftdi/drivers/usb/serial/ftdi_sio.h	2004-11-30 20:44:05.651382020 -0500
@@ -249,6 +249,8 @@
 #define FTDI_SIO_GET_MODEM_STATUS	5 /* Retrieve current value of modern status register */
 #define FTDI_SIO_SET_EVENT_CHAR	6 /* Set the event character */
 #define FTDI_SIO_SET_ERROR_CHAR	7 /* Set the error character */
+#define FTDI_SIO_SET_LATENCY_TIMER	9 /* Set the latency timer */
+#define FTDI_SIO_GET_LATENCY_TIMER	10 /* Get the latency timer */
 
 /* Port interface code for FT2232C */
 #define INTERFACE_A		1
@@ -503,6 +505,55 @@ typedef enum {
  */  
  
 /*
+ * FTDI_SIO_GET_LATENCY_TIMER
+ *
+ * Set the timeout interval. The FTDI collects data from the slave
+ * device, transmitting it to the host when either A) 62 bytes are
+ * received, or B) the timeout interval has elapsed and the buffer
+ * contains at least 1 byte.  Setting this value to a small number
+ * can dramatically improve performance for applications which send
+ * small packets, since the default value is 16ms.
+ */
+#define  FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER
+#define  FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0
+
+/* 
+ *  BmRequestType:   1100 0000b
+ *  bRequest:        FTDI_SIO_GET_LATENCY_TIMER
+ *  wValue:          0
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            latency (on return)
+ */
+
+/* 
+ * FTDI_SIO_SET_LATENCY_TIMER
+ *
+ * Set the timeout interval. The FTDI collects data from the slave
+ * device, transmitting it to the host when either A) 62 bytes are
+ * received, or B) the timeout interval has elapsed and the buffer
+ * contains at least 1 byte.  Setting this value to a small number
+ * can dramatically improve performance for applications which send
+ * small packets, since the default value is 16ms.
+ */
+#define  FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
+#define  FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
+
+/* 
+ *  BmRequestType:   0100 0000b
+ *  bRequest:        FTDI_SIO_SET_LATENCY_TIMER
+ *  wValue:          Latency (milliseconds)
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            None
+ *
+ * wValue:
+ *   B0..7   Latency timer
+ *   B8..15  0
+ *
+ */
+
+/*
  * FTDI_SIO_SET_EVENT_CHAR 
  *
  * Set the special event character for the specified communications port.

Reply via email to