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