Re: ftdi_sio: Problem when changing the baud rate after a transfer

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

 



Please wrap your lines at 72 column or so. I've tried to reflow you mail
below.

On Fri, Jan 28, 2022 at 10:09:26AM +0000, embedded (VIVAVIS AG) wrote:
> > Gesendet: Montag, 10. Januar 2022 14:27
> > An: linux-usb@xxxxxxxxxxxxxxx
> > Betreff: ftdi_sio: Problem when changing the baud rate after a transfer
> >
> > Hi,
> > there seems to be a problem with the ftdi_sio driver in conjunction
> > with an FT2232C and changing the baud rate.
> > This behavior is observable at least on linux 4.19.190.

You need to reproduce any issues you have with a more recent kernel such
as 5.16.

> > The following was done in order to observe this problem:
> > A transfer is started over one of the serial interfaces of the
> > FT2232C at a lower baud rate, eg. 300 baud.
> > The code waits for the driver to empty the tx buffer by calling
> > tcdrain(). After the call returns the code changes
> > the baud rate of the serial interface to a higher rate, eg. 4800
> > baud, and writes another stream of bytes.
> > Now the problem occurs: Looking at the TX pin of the used interface
> > with an oscilloscope, one can see that the last byte of the previous
> > transfer, which is supposed to be transferred at 300 baud, is
> > transferred at the higher rate of 4800 baud. Even worse, it is not
> > even the complete byte, but rather some of the last bits of that
> > last byte which are transferred at the new baud rate configured.
> > This problem occurs independent of whether the interface is opened
> > in blocking or non-blocking mode.
> > I verified that the driver does in fact ask the hardware if it's tx
> > buffer is empty when the hardware status is reported.

How exactly did you verify that?

> > However, it seems that the reported status by the FT2232C does not
> > check the status of it's shift register (if that is even possible at
> > all), which is clearly influenced by the changed baud rate.

If it really is a hardware issue, then it's not much we can do, but see
below first.

> > Can someone confirm this behavior and is there a proper way to fix it?
> >
> > Regards,
> > Yasin Morsli
> >
> >
> > PS: Here is an MWE to test this behavior:
> >
> > #include <stdio.h>
> > #include <fcntl.h>
> > #include <string.h>
> > #include <termios.h>
> >
> > const char* help_msg =
> >    "Usage: %s [tty] [data]\n"
> >    "  tty:  filepath to the tty\n"
> >    "  data: data to transfer\n";
> >
> > int error(const char* msg) {
> >    printf("Error: %s\n", msg);
> >    return -1;
> >}
> >
> >int setspeed(int fd_tty, int speed) {
> >   struct termios tty;
> >    if (tcgetattr(fd_tty, &tty) != 0) return error("tcgetattr failed");
> >
> >    cfsetospeed(&tty, speed);
> >    cfsetispeed(&tty, speed);
> >
> >    if (tcsetattr(fd_tty, TCSANOW, &tty) != 0) return error("tcsetattr failed");

Unless you use TCSADRAIN (or TCSAFLUSH) the driver is not supposed to
wait for the outgoing buffer to drain.

Please confirm if changing this fixes the problem you're seeing.

> >
> >    return 0;
> >}
> >
> >int main(int argc, const char** argv) {
> >    if (argc < 3) {
> >        printf(help_msg, argv[0]);
> >        return 0;
> >    }
> >
> >    const char* path_tty = argv[1];
> >    const char* data_tty = argv[2];
> >
> >    int fd_tty = open(path_tty, O_RDWR | O_NOCTTY);
> >    if (fd_tty < 0) return error("open failed");
> >
> >    struct termios tty;
> >    if (tcgetattr(fd_tty, &tty) != 0) return error("tcgetattr failed");
> >
> >    tty.c_cflag &= ~(CSIZE  | PARENB | CRTSCTS);
> >    tty.c_cflag |=  (CS7 | CSTOPB);
> >    tty.c_iflag &= ~(IXON | IXOFF | IXANY | IGNBRK);
> >    tty.c_lflag = 0;
> >    tty.c_oflag = 0;
> >    tty.c_cc[VMIN] = 0;
> >
> >    if (tcsetattr(fd_tty, TCSANOW, &tty) != 0) return error("tcsetattr failed");
> >
> >    if (setspeed(fd_tty, B300) != 0) return error("setspeed failed");
> >    write(fd_tty, data_tty, strlen(data_tty));
> >    tcdrain(fd_tty);
> >
> >    if (setspeed(fd_tty, B4800) != 0) return error("setspeed failed");
> >    write(fd_tty, data_tty, strlen(data_tty));
> >    tcdrain(fd_tty);
> >
> >    close(fd_tty);
> >
> >    return 0;
> >}
> 
> I've found this older thread
> https://www.spinics.net/lists/linux-usb/msg71689.html.  The proposed
> solution or patch with chars_in_buffer() function doesn't exist in
> more recent kernels (4.19 or newer), but the same functionality is
> achieved by ftdi_tx_empty(), which is indeed called, when tcdrain() is
> called from userspace.  ftdi_tx_empty() calls ftdi_get_modem_status()
> and checks whether FTDI_RS_TEMPT flag is set. If set (i.e. shift
> register empty) ftdi_tx_empty() returns true.
> 
> But I wonder why FTDI_RS_THRE (transmit holding register empty) is not
> taken into account.  Furthermore, I can not find any checks for
> tx-fifos. But possibly the FTDI chip has a guarantee that if
> FTDI_RS_TEMPT is set, the holding register and internal tx-fifos are
> empty, too.

That's the way it's supposed to work, yes (i.e. FTDI_RS_TEMPT implies
FTDI_RS_THRE). 

> As Yasin stated above, it can be observed that the chip transmits
> data, even if the driver reports ftdi_tx_empty() == true. Possibly due
> to a bug in the driver or by poor chip design.
> 
> Any thoughts on this?

Please try using TCSADRAIN first.

Johan



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

  Powered by Linux