3
votes

When opening an FDTI USB UART based serial port plugged into the USB host of an ARM9 based embedded board, it spontaneously transmits data. It does this right upon opening, even before the bit rate has been set, or anything else has been done to the fd.

The data being sent is always more or less the same:

^@^@^@^@^@

In hexadecimal notation that is: 0x5e 0x40 0x5e 0x40 0x5e 0x40 0x5e 0x40 0x5e 0x40. The number of repetitions varies from a single pair, up to 256 pairs.

I have traced back this data and it origins from the tty Kernel workqueue. So it is not caused by a buggy driver with an improperly initialized transmit queue or anything along that line. Clearly the Kernel is purposely transmitting this data, upon the open syscall.

This problem only occurs when opening the tty port for the first time after reboot or after plugging in the FTDI USB UART. (A reboot power-cycles the USB device, so those two are basically the same.)

And yes, I do realize that ^@ means '\0'. And also that NULL can be used to implement delays. From the manual:

The delay bits specify how long transmission stops to allow for mechanical or other movement when certain characters are sent to the terminal. In all cases a value of 0 shall indicate no delay. If OFILL is set, fill characters shall be transmitted for delay instead of a timed delay. This is useful for high baud rate terminals which need only a minimal delay. If OFDEL is set, the fill character shall be DEL; otherwise, NUL.

However, my TTY settings don't indicate such a setting being active:

root@IVW78103 ~$ stty -F /dev/ttyUSB0 -a
speed 9600 baud;stty: /dev/ttyUSB0
 line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

Now the last - and most important - clue that I have is that the device on the serial port side of the FTDI outputs a lot of data when powering up (= USB plug-in). And when I isolate the FTDI RxD line, the problem doesn't occur. So obviously the Kernel, termios or tty takes offence in this data.

Now the question is: How do I prevent this ^@ data from being sent? Because the device on the serial port side of the FTDI doesn't respond well to it.

Edit: The output seems to be termios echoing input. The symptom vanishes if I lobotomise the Kernel by out-commenting this code in .../drivers/tty/n_tty.c:process_echoes:639

tty_put_char(tty, '^');
tty_put_char(tty, op ^ 0100);

It still puzzles me why it is echoing data received long before opening the tty port. I may be a side effect of USB UARTs not being able to truly flush all data.

2
"even before the bit rate has been set" -- The baudrate is always "set". You have to change it to ensure it's correct. "The data being sent is ...^@... In hexadecimal notation :..." -- Apparently you're using a terminal emulator program, and NUL characters are displayed in text as ^@ keystroke form, so the hex codes for this is irrelevant. "How do I prevent this ^@ data from being sent?" __ The NUL chars are probably due to your (unspecified) setup. I have ARM9 SBCs and FTDI USB-to-RS232 adapters, and have never seen extraneous null characters.sawdust
"even before the bit rate has been set", as in: Even before I had the chance of changing it into the desired bit rate. Or; Even before I could do anything else.Robert
No; I'm not using a terminal emulator; I'm using a logic analyser and I can literally see 0x5e and 0x40 passing by. Therefore I also know for sure that the device on the serial port side of the FTDI has not output any data for 20 up to 20 seconds before opening the tty port. It really seems to be a belated echo of that >20 seconds old data.Robert

2 Answers

1
votes

This problem is caused by a combination of termios echo being enabled by default and the FTDI tty device driver not supporting flushing.

The latter is explained by Greg in his comment here. His comment is mostly about TCOFLUSH, but apparently TCIFLUSH isn't supported either. The comment is specifically about the FDTI tty driver, but I'm sure the same goes for many - if not all - other USB UART drivers.

The error mode goes as follows: When the USB serial device is plugged in and powers up, it performs it's POST and dumps the resulting output on the serial port, on a whole different bit rate as the default for that port. When later an application opens that tty port, it is not flushed upon opening. The accumulated POST data still in the shift-register, FIFO and USB buffers, are being read from that moment on, and will be echoed back because echo is enabled by default. The echoed data looks like NULL characters or junk because of the bit rate mismatch.

Since the real solution is to implement flushing properly, and since that may be difficult to impossible, I have disabled termios echo per default for all USB UARTs in the Kernel with this simple patch:

    Index: drivers/usb/serial/usb-serial.c
===================================================================
--- drivers/usb/serial/usb-serial.c (revision 1166)
+++ drivers/usb/serial/usb-serial.c (working copy)
@@ -1270,6 +1270,8 @@
                            | HUPCL | CLOCAL;
    usb_serial_tty_driver->init_termios.c_ispeed = 9600;
    usb_serial_tty_driver->init_termios.c_ospeed = 9600;
+   usb_serial_tty_driver->init_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+   usb_serial_tty_driver->init_termios.c_oflag &= ~OPOST;
    tty_set_operations(usb_serial_tty_driver, &serial_ops);
    result = tty_register_driver(usb_serial_tty_driver);
    if (result) {

This patch does a little more; It puts the tty port in raw mode, because I'm not taking any chances any more. Disabling ICANON and ECHO should suffice.

With this patch in place, the spontaneously transmitted data upon opening is gone.

After opening, I still receive the POST data from before opening in the form on NULL characters. And not immediately after opening, as I would expect, but only after sending out the first bytes. This is probably due to a receive event in the driver being ignored before it is opened, and not being triggered after opening because nothing was received after opening.

Since my application ignores unframed data at launch, I have decided that the work around in this patch is the solution for this problem.

I hope it helps others too.

0
votes

This is the connection scheme: device --- usb-rs232 convertor --- USB host.

Using device with native USB support and no usb-rs232 convertors solves all problems of interfacing a device with USB host.

USB includes flow control implemented at the hardware level. USB device will automatically prevent usb host from transmitting. And vice versa, usb host will automatically prevent USB device from transmitting. The point is that all synchronization is done at the hardware level.

usb-rs232 convertor does not allow synchronization - the result is that it buffers data; example of good application of usb-rs232 convertor is when only RX is used, and incoming data is "framed"; for usb-device to usb-host communication, where synchronization is necessary, usb-rs232 convertors are inappropriate for the job - only native usb must be used.

Then on usb host side you may disable echo after opening the tty device and then give an indication to usb device that it is allowed to send now (this may be done via write() or via out-of-band DTR signal using ioctl()).