aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
diff options
context:
space:
mode:
authorJarkko Huijts <jarkko.huijts@gmail.com>2012-10-10 09:05:06 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-10-25 12:42:26 -0400
commit6f602912c9d0c84c2edbd446dd9f72660b701605 (patch)
tree55f34e6658cab9f17f845ec9255a76e25f42ee9f /drivers/usb/serial
parentbd066eef1aea5dd1f8e98934c4c6d21c5e0439c8 (diff)
usb: serial: ftdi_sio: Add missing chars_in_buffer function
The driver does not wait until the hardware buffer (for data from the PC to the UART line) is drained when tcdrain or close is called in an application. Solution: Implement a chars_in_buffer function that checks both the software and hardware buffer. If the TEMT (TX empty) bit of the line status register indicates the hw buffer is not empty, let the function return at least 1. This has been verified to work correctly with an FT232RL. The check on the hw buffer can not be done for the original SIO device. Signed-off-by: Jarkko Huijts <jarkko.huijts@gmail.com> Acked-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/ftdi_sio.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index be845873e23d..381515572235 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -923,6 +923,7 @@ static int ftdi_get_icount(struct tty_struct *tty,
923static int ftdi_ioctl(struct tty_struct *tty, 923static int ftdi_ioctl(struct tty_struct *tty,
924 unsigned int cmd, unsigned long arg); 924 unsigned int cmd, unsigned long arg);
925static void ftdi_break_ctl(struct tty_struct *tty, int break_state); 925static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
926static int ftdi_chars_in_buffer(struct tty_struct *tty);
926 927
927static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base); 928static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
928static unsigned short int ftdi_232am_baud_to_divisor(int baud); 929static unsigned short int ftdi_232am_baud_to_divisor(int baud);
@@ -957,6 +958,7 @@ static struct usb_serial_driver ftdi_sio_device = {
957 .ioctl = ftdi_ioctl, 958 .ioctl = ftdi_ioctl,
958 .set_termios = ftdi_set_termios, 959 .set_termios = ftdi_set_termios,
959 .break_ctl = ftdi_break_ctl, 960 .break_ctl = ftdi_break_ctl,
961 .chars_in_buffer = ftdi_chars_in_buffer,
960}; 962};
961 963
962static struct usb_serial_driver * const serial_drivers[] = { 964static struct usb_serial_driver * const serial_drivers[] = {
@@ -2089,6 +2091,64 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
2089 2091
2090} 2092}
2091 2093
2094static int ftdi_chars_in_buffer(struct tty_struct *tty)
2095{
2096 struct usb_serial_port *port = tty->driver_data;
2097 struct ftdi_private *priv = usb_get_serial_port_data(port);
2098 unsigned long flags;
2099 int chars;
2100 unsigned char *buf;
2101 int ret;
2102
2103 /* Check software buffer (code from
2104 * usb_serial_generic_chars_in_buffer()) */
2105 spin_lock_irqsave(&port->lock, flags);
2106 chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
2107 spin_unlock_irqrestore(&port->lock, flags);
2108
2109 /* Check hardware buffer */
2110 switch (priv->chip_type) {
2111 case FT8U232AM:
2112 case FT232BM:
2113 case FT2232C:
2114 case FT232RL:
2115 case FT2232H:
2116 case FT4232H:
2117 case FT232H:
2118 case FTX:
2119 break;
2120 case SIO:
2121 default:
2122 return chars;
2123 }
2124
2125 buf = kmalloc(2, GFP_KERNEL);
2126 if (!buf) {
2127 dev_err(&port->dev, "kmalloc failed");
2128 return chars;
2129 }
2130
2131 ret = usb_control_msg(port->serial->dev,
2132 usb_rcvctrlpipe(port->serial->dev, 0),
2133 FTDI_SIO_GET_MODEM_STATUS_REQUEST,
2134 FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
2135 0, priv->interface,
2136 buf, 2, WDR_TIMEOUT);
2137
2138 if (ret < 2) {
2139 dev_err(&port->dev, "Unable to read modem and line status: "
2140 "%i\n", ret);
2141 goto chars_in_buffer_out;
2142 }
2143
2144 if (!(buf[1] & FTDI_RS_TEMT))
2145 chars++;
2146
2147chars_in_buffer_out:
2148 kfree(buf);
2149 return chars;
2150}
2151
2092/* old_termios contains the original termios settings and tty->termios contains 2152/* old_termios contains the original termios settings and tty->termios contains
2093 * the new setting to be used 2153 * the new setting to be used
2094 * WARNING: set_termios calls this with old_termios in kernel space 2154 * WARNING: set_termios calls this with old_termios in kernel space