ftdi_sio: Problem when changing the baud rate after a transfer

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

 



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;
}



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

  Powered by Linux