From 7db3a8a34af23c34bc5cc063036bf479bbc36612 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Fri, 2 Nov 2018 12:47:55 +0100 Subject: [PATCH] Tests: USB: DTR fix for Linux hosts A DTR line is used to signal that the host has configured a terminal and is ready to transmit and receive data from the USB CDC/Serial device. When this test suite is run with the use of a Linux host, a workaround has to be used to overcome some platform specific DTR line behavior. Every time the serial port file descriptor is opened, the DTR line is asserted until the terminal attributes are set. As a consequence, the device receives a premature DTR signal with a duration of 200-500 us before the correct, long-lasting DTR signal set by the host-side test script. (tested on the Linux kernel 4.15.0) The solution is to wait for the first DTR spike, ignore it, and wait for the correct DTR signal again. --- TESTS/host_tests/usb_device_serial.py | 29 +++++----- TESTS/usb_device/serial/main.cpp | 78 +++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/TESTS/host_tests/usb_device_serial.py b/TESTS/host_tests/usb_device_serial.py index 2280b6db8e..00fdc095d2 100644 --- a/TESTS/host_tests/usb_device_serial.py +++ b/TESTS/host_tests/usb_device_serial.py @@ -128,7 +128,7 @@ class USBSerialTest(mbed_host_tests.BaseHostTest): def port_open_wait(self): """Open the serial and wait until it's closed by the device.""" - mbed_serial = serial.Serial() + mbed_serial = serial.Serial(dsrdtr=False) mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( @@ -139,19 +139,20 @@ class USBSerialTest(mbed_host_tests.BaseHostTest): fun=mbed_serial.open, num_retries=20, retry_delay=0.05) - mbed_serial.dtr = True - try: - mbed_serial.read() # wait until closed - except (serial.portNotOpenError, serial.SerialException): - pass except RetryError as exc: self.log('TEST ERROR: {}'.format(exc)) self.notify_complete(False) return + mbed_serial.dtr = True + try: + mbed_serial.read() # wait until closed + except (serial.portNotOpenError, serial.SerialException): + pass def port_open_close(self): """Open the serial and close it with a delay.""" - mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1) + mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=False) + mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable @@ -161,14 +162,14 @@ class USBSerialTest(mbed_host_tests.BaseHostTest): fun=mbed_serial.open, num_retries=20, retry_delay=0.05) - mbed_serial.reset_output_buffer() - mbed_serial.dtr = True - time.sleep(TERM_REOPEN_DELAY) - mbed_serial.close() except RetryError as exc: self.log('TEST ERROR: {}'.format(exc)) self.notify_complete(False) return + mbed_serial.reset_output_buffer() + mbed_serial.dtr = True + time.sleep(TERM_REOPEN_DELAY) + mbed_serial.close() def send_data_sequence(self, chunk_size=1): """Open the serial and send a sequence of values. @@ -176,7 +177,7 @@ class USBSerialTest(mbed_host_tests.BaseHostTest): chunk_size defines the size of data sent in each write operation. The input buffer content is discarded. """ - mbed_serial = serial.Serial(write_timeout=0.1) + mbed_serial = serial.Serial(write_timeout=0.1, dsrdtr=False) try: mbed_serial.port = retry_fun_call( fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable @@ -211,7 +212,7 @@ class USBSerialTest(mbed_host_tests.BaseHostTest): def loopback(self): """Open the serial and send back every byte received.""" - mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1) + mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=False) mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( @@ -248,7 +249,7 @@ class USBSerialTest(mbed_host_tests.BaseHostTest): New line coding params are read from the device serial data. """ - mbed_serial = serial.Serial(timeout=0.5) + mbed_serial = serial.Serial(timeout=0.5, dsrdtr=False) mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( diff --git a/TESTS/usb_device/serial/main.cpp b/TESTS/usb_device/serial/main.cpp index a356595003..c6c804a191 100644 --- a/TESTS/usb_device/serial/main.cpp +++ b/TESTS/usb_device/serial/main.cpp @@ -52,6 +52,24 @@ // This way the device has to correctly handle data bigger that its buffer. #define HOST_RX_BUFF_SIZE_RATIO 64 +// A DTR line is used to signal that the host has configured a terminal and +// is ready to transmit and receive data from the USB CDC/Serial device. +// When this test suite is run with the use of a Linux host, a workaround has +// to be used to overcome some platform specific DTR line behavior. +// Every time the serial port file descriptor is opened, the DTR line is +// asserted until the terminal attributes are set. +// As a consequence, the device receives a premature DTR signal with a +// duration of 200-500 us before the correct, long-lasting DTR signal set by +// the host-side test script. (tested on the Linux kernel 4.15.0) +// +// Online references: +// https://github.com/pyserial/pyserial/issues/124#issuecomment-227235402 +// +// The solution is to wait for the first DTR spike, ignore it, and wait for +// the correct DTR signal again. +#define LINUX_HOST_DTR_FIX 1 +#define LINUX_HOST_DTR_FIX_DELAY_MS 1 + #define CDC_LOOPBACK_REPS 1200 #define SERIAL_LOOPBACK_REPS 100 #define USB_RECONNECT_DELAY_MS 1 @@ -275,6 +293,10 @@ void test_cdc_usb_reconnect() greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY); // Wait for the host to open the port. +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); TEST_ASSERT_TRUE(usb_cdc.configured()); TEST_ASSERT_TRUE(usb_cdc.ready()); @@ -296,6 +318,10 @@ void test_cdc_usb_reconnect() greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY); // Wait for the host to open the port again. +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); TEST_ASSERT_TRUE(usb_cdc.configured()); TEST_ASSERT_TRUE(usb_cdc.ready()); @@ -317,6 +343,10 @@ void test_cdc_rx_single_bytes() TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); usb_cdc.connect(); greentea_send_kv(MSG_KEY_SEND_BYTES_SINGLE, MSG_VALUE_DUMMY); +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); uint8_t buff = 0x01; for (int expected = 0xff; expected >= 0; expected--) { @@ -360,6 +390,10 @@ void test_cdc_rx_single_bytes_concurrent() TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); usb_cdc.connect(); greentea_send_kv(MSG_KEY_SEND_BYTES_SINGLE, MSG_VALUE_DUMMY); +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); Thread tx_thread; event_flags.set(EF_SEND); @@ -393,6 +427,10 @@ void test_cdc_rx_multiple_bytes() TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); usb_cdc.connect(); greentea_send_kv(MSG_KEY_SEND_BYTES_MULTIPLE, HOST_RX_BUFF_SIZE_RATIO); +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); uint8_t buff[RX_BUFF_SIZE] = { 0 }; uint8_t expected_buff[RX_BUFF_SIZE] = { 0 }; @@ -429,6 +467,10 @@ void test_cdc_rx_multiple_bytes_concurrent() TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); usb_cdc.connect(); greentea_send_kv(MSG_KEY_SEND_BYTES_MULTIPLE, HOST_RX_BUFF_SIZE_RATIO); +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); Thread tx_thread; event_flags.set(EF_SEND); @@ -470,6 +512,10 @@ void test_cdc_loopback() TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); usb_cdc.connect(); greentea_send_kv(MSG_KEY_LOOPBACK, MSG_VALUE_DUMMY); +#if LINUX_HOST_DTR_FIX + usb_cdc.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_cdc.wait_ready(); uint8_t rx_buff, tx_buff; for (int i = 0; i < CDC_LOOPBACK_REPS; i++) { @@ -511,6 +557,10 @@ void test_serial_usb_reconnect() greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY); // Wait for the host to open the port. +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif while (!usb_serial.connected()) { wait_ms(1); } @@ -537,6 +587,10 @@ void test_serial_usb_reconnect() greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY); // Wait for the host to open the port again. +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif while (!usb_serial.connected()) { wait_ms(1); } @@ -563,6 +617,10 @@ void test_serial_term_reopen() usb_serial.connect(); greentea_send_kv(MSG_KEY_PORT_OPEN_CLOSE, MSG_VALUE_DUMMY); // Wait for the host to open the terminal. +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif while (!usb_serial.connected()) { wait_ms(1); } @@ -582,6 +640,10 @@ void test_serial_term_reopen() greentea_send_kv(MSG_KEY_PORT_OPEN_CLOSE, MSG_VALUE_DUMMY); // Wait for the host to open the terminal again. +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif while (!usb_serial.connected()) { wait_ms(1); } @@ -613,9 +675,11 @@ void test_serial_getc() TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); usb_serial.connect(); greentea_send_kv(MSG_KEY_SEND_BYTES_SINGLE, MSG_VALUE_DUMMY); - while (!usb_serial.connected()) { - wait_ms(1); - } +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_serial.wait_ready(); for (int expected = 0xff; expected >= 0; expected--) { TEST_ASSERT_EQUAL_INT(expected, usb_serial.getc()); } @@ -643,6 +707,10 @@ void test_serial_printf_scanf() TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); usb_serial.connect(); greentea_send_kv(MSG_KEY_LOOPBACK, MSG_VALUE_DUMMY); +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_serial.wait_ready(); static const char fmt[] = "Formatted\nstring %i."; int tx_val, rx_val, rc; @@ -684,6 +752,10 @@ void test_serial_line_coding_change() TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); usb_serial.connect(); greentea_send_kv(MSG_KEY_CHANGE_LINE_CODING, MSG_VALUE_DUMMY); +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif usb_serial.wait_ready(); usb_serial.attach(line_coding_changed_cb); size_t num_line_codings = sizeof test_codings / sizeof test_codings[0];