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

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

 



> 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.
> 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. 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.
>
> 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");
>
>    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.

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?

Carsten




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

  Powered by Linux