summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Cox <alan@linux.intel.com>2009-06-11 16:55:34 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-11 19:51:05 +0400
commit557aaa7ffab639d0190b935a041b16ae44606342 (patch)
tree1b555b5ea37078177896ccff0648cd152a98698d
parent4cc27bd6d7d6750dba33b4ccb4585c00b8fca7d2 (diff)
downloadlinux-557aaa7ffab639d0190b935a041b16ae44606342.tar.xz
ft232: support the ASYNC_LOW_LATENCY flag
This allows users to use the standard setserial command with this FT232 feature as well as obscure chip specific interfaces we have now. We keep track of and respect the sysfs value for non-low-latency cases. In theory we could do smart stuff with VTIME and the like but this seems of questionable worth. Closes-bug: http://bugzilla.kernel.org/show_bug.cgi?id=9120 Signed-off-by: Alan Cox <alan@linux.intel.com) Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/usb/serial/ftdi_sio.c94
1 files changed, 60 insertions, 34 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index d9d87111f9a9..21c053c31b9e 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -89,6 +89,7 @@ struct ftdi_private {
int force_rtscts; /* if non-zero, force RTS-CTS to always
be enabled */
+ unsigned int latency; /* latency setting in use */
spinlock_t tx_lock; /* spinlock for transmit state */
unsigned long tx_bytes;
unsigned long tx_outstanding_bytes;
@@ -1038,7 +1039,54 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
return rv;
}
+static int write_latency_timer(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev = port->serial->dev;
+ char buf[1];
+ int rv = 0;
+ int l = priv->latency;
+
+ if (priv->flags & ASYNC_LOW_LATENCY)
+ l = 1;
+
+ dbg("%s: setting latency timer = %i", __func__, l);
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+ l, priv->interface,
+ buf, 0, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
+ return rv;
+}
+
+static int read_latency_timer(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev = port->serial->dev;
+ unsigned short latency = 0;
+ int rv = 0;
+
+
+ dbg("%s", __func__);
+
+ 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(&port->dev, "Unable to read latency timer: %i\n", rv);
+ return -EIO;
+ }
+ return latency;
+}
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
@@ -1098,6 +1146,7 @@ static int set_serial_info(struct tty_struct *tty,
priv->custom_divisor = new_serial.custom_divisor;
tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ write_latency_timer(port);
check_and_exit:
if ((old_priv.flags & ASYNC_SPD_MASK) !=
@@ -1193,27 +1242,13 @@ static ssize_t show_latency_timer(struct device *dev,
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev = port->serial->dev;
- unsigned short latency = 0;
- int rv = 0;
-
-
- dbg("%s", __func__);
-
- 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\n", rv);
- return -EIO;
- }
- return sprintf(buf, "%i\n", latency);
+ if (priv->flags & ASYNC_LOW_LATENCY)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "%i\n", priv->latency);
}
+
/* Write a new value of the latency timer, in units of milliseconds. */
static ssize_t store_latency_timer(struct device *dev,
struct device_attribute *attr, const char *valbuf,
@@ -1221,25 +1256,13 @@ static ssize_t store_latency_timer(struct device *dev,
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev = port->serial->dev;
- char buf[1];
int v = simple_strtoul(valbuf, NULL, 10);
int rv = 0;
- dbg("%s: setting latency timer = %i", __func__, 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\n", rv);
+ priv->latency = v;
+ rv = write_latency_timer(port);
+ if (rv < 0)
return -EIO;
- }
-
return count;
}
@@ -1393,6 +1416,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
usb_set_serial_port_data(port, priv);
ftdi_determine_type(port);
+ read_latency_timer(port);
create_sysfs_attrs(port);
return 0;
}
@@ -1515,6 +1539,8 @@ static int ftdi_open(struct tty_struct *tty,
if (tty)
tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ write_latency_timer(port);
+
/* No error checking for this (will get errors later anyway) */
/* See ftdi_sio.h for description of what is reset */
usb_control_msg(dev, usb_sndctrlpipe(dev, 0),