+static int ftdi_chars_in_buffer(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ int chars;
+ unsigned char *buf;
+ int ret;
+
+ /* Check software buffer (code from
+ * usb_serial_generic_chars_in_buffer()) */
+ spin_lock_irqsave(&port->lock, flags);
+ chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Check hardware buffer */
+ switch (priv->chip_type) {
+ case FT8U232AM:
+ case FT232BM:
+ case FT2232C:
+ case FT232RL:
+ case FT2232H:
+ case FT4232H:
+ case FT232H:
+ case FTX:
+ break;
+ case SIO:
+ default:
+ return chars;
+ }
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&port->dev, "kmalloc failed");
+ return chars;
+ }
+
+ ret = usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
+ FTDI_SIO_GET_MODEM_STATUS_REQUEST,
+ FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+ 0, priv->interface,
+ buf, 2, WDR_TIMEOUT);
+
+ if (ret < 2) {
+ dev_err(&port->dev, "Unable to read modem and line status: "
+ "%i\n", ret);
+ goto chars_in_buffer_out;
+ }
+
+ if (!(buf[1] & FTDI_RS_TEMT))
+ chars++;
+
+chars_in_buffer_out:
+ kfree(buf);
+ return chars;
+}
+