linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* ftdi_sio: Problem when changing the baud rate after a transfer
@ 2022-01-10 13:27 embedded (VIVAVIS AG)
  2022-01-28 10:09 ` AW: " embedded (VIVAVIS AG)
  0 siblings, 1 reply; 5+ messages in thread
From: embedded (VIVAVIS AG) @ 2022-01-10 13:27 UTC (permalink / raw)
  To: linux-usb

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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* AW: ftdi_sio: Problem when changing the baud rate after a transfer
  2022-01-10 13:27 ftdi_sio: Problem when changing the baud rate after a transfer embedded (VIVAVIS AG)
@ 2022-01-28 10:09 ` embedded (VIVAVIS AG)
  2022-01-31  7:55   ` Johan Hovold
  0 siblings, 1 reply; 5+ messages in thread
From: embedded (VIVAVIS AG) @ 2022-01-28 10:09 UTC (permalink / raw)
  To: linux-usb

> Gesendet: Montag, 10. Januar 2022 14:27
> An: linux-usb@vger.kernel.org
> 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


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: ftdi_sio: Problem when changing the baud rate after a transfer
  2022-01-28 10:09 ` AW: " embedded (VIVAVIS AG)
@ 2022-01-31  7:55   ` Johan Hovold
  2022-02-21 15:08     ` AW: " embedded (VIVAVIS AG)
  0 siblings, 1 reply; 5+ messages in thread
From: Johan Hovold @ 2022-01-31  7:55 UTC (permalink / raw)
  To: embedded (VIVAVIS AG); +Cc: linux-usb

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@vger.kernel.org
> > 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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* AW: ftdi_sio: Problem when changing the baud rate after a transfer
  2022-01-31  7:55   ` Johan Hovold
@ 2022-02-21 15:08     ` embedded (VIVAVIS AG)
  2022-04-11  8:46       ` Johan Hovold
  0 siblings, 1 reply; 5+ messages in thread
From: embedded (VIVAVIS AG) @ 2022-02-21 15:08 UTC (permalink / raw)
  To: Johan Hovold; +Cc: linux-usb

Hi,
thanks for the suggestion.

> > > 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.
Unfortunately, I cannot use a more recent kernel due to restrictions
with the development environment I am using. I did compare the code of
the ftdi_sio driver in version 4.19.190 with code included in a more
recent kernel release. I did not spot any fundamental changes that would
suggest that the problem was fixed.

> > > 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?
I checked the code of the ftdi_sio driver, which captures the state of
the tx empty flag that is reported by the IC every time a specific USB
message is sent to it.

> 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.
I can still observe the same behavior, even after changing the calls
to tcsetattr to use TCSADRAIN instead of TCSANOW. The last byte of a
transfer is still being shifted out with the wrong baud rate.
Is there anything else that can be done in software to possibly prevent this behavior?

Thanks and regards,
Yasin Morsli

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: ftdi_sio: Problem when changing the baud rate after a transfer
  2022-02-21 15:08     ` AW: " embedded (VIVAVIS AG)
@ 2022-04-11  8:46       ` Johan Hovold
  0 siblings, 0 replies; 5+ messages in thread
From: Johan Hovold @ 2022-04-11  8:46 UTC (permalink / raw)
  To: embedded (VIVAVIS AG); +Cc: linux-usb

On Mon, Feb 21, 2022 at 03:08:43PM +0000, embedded (VIVAVIS AG) wrote:
> Hi,
> thanks for the suggestion.
> 
> > > > 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.
> Unfortunately, I cannot use a more recent kernel due to restrictions
> with the development environment I am using. I did compare the code of
> the ftdi_sio driver in version 4.19.190 with code included in a more
> recent kernel release. I did not spot any fundamental changes that would
> suggest that the problem was fixed.

Then you're on your own, sorry. Perhaps you can bring it up with FTDI in
case it is a known firmware issue.

> > > > 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?
> I checked the code of the ftdi_sio driver, which captures the state of
> the tx empty flag that is reported by the IC every time a specific USB
> message is sent to it.

Yeah, the driver supports that, but did you make sure that that function
is actually called *before* changing the baud rate when you observe the
problem? Note that it is also called during close.

You need to trace the calls (or USB requests) to make sure that what you
think is happening is indeed happening. The missing TCSADRAIN in your
test program is an example of why that is needed.

> > 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.
> I can still observe the same behavior, even after changing the calls
> to tcsetattr to use TCSADRAIN instead of TCSANOW. The last byte of a
> transfer is still being shifted out with the wrong baud rate.
> Is there anything else that can be done in software to possibly
> prevent this behavior?

If you can reproduce this on a recent mainline kernel and show that it
is indeed a driver or firmware issue, perhaps we can try to find a way
to work around it.

Good luck!

Johan

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-04-11  8:46 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-10 13:27 ftdi_sio: Problem when changing the baud rate after a transfer embedded (VIVAVIS AG)
2022-01-28 10:09 ` AW: " embedded (VIVAVIS AG)
2022-01-31  7:55   ` Johan Hovold
2022-02-21 15:08     ` AW: " embedded (VIVAVIS AG)
2022-04-11  8:46       ` Johan Hovold

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).