Repository: serialport/serialport-rs (language: rust)

Issue:
Ubuntu 20.10: hardware_test receive fails
This issue was migrated from GitLab. The original issue can be found here:
https://gitlab.com/susurrus/serialport-rs/-/issues/108
---
The read functionality does not work properly with CP210x. `hardware_test` fails. In a custom test project `read()` does not read from the input buffer and `read_to_string()` always returns a timeout error, but does read from the input buffer. rx-data are checked with a scope. All test were made with the current master ( 821cdf1aa6171fa4ce61127bdead86d0a74fe359 ).

Environment:
- Ubuntu 20.10 (kernel 5.8.0-48-generic)
- Rust: rustc 1.51.0 (2fd73fabe 2021-03-23)

```sh
$lsusb
Bus 001 Device 012: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
# ...

# lsmod during running custom test program
$ lsmod
Module                  Size  Used by
# ...
cp210x                 36864  1
# ...

# ttyUSB0 verified as CP210x
$ ls /dev | grep USB
ttyUSB0
```

library hardware test run:

```sh
$ cargo run --example hardware_check /dev/ttyUSB0 --loopback
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/examples/hardware_check /dev/ttyUSB0 --loopback`
Testing '/dev/ttyUSB0':
Testing baud rates...
  9600: success
  38400: success
  115200: FAILED (baud rate 115384 does not match set baud rate 115200)
Testing non-standard baud rates...
  10000: success
  600000: success
  1800000: FAILED (baud rate 1846153 does not match set baud rate 1800000)
Testing data bits...
  Five: success
  Six: success
  Seven: success
  Eight: success
Testing flow control...
  Software: success
  Hardware: success
  None: success
Testing parity...
  Odd: success
  Even: success
  None: success
Testing stop bits...
  Two: success
  One: success
Testing bytes to read and write...
  SerialPort::bytes_to_write: success
  SerialPort::bytes_to_read: success
Test clearing software buffers...
  Input: success
  Output: success
  All: success
Testing data transmission...success
Testing data reception...FAILED
```

The output of the test run was verified with an scope. See the image below:

![SCR04](/uploads/207990c2e07ed539147e0e42ae9b81a4/SCR04.PNG)

Scope decoding configuration:

![SCR05](/uploads/2ad6eaa8514df1c602f362947466b85f/SCR05.PNG)

Hardware (with loopback) was tested with another program (CuteCom). In that case, tx and rx were working properly, so I assume, the bug will probably be within this library or within a dependency.


Discussion / hints:
Reproduced this on my system (not including the baudrate failures; those are likely unrelated): 

```
$ lsusb
0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC

$ uname -ar
5.13.0-27-generic #29~20.04.1-Ubuntu
```
The issue can be trivially fixed by setting the port timeout on my system:

```diff
diff --git a/examples/hardware_check.rs b/examples/hardware_check.rs
index 881b680..2107e50 100644
--- a/examples/hardware_check.rs
+++ b/examples/hardware_check.rs
@@ -247,11 +247,13 @@ fn test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool) {
         .expect("Unable to write bytes.");
     println!("success");
 
-    print!("Testing data reception...");
     if loopback {
+        print!("Testing data reception...");
+        port.set_timeout(Duration::from_millis(1000)).ok();
+
         let mut buf = [0u8; 12];
-        if port.read_exact(&mut buf).is_err() {
-            println!("FAILED");
+        if let Err(e) = port.read_exact(&mut buf) {
+            println!("FAILED ({})", e);
         } else {
             assert_eq!(
                 str::from_utf8(&buf).unwrap(),
```

The default timeout is zero:

https://github.com/serialport/serialport-rs/blob/a84a24539c6c02880096a91076f14a1bae0989c1/examples/hardware_check.rs#L430

I'm not sure what the expected behavior of 'read_exact' with a zero-duration timeout is, however.

**Edit:**

The timeout duration is eventually consumed by either [nix::poll::ppoll or nix::poll::poll](https://docs.rs/nix/latest/nix/poll/fn.ppoll.html) on posix systems:

https://github.com/serialport/serialport-rs/blob/a84a24539c6c02880096a91076f14a1bae0989c1/src/posix/poll.rs#L32

Both of these functions support a format for specifying 'no timeout', however, serialport-rs can't currently express this as its timeout is not optional. I think that supporting `Option<Duration>` would be a nice feature for serialport-rs, but constitutes a fairly significant change.

For the scope of this issue, adding a non-zero timeout to the example seems reasonable.

Fix patch:
diff --git a/examples/hardware_check.rs b/examples/hardware_check.rs
index 881b680f..20922d71 100644
--- a/examples/hardware_check.rs
+++ b/examples/hardware_check.rs
@@ -247,11 +247,13 @@ fn test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool) {
         .expect("Unable to write bytes.");
     println!("success");
 
-    print!("Testing data reception...");
     if loopback {
+        print!("Testing data reception...");
+        port.set_timeout(Duration::from_millis(250)).ok();
+
         let mut buf = [0u8; 12];
-        if port.read_exact(&mut buf).is_err() {
-            println!("FAILED");
+        if let Err(e) = port.read_exact(&mut buf) {
+            println!("FAILED ({})", e);
         } else {
             assert_eq!(
                 str::from_utf8(&buf).unwrap(),


Test patch: