Re: FTDI USB-to-UART converters and tcdrain()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2012-10-04 at 15:25 -0700, Greg KH wrote:
> > - Is tcdrain() supposed to ensure that all TX data has been sent for any
> > serial device? If so, why is it not implemented for this device?
> 
> No one has implemented it to do so.  Other usb-serial drivers have
> implemented this functionality, just not this one.  Perhaps because no
> one has noticed before, or maybe the chip really can't do it.
> 
> Without the specs for the device itself, I can't really tell for sure.
> If you do get ahold of the specs, let me know, and I'll be glad to work
> to add this support to the driver, as I agree, it is something that
> would be useful to a lot of people.
> 
> Sorry I can't be of more help,

Hello Greg,

You helped by confirming that it is indeed missing. I have figured out a
solution that at least works for my FT232RL chip.

The solution: Add a chars_in_buffer() function that checks both the
software buffer and the hardware buffer. This function is called when
tcdrain() or close() is called in an application.

The amount of data in the hardware buffer cannot be retrieved, but (as
suggested by ftdi_tiocmget()) all devices, except the original SIO, can
indicate whether the hardware buffer is empty. All newer devices return
2 bytes when reading register GET_MODEM_STATUS, the second being the
line status, which contains bit FTDI_RS_TEMT (TX empty, already defined
in the header file). If the hardware buffer is not empty, let the
function at least return 1.

I have attached the code for the function.
- The symbol for function usb_serial_generic_chars_in_buffer() is not
exported, so I had to copy the code.
- I'm not sure whether the kmalloc is really needed and whether no other
locking is necessary.
- As mentioned, this works for my FT232RL. The function is called many
times when doing tcdrain() or close(). At first it returns something
like 3000 (characters in the software buffer). In the end it returns 1
for a while (software buffer empty, hardware buffer not yet empty).

I have e-mailed FTDI's support to ask whether the assumptions described
above are true and whether there is no register to read the actual
amount of data in the hardware buffer(s). Their website states you need
to sign an NDA to get the register descriptions, but with some luck that
isn't needed.

Kind regards,
Jarkko
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:
		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;
}

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux