diff --git a/TESTS/host_tests/pyusb_basic.py b/TESTS/host_tests/pyusb_basic.py index 60dcb3ec3e..e7a791a66f 100644 --- a/TESTS/host_tests/pyusb_basic.py +++ b/TESTS/host_tests/pyusb_basic.py @@ -290,6 +290,19 @@ class PyusbBasicTest(BaseHostTest): except (RuntimeError) as exc: self.report_error(exc) + def _callback_ep_test_data_toggle(self, key, value, timestamp): + self.log("Received serial %s" % (value)) + + dev = self.find_device(value) + if(dev == None): + return + + try: + ep_test_data_toggle(dev, log=print) + self.report_success() + except (RuntimeError) as exc: + self.report_error(exc) + def _callback_reset_support(self, key, value, timestamp): status = "false" if sys.platform == "darwin" else "true" self.log("Reset supported: %s" % status) @@ -345,6 +358,7 @@ class PyusbBasicTest(BaseHostTest): self.register_callback('ep_test_parallel_transfers', self._callback_ep_test_parallel_transfers) self.register_callback('ep_test_parallel_transfers_ctrl', self._callback_ep_test_parallel_transfers_ctrl) self.register_callback('ep_test_abort', self._callback_ep_test_abort) + self.register_callback('ep_test_data_toggle', self._callback_ep_test_data_toggle) self.register_callback('reset_support', self._callback_reset_support) @@ -1354,6 +1368,106 @@ def ep_test_abort(dev, log, verbose=False): NUM_PACKETS_UNTIL_ABORT * ep_out.wMaxPacketSize, payload_size)) +def ep_test_data_toggle(dev, log, verbose=False): + """Test data toggle reset for bulk OUT/IN endpoint pairs. + + Given a USB device + When an interface is set + Then the data toggle bits for all endpoints are reset to DATA0 + When clear feature is called for an endpoint that *IS NOT* stalled + Then the data toggle is reset to DATA0 for that endpoint + When clear halt is called for an endpoint that *IS* stalled + Then the data toggle is reset to DATA0 for that endpoint + """ + cfg = dev.get_active_configuration() + for intf in cfg: + log('interface {}, alt {} -- '.format(intf.bInterfaceNumber, intf.bAlternateSetting), end='') + if intf.bAlternateSetting == 0: + log('skipping the default AlternateSetting') + continue + log('running tests') + + if verbose: + log('Testing data toggle reset for bulk endpoint pair.') + + # 1.1 reset OUT and IN data toggle to DATA0 + intf.set_altsetting() + bulk_out, bulk_in = find_ep_pair(intf, usb.ENDPOINT_TYPE_BULK) + + # 1.2 send and receive a single data packet, + # so both OUT and IN endpoints switch to DATA1 + loopback_ep_test(bulk_out, bulk_in, bulk_out.wMaxPacketSize) + + # 1.3 reset OUT and IN data toggle to DATA0 + # USB spec, section 9.1.1.5 + # " + # Configuring a device or changing an alternate setting causes all of the status and + # configuration values associated with endpoints in the affected interfaces to be set to their default values. + # This includes setting the data toggle of any endpoint using data toggles to the value DATA0. + # " + intf.set_altsetting() + bulk_out, bulk_in = find_ep_pair(intf, usb.ENDPOINT_TYPE_BULK) + + # 1.4 verify that host and USB device are still in sync with respect to data toggle + try: + loopback_ep_test(bulk_out, bulk_in, bulk_out.wMaxPacketSize) + except usb.USBError as err: + if verbose: + log(USB_ERROR_FMT.format(err, bulk_out, bulk_in, bulk_out.wMaxPacketSize)) + raise_unconditionally(lineno(), 'Data toggle not reset when setting interface.') + + # 2.1 reset OUT and IN data toggle to DATA0 + intf.set_altsetting() + bulk_out, bulk_in = find_ep_pair(intf, usb.ENDPOINT_TYPE_BULK) + + # 2.2 send and receive a single data packet, + # so both OUT and IN endpoints switch to DATA1 + loopback_ep_test(bulk_out, bulk_in, bulk_out.wMaxPacketSize) + + # 2.3 reset OUT data toggle to DATA0 + # USB spec, section 9.4.5 + # " + # For endpoints using data toggle, regardless of whether an endpoint has the Halt feature set, a + # ClearFeature(ENDPOINT_HALT) request always results in the data toggle being reinitialized to DATA0. + # " + bulk_out.clear_halt() +# request_endpoint_read_start(dev, bulk_out) + + # 2.4 verify that host and USB device are still in sync with respect to data toggle + try: + loopback_ep_test(bulk_out, bulk_in, bulk_out.wMaxPacketSize) + except usb.USBError as err: + if verbose: + log(USB_ERROR_FMT.format(err, bulk_out, bulk_in, bulk_out.wMaxPacketSize)) + raise_unconditionally(lineno(), 'Data toggle not reset when calling ClearFeature(ENDPOINT_HALT) ' + 'on an endpoint that has not been halted.') + + # 3.1 reset OUT and IN data toggle to DATA0 + intf.set_altsetting() + bulk_out, bulk_in = find_ep_pair(intf, usb.ENDPOINT_TYPE_BULK) + + # 3.2 send and receive a single data packet, + # so both OUT and IN endpoints switch to DATA1 + loopback_ep_test(bulk_out, bulk_in, bulk_out.wMaxPacketSize) + + # 3.3 reset IN data toggle to DATA0 + # USB spec, section 9.4.5 + # " + # For endpoints using data toggle, regardless of whether an endpoint has the Halt feature set, a + # ClearFeature(ENDPOINT_HALT) request always results in the data toggle being reinitialized to DATA0. + # " + usb.control.set_feature(dev, FEATURE_ENDPOINT_HALT, bulk_in) + bulk_in.clear_halt() + + # 3.4 verify that host and USB device are still in sync with respect to data toggle + try: + loopback_ep_test(bulk_out, bulk_in, bulk_out.wMaxPacketSize) + except usb.USBError as err: + if verbose: + log(USB_ERROR_FMT.format(err, bulk_out, bulk_in, bulk_out.wMaxPacketSize)) + raise_unconditionally(lineno(), 'Data toggle not reset when clearing endpoint halt.') + + def device_reset_test(log): """ Test USB implementation against repeated reset diff --git a/TESTS/usb_device/basic/main.cpp b/TESTS/usb_device/basic/main.cpp index 7a030473c5..18d980108c 100644 --- a/TESTS/usb_device/basic/main.cpp +++ b/TESTS/usb_device/basic/main.cpp @@ -385,6 +385,45 @@ void ep_test_abort() } } +/** Test data toggle reset for bulk OUT/IN endpoint pairs + * + * Given a USB device + * When an interface is set + * Then the data toggle bits for all endpoints are reset to DATA0 + * When clear feature is called for an endpoint that *IS NOT* stalled + * Then the data toggle is reset to DATA0 for that endpoint + * When clear halt is called for an endpoint that *IS* stalled + * Then the data toggle is reset to DATA0 for that endpoint + */ +void ep_test_data_toggle() +{ + uint16_t vendor_id = 0x0d28; + // Use a product ID different than that used in other tests, + // to help Windows hosts use the correct configuration descriptor. + uint16_t product_id = 0x0206; + uint16_t product_release = 0x0001; + char _key[11] = { }; + char _value[128] = { }; + + { + USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, false); + greentea_send_kv("ep_test_data_toggle", serial.get_serial_desc_string()); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); +#if EP_DBG + wait_ms(100); + printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf()); + printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf()); + printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out()); + printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in()); + printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out()); + printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in()); + printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out()); + printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in()); +#endif + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + /** Test USB implementation against repeated reset Given an initialized USB (HOST <---> DUT connection established) @@ -602,7 +641,8 @@ Case cases[] = { Case("endpoint test halt", ep_test_halt), Case("endpoint test parallel transfers", ep_test_parallel_transfers), Case("endpoint test parallel transfers ctrl", ep_test_parallel_transfers_ctrl), - Case("endpoint test abort", ep_test_abort) + Case("endpoint test abort", ep_test_abort), + Case("endpoint test data toggle reset", ep_test_data_toggle) }; utest::v1::status_t greentea_test_setup(const size_t number_of_cases)