diff --git a/TESTS/usb_device/README.md b/TESTS/usb_device/README.md deleted file mode 100644 index 38f6bdb..0000000 --- a/TESTS/usb_device/README.md +++ /dev/null @@ -1,166 +0,0 @@ -# Testing the Mbed OS USB device - -## Setup -Before running tests, please make sure to use a -[top-level requirements.txt][LN-requirements] file to install all the -required Python modules. - -``` -pip install -r requirements.txt -``` - -Additional, platform-specific setup is described below. -See also [Known issues](#known-issues). - -### Windows -1. Install a **generic USB driver** for two test devices. - 1. Download `Zadig` application from [the Zadig website][LN-zadig]. - 1. Unplug the Mbed device. - 1. Open `Zadig`. - 1. Select *Device -> Load Preset Device*. - 1. Open [TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg][LN-zadig_conf1] - 1. Choose `libusb-win32 (v1.2.6.0)` driver. - 1. Select `Install Driver` and click it. - 1. Select *Device -> Load Preset Device*. - 1. Open [TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg][LN-zadig_conf2] - 1. Choose `libusb-win32 (v1.2.6.0)` driver. - 1. Select `Install Driver` and click it. - 1. Close `Zadig`. - 1. Plug both USB interfaces (*DAPLink* and *USB device*). - -### Linux -1. Install the `hidapi` Python module, otherwise some USB HID test cases will - be skipped. This module is not installed during the initial setup due to - external dependencies for Linux. - - For Debian-based Linux distros, the dependencies can be installed as follows - (based on module's [README][LN-hidapi_readme]): - - ```bash - apt-get install python-dev libusb-1.0-0-dev libudev-dev - pip install --upgrade setuptools - ``` - - To install the `hidapi` module itself, please use the attached - [requirements.txt][LN-hid_requirements] file: - - ```bash - pip install -r TESTS/usb_device/hid/requirements.txt - ``` - -1. Update the `udev` rules for Mbed USB CDC device as follows - ([source][LN-udev_rules]): - - ```bash - sudo tee /etc/udev/rules.d/99-ttyacms.rules >/dev/null < -m -DUSB_DEVICE_TESTS -n tests-usb_device-* - ``` - -## Known issues - -### Insufficient user permissions on a Linux machine -Some of the tests require privileged access to the USB device. Running these -as an unprivileged user will manifest with either of the following errors: -* ``` - [HTST][INF] Device not found - ``` -* ``` - [HTST][INF] TEST ERROR: Failed with "The device has no langid". Tried 20 times. - ``` - -#### Solution -Execute tests with elevated permissions using `sudo`: -```bash -mbed test -t -m -n tests-usb_device-* --compile -sudo mbed test -t -m -n tests-usb_device-* --run -v -``` -Note only the `mbed test --run` command requires `sudo`. You can still -`mbed test --compile` as a normal user. - -#### Alternative solution -Add an `udev` rule to set the ownership of the device node -[as shown here][LN-libusb_permissions]. - -### Data toggle reset test fails on a Linux machine -The `tests-usb_device-basic` / `"endpoint test data toggle reset"` test fails -with the following error: -``` -[HTST][INF] TEST FAILED: Data toggle not reset when calling -ClearFeature(ENDPOINT_HALT) on an endpoint that has not been halted. -``` - -Implementations of the *xHCI* driver prior to version 4.17 of the Linux kernel did -not have the functionality necessary to test `"endpoint test data toggle reset"`. -Even though the data toggle is correctly reset on the device side, the host -side will not be synchronized and the test will falsely fail. - -#### Solution -Make sure that **at least one** of the following prerequisites is met: -* using the Linux kernel ***4.17* or newer**, -* using the ***eHCI*** USB driver instead of *xHCI*. - -Changing the USB driver may be as simple as updating one of the BIOS settings -on your machine. If you'd rather avoid rebooting, you can try -[using setpci command][LN-xhci_setpci]. - -#### Further reading -1. [the Linux kernel patch adding missing xHCI behavior][LN-linux_xhci_patch], -1. [LKML discussion explaining the details of this issue][LN-xhci_lkml_discussion]. - -### Mass storage tests are skipped on some targets -The `tests-usb_device-msd` test outputs the following message: -``` -[CONN][RXD] SKIP: Not enough heap memory for HeapBlockDevice creation -``` - -#### Solution -A device with at least *70 kB* of RAM is required to run this test. -The FAT32 filesystem cannot be mounted on a device smaller than 64 kB. - -The test can be easily extended to use any block device available in Mbed. - -### Windows 8/10: Mass storage tests are failing on test file read -By default Windows 8 and 10 access and write to removable drives shortly after they are connected. It's caused by drive indexing mechanisms. Because disk used in test has only 64kB its content can be easily corrupted by writing large files, what actually was encountered during test runs. - -To prevent Windows from writing to removable drives on connect drive indexing can be turned off with the following procedure: -- Start the program "gpedit.msc" -- Navigate to "Computer Configuration \ Administrative Templates \ Windows Components \ Search" -- Enable the policy "Do not allow locations on removable drives to be added to libraries." - -### Isochronous endpoints are skipped in loopback tests -#### Unresolved - -### Serial tests fail intermittently on a Linux machine -#### Unresolved -You may want to connect the device directly to the host machine with no hubs on the way. - - -[LN-requirements]: ../../requirements.txt -[LN-zadig]: https://zadig.akeo.ie/ -[LN-zadig_conf1]: basic/zadig_conf/mbed_os-usb_test_device1.cfg -[LN-zadig_conf2]: basic/zadig_conf/mbed_os-usb_test_device2.cfg -[LN-hidapi_readme]: https://github.com/trezor/cython-hidapi/blob/master/README.rst#install -[LN-hid_requirements]: hid/requirements.txt -[LN-udev_rules]: https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284 -[LN-libusb_permissions]: https://stackoverflow.com/questions/3738173/why-does-pyusb-libusb-require-root-sudo-permissions-on-linux/8582398#8582398 -[LN-linux_xhci_patch]: https://github.com/torvalds/linux/commit/f5249461b504d35aa1a40140983b7ec415807d9e -[LN-xhci_lkml_discussion]: https://lkml.org/lkml/2016/12/15/388 -[LN-xhci_setpci]: https://linuxmusicians.com/viewtopic.php?t=16901 - diff --git a/TESTS/usb_device/basic/USBEndpointTester.cpp b/TESTS/usb_device/basic/USBEndpointTester.cpp deleted file mode 100644 index 4b48e94..0000000 --- a/TESTS/usb_device/basic/USBEndpointTester.cpp +++ /dev/null @@ -1,865 +0,0 @@ -/* - * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if USB_DEVICE_TESTS - -#include "stdint.h" -#include "stdlib.h" -#include "USBEndpointTester.h" -#include "events/mbed_shared_queues.h" -#include "EndpointResolver.h" - -#define DEFAULT_CONFIGURATION (1) - -#define NUM_PACKETS_UNTIL_ABORT 2 -#define NUM_PACKETS_AFTER_ABORT 8 -#define EP_ABORT_BUFF_VALUE 0xff - -/* If the host ever receives a payload with any byte set to this value, - * the device does not handle abort operation correctly. The buffer - * passed to aborted operation must not be used after call to abort(). - */ -#define FORBIDDEN_PAYLOAD_VALUE (NUM_PACKETS_AFTER_ABORT + 1) - -#define VENDOR_TEST_CTRL_IN 1 -#define VENDOR_TEST_CTRL_OUT 2 -#define VENDOR_TEST_CTRL_IN_SIZES 3 -#define VENDOR_TEST_CTRL_OUT_SIZES 4 -#define VENDOR_TEST_RW_RESTART 5 -#define VENDOR_TEST_ABORT_BUFF_CHECK 6 - -#define CTRL_BUF_SIZE (2048) - -#define EVENT_READY (1 << 0) - -#define TEST_SIZE_EP_BULK_MAX (64) -#define TEST_SIZE_EP_BULK_MIN (8) -#define TEST_SIZE_EP_BULK_0 (16) -#define TEST_SIZE_EP_BULK_1 TEST_SIZE_EP_BULK_MAX -#define TEST_SIZE_EP_BULK_2 (32) -#define TEST_SIZE_EP_BULK_3 (16) -#define TEST_SIZE_EP_BULK_4 TEST_SIZE_EP_BULK_MIN - -#define TEST_SIZE_EP_INT_MAX (64) -#define TEST_SIZE_EP_INT_MIN (1) -#define TEST_SIZE_EP_INT_0 (16) -#define TEST_SIZE_EP_INT_1 TEST_SIZE_EP_INT_MAX -#define TEST_SIZE_EP_INT_2 (32) -#define TEST_SIZE_EP_INT_3 (16) -#define TEST_SIZE_EP_INT_4 TEST_SIZE_EP_INT_MIN - - -/* According to USB spec, the wMaxPacketSize for FS isochronous endpoints - * is 1023 B. There are a couple of reasons this value is not used in tests: - * - some of the boards supported by Mbed OS have too little RAM dedicated - * for USB, making EndpointResolve::valid() fail when all the endpoints (2x - * bulk, 2x interrupt, 2x isochronous, 2x control) are configured to use - * the max value of wMaxPacketSize - * (e.g. NUCLEO_F207ZG has 1.25K of endpoint RAM), - * - given a test host with other USB devices on the bus, it is unlikely - * for the test device to be able to reserve the bandwidth associated with - * high wMaxPacketSize for iso endpoints. - */ -#define TEST_SIZE_EP_ISO_MAX (256) -#define TEST_SIZE_EP_ISO_MIN (1) -#define TEST_SIZE_EP_ISO_0 (0) -#define TEST_SIZE_EP_ISO_1 (0) -#define TEST_SIZE_EP_ISO_2 (0) -#define TEST_SIZE_EP_ISO_3 (0) -#define TEST_SIZE_EP_ISO_4 (0) - -#define EP_BULK_OUT 0 -#define EP_BULK_IN 1 -#define EP_INT_OUT 2 -#define EP_INT_IN 3 -#define EP_ISO_OUT 4 -#define EP_ISO_IN 5 - -USBEndpointTester::ep_config_t USBEndpointTester::_intf_config_max[NUM_ENDPOINTS] = { - { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_MAX, static_cast(&USBEndpointTester::_cb_bulk_out) }, - { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_MAX, static_cast(&USBEndpointTester::_cb_bulk_in) }, - { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_MAX, static_cast(&USBEndpointTester::_cb_int_out) }, - { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_MAX, static_cast(&USBEndpointTester::_cb_int_in) }, - { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_MAX, static_cast(&USBEndpointTester::_cb_iso_out) }, - { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_MAX, static_cast(&USBEndpointTester::_cb_iso_in) }, -}; - -USBEndpointTester::ep_config_t USBEndpointTester::_intf_config0[NUM_ENDPOINTS] = { - { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_0, NULL }, - { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_0, NULL }, - { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_0, NULL }, - { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_0, NULL }, - { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_0, NULL }, - { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_0, NULL }, -}; - -USBEndpointTester::ep_config_t USBEndpointTester::_intf_config1[NUM_ENDPOINTS] = { - { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_1, static_cast(&USBEndpointTester::_cb_bulk_out) }, - { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_1, static_cast(&USBEndpointTester::_cb_bulk_in) }, - { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_1, static_cast(&USBEndpointTester::_cb_int_out) }, - { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_1, static_cast(&USBEndpointTester::_cb_int_in) }, - { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_1, static_cast(&USBEndpointTester::_cb_iso_out) }, - { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_1, static_cast(&USBEndpointTester::_cb_iso_in) }, -}; - -USBEndpointTester::ep_config_t USBEndpointTester::_intf_config2[NUM_ENDPOINTS] = { - { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_2, static_cast(&USBEndpointTester::_cb_bulk_out) }, - { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_2, static_cast(&USBEndpointTester::_cb_bulk_in) }, - { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_2, static_cast(&USBEndpointTester::_cb_int_out) }, - { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_2, static_cast(&USBEndpointTester::_cb_int_in) }, - { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_2, static_cast(&USBEndpointTester::_cb_iso_out) }, - { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_2, static_cast(&USBEndpointTester::_cb_iso_in) }, -}; - -USBEndpointTester::ep_config_t USBEndpointTester::_intf_config3[NUM_ENDPOINTS] = { - { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_3, static_cast(&USBEndpointTester::_cb_bulk_out) }, - { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_3, static_cast(&USBEndpointTester::_cb_bulk_in) }, - { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_3, static_cast(&USBEndpointTester::_cb_int_out) }, - { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_3, static_cast(&USBEndpointTester::_cb_int_in) }, - { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_3, static_cast(&USBEndpointTester::_cb_iso_out) }, - { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_3, static_cast(&USBEndpointTester::_cb_iso_in) }, -}; - -USBEndpointTester::ep_config_t USBEndpointTester::_intf_config4[NUM_ENDPOINTS] = { - { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_4, static_cast(&USBEndpointTester::_cb_bulk_out) }, - { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_4, static_cast(&USBEndpointTester::_cb_bulk_in) }, - { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_4, static_cast(&USBEndpointTester::_cb_int_out) }, - { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_4, static_cast(&USBEndpointTester::_cb_int_in) }, - { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_4, static_cast(&USBEndpointTester::_cb_iso_out) }, - { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_4, static_cast(&USBEndpointTester::_cb_iso_in) }, -}; - -USBEndpointTester::USBEndpointTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, - bool abort_transfer_test) : - USBDevice(phy, vendor_id, product_id, product_release), _abort_transfer_test(abort_transfer_test), _endpoint_configs( - &_intf_config_max) -{ - _cnt_cb_set_conf = 0; - _cnt_cb_set_intf = 0; - _cnt_cb_bulk_out = 0; - _cnt_cb_bulk_in = 0; - _cnt_cb_int_out = 0; - _cnt_cb_int_in = 0; - _cnt_cb_iso_out = 0; - _cnt_cb_iso_in = 0; - _num_packets_bulk_out_abort = 0; - _num_packets_bulk_in_abort = 0; - _num_packets_int_out_abort = 0; - _num_packets_int_in_abort = 0; - - EndpointResolver resolver(endpoint_table()); - resolver.endpoint_ctrl(64); - ep_config_t *epc = NULL; - for (size_t i = 0; i < NUM_ENDPOINTS; i++) { - epc = &((*_endpoint_configs)[i]); - _endpoints[i] = resolver.next_free_endpoint(epc->dir_in, epc->type, epc->max_packet); - _endpoint_buffs[i] = (uint8_t *) calloc(epc->max_packet, sizeof(uint8_t)); - MBED_ASSERT(_endpoint_buffs[i] != NULL); - } - MBED_ASSERT(resolver.valid()); - configuration_desc(0); - ctrl_buf = new uint8_t[CTRL_BUF_SIZE]; - init(); - USBDevice::connect(); - flags.wait_any(EVENT_READY, osWaitForever, false); -} - -USBEndpointTester::~USBEndpointTester() -{ - for (size_t i = 0; i < NUM_ENDPOINTS; i++) { - if (_endpoint_buffs[i] != NULL) { - free(_endpoint_buffs[i]); - } - } - deinit(); - delete[] ctrl_buf; -} - -const char *USBEndpointTester::get_desc_string(const uint8_t *desc) -{ - static char ret_string[128] = { }; - const uint8_t desc_size = desc[0] - 2; - const uint8_t *desc_str = &desc[2]; - uint32_t j = 0; - for (uint32_t i = 0; i < desc_size; i += 2, j++) { - ret_string[j] = desc_str[i]; - } - ret_string[j] = '\0'; - return ret_string; -} - -const char *USBEndpointTester::get_serial_desc_string() -{ - return get_desc_string(string_iserial_desc()); -} - -void USBEndpointTester::callback_state_change(DeviceState new_state) -{ - if (new_state == Configured) { - flags.set(EVENT_READY); - } else { - flags.clear(EVENT_READY); - } -} - -void USBEndpointTester::callback_request(const setup_packet_t *setup) -{ - /* Called in ISR context */ - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - /* Process vendor-specific requests */ - if (setup->bmRequestType.Type == VENDOR_TYPE) { - switch (setup->bRequest) { - case VENDOR_TEST_CTRL_IN: - result = Send; - data = ctrl_buf; - size = setup->wValue < CTRL_BUF_SIZE ? setup->wValue : CTRL_BUF_SIZE; - break; - case VENDOR_TEST_CTRL_OUT: - result = Receive; - data = ctrl_buf; - size = setup->wValue < 8 ? setup->wValue : 8; - break; - case VENDOR_TEST_CTRL_IN_SIZES: - result = Send; - data = ctrl_buf; - size = setup->wLength; - break; - case VENDOR_TEST_CTRL_OUT_SIZES: - result = Receive; - data = ctrl_buf; - size = setup->wValue; - break; - case VENDOR_TEST_RW_RESTART: - result = (_request_rw_restart(setup)) ? Success : Failure; - break; - case VENDOR_TEST_ABORT_BUFF_CHECK: - result = Send; - ctrl_buf[0] = _request_abort_buff_check(setup); - data = ctrl_buf; - size = 1; - break; - default: - result = PassThrough; - break; - } - } - complete_request(result, data, size); -} - -bool USBEndpointTester::_request_rw_restart(const setup_packet_t *setup) -{ - assert_locked(); - ep_config_t *epc = NULL; - for (size_t i = 0; i < NUM_ENDPOINTS; i++) { - epc = &((*_endpoint_configs)[i]); - endpoint_abort(_endpoints[i]); - if (epc->dir_in == false) { - // Wait for data on every OUT endpoint - read_start(_endpoints[i], _endpoint_buffs[i], epc->max_packet); - } - } - return true; -} - -bool USBEndpointTester::_request_abort_buff_check(const setup_packet_t *setup) -{ - assert_locked(); - if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) { - return false; - } - size_t ep_index = NUM_ENDPOINTS; - for (size_t i = 0; i < NUM_ENDPOINTS; i++) { - if (_endpoints[i] == setup->wIndex) { - ep_index = i; - break; - } - } - if (ep_index == NUM_ENDPOINTS) { - return false; - } - if (_endpoint_buffs[ep_index] == NULL) { - return false; - } - for (size_t i = 0; i < (*_endpoint_configs)[ep_index].max_packet; i++) { - if (_endpoint_buffs[ep_index][i] != EP_ABORT_BUFF_VALUE) { - return false; - } - } - return true; -} - -void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - if (aborted) { - complete_request_xfer_done(false); - return; - } - - bool result = false; - if (setup->bmRequestType.Type == VENDOR_TYPE) { - switch (setup->bRequest) { - case VENDOR_TEST_CTRL_IN: - result = true; - break; - case VENDOR_TEST_CTRL_OUT: - result = true; - break; - case VENDOR_TEST_CTRL_OUT_SIZES: - result = true; - break; - case VENDOR_TEST_CTRL_IN_SIZES: - result = true; - break; - case VENDOR_TEST_ABORT_BUFF_CHECK: - result = true; - break; - default: - result = false; - break; - } - } - complete_request_xfer_done(result); -} - -void USBEndpointTester::callback_set_configuration(uint8_t configuration) -{ - _cnt_cb_set_conf++; - if (configuration != DEFAULT_CONFIGURATION) { - complete_set_configuration(false); - return; - } - - // Configure endpoints > 0 - bool status = _setup_interface(0, 0); - complete_set_configuration(status); -} - -bool USBEndpointTester::_setup_interface(uint16_t interface, uint8_t alternate) -{ - if (interface != 0) { - return false; - } - - switch (alternate) { - case 0: - _endpoint_configs = &_intf_config0; - break; - case 1: - _endpoint_configs = &_intf_config1; - break; - case 2: - _endpoint_configs = &_intf_config2; - break; - case 3: - _endpoint_configs = &_intf_config3; - break; - case 4: - _endpoint_configs = &_intf_config4; - break; - default: - return false; - } - - _setup_non_zero_endpoints(); - - if (_abort_transfer_test && alternate >= 1) { - _num_packets_bulk_out_abort = 0; - _num_packets_bulk_in_abort = 0; - _num_packets_int_out_abort = 0; - _num_packets_int_in_abort = 0; - start_ep_in_abort_test(); - } - return true; -} - -void USBEndpointTester::_setup_non_zero_endpoints() -{ - ep_config_t *epc = NULL; - for (size_t i = 0; i < NUM_ENDPOINTS; i++) { - epc = &((*_endpoint_configs)[i]); - endpoint_add(_endpoints[i], epc->max_packet, epc->type, epc->callback); - if (epc->callback == NULL) { - continue; - } - if (epc->dir_in == false) { - // Wait for data on every OUT endpoint - read_start(_endpoints[i], _endpoint_buffs[i], epc->max_packet); - } - } -} - -void USBEndpointTester::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - _cnt_cb_set_intf++; - if (interface != 0 || alternate > 4) { - complete_set_interface(false); - return; - } - for (size_t i = 0; i < NUM_ENDPOINTS; i++) { - endpoint_abort(_endpoints[i]); - endpoint_remove(_endpoints[i]); - } - bool status = _setup_interface(interface, alternate); - complete_set_interface(status); -} - -#define CONFIG1_DESC_SIZE (CONFIGURATION_DESCRIPTOR_LENGTH \ - + 5 * (INTERFACE_DESCRIPTOR_LENGTH + NUM_ENDPOINTS * ENDPOINT_DESCRIPTOR_LENGTH) ) - -const uint8_t *USBEndpointTester::configuration_desc(uint8_t index) -{ - static const uint8_t config_1_descriptor[] = { - // configuration descriptor - CONFIGURATION_DESCRIPTOR_LENGTH, // bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(CONFIG1_DESC_SIZE), // wTotalLength (LSB) - MSB(CONFIG1_DESC_SIZE), // wTotalLength (MSB) - 1, // bNumInterfaces - 1, // bConfigurationValue - 0, // iConfiguration - 0x80, // bmAttributes - 50, // bMaxPower - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 0, // bAlternateSetting - NUM_ENDPOINTS, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_OUT], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config0[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config0[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_IN], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config0[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config0[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_OUT], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config0[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config0[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_IN], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config0[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config0[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_OUT], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config0[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config0[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_IN], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config0[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config0[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 1, // bAlternateSetting - NUM_ENDPOINTS, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_OUT], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config1[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config1[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_IN], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config1[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config1[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_OUT], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config1[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config1[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_IN], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config1[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config1[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_OUT], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config1[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config1[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_IN], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config1[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config1[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 2, // bAlternateSetting - NUM_ENDPOINTS, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_OUT], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config2[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config2[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_IN], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config2[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config2[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_OUT], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config2[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config2[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_IN], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config2[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config2[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_OUT], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config2[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config2[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_IN], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config2[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config2[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 3, // bAlternateSetting - NUM_ENDPOINTS, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_OUT], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config3[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config3[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_IN], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config3[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config3[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_OUT], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config3[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config3[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_IN], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config3[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config3[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_OUT], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config3[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config3[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_IN], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config3[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config3[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 4, // bAlternateSetting - NUM_ENDPOINTS, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_OUT], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config4[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config4[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_BULK_IN], // bEndpointAddress - E_BULK, // bmAttributes - (uint8_t)(LSB(_intf_config4[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config4[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) - 0, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_OUT], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config4[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config4[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_INT_IN], // bEndpointAddress - E_INTERRUPT, // bmAttributes - (uint8_t)(LSB(_intf_config4[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config4[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_OUT], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config4[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config4[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _endpoints[EP_ISO_IN], // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_intf_config4[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) - (uint8_t)(MSB(_intf_config4[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) - 1, // bInterval - }; - if (index == 0) { - return config_1_descriptor; - } else { - return NULL; - } -} - -void USBEndpointTester::_cb_bulk_out() -{ - _cnt_cb_bulk_out++; - uint32_t rx_size = read_finish(_endpoints[EP_BULK_OUT]); - if (_abort_transfer_test == false) { - // Send data back to host using the IN endpoint. - memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet); - memcpy(_endpoint_buffs[EP_BULK_IN], _endpoint_buffs[EP_BULK_OUT], rx_size); - write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], rx_size); - } else { - // Abort the transfer if enough data was received. - _num_packets_bulk_out_abort++; - if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) { - // Set every byte of the buffer to a known value. - memset(_endpoint_buffs[EP_BULK_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_BULK_OUT].max_packet); - } - read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet); - if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) { - endpoint_abort(_endpoints[EP_BULK_OUT]); - } - } -} - -void USBEndpointTester::_cb_bulk_in() -{ - _cnt_cb_bulk_in++; - write_finish(_endpoints[EP_BULK_IN]); - if (_abort_transfer_test == false) { - // Receive more data from the host using the OUT endpoint. - read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet); - } else { - _num_packets_bulk_in_abort++; - if (_num_packets_bulk_in_abort >= NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) { - return; - } - // Abort the transfer if enough data was sent. - memset(_endpoint_buffs[EP_BULK_IN], _num_packets_bulk_in_abort, (*_endpoint_configs)[EP_BULK_IN].max_packet); - write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet); - if (_num_packets_bulk_in_abort == NUM_PACKETS_UNTIL_ABORT) { - endpoint_abort(_endpoints[EP_BULK_IN]); - // Verify that buffer given in write_start is not used after the - // call to endpoint_abort(), by changing the buffer contents. - // The test will fail if the host receives new buffer content. - memset(_endpoint_buffs[EP_BULK_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_BULK_IN].max_packet); - } - } -} - -void USBEndpointTester::_cb_int_out() -{ - _cnt_cb_int_out++; - uint32_t rx_size = read_finish(_endpoints[EP_INT_OUT]); - if (_abort_transfer_test == false) { - // Send data back to host using the IN endpoint. - memset(_endpoint_buffs[EP_INT_IN], 0, (*_endpoint_configs)[EP_INT_IN].max_packet); - memcpy(_endpoint_buffs[EP_INT_IN], _endpoint_buffs[EP_INT_OUT], rx_size); - write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], rx_size); - } else { - // Abort the transfer if enough data was received. - _num_packets_int_out_abort++; - if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) { - // Set every byte of the buffer to a known value. - memset(_endpoint_buffs[EP_INT_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_INT_OUT].max_packet); - } - read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet); - if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) { - endpoint_abort(_endpoints[EP_INT_OUT]); - } - } -} - -void USBEndpointTester::_cb_int_in() -{ - _cnt_cb_int_in++; - write_finish(_endpoints[EP_INT_IN]); - if (_abort_transfer_test == false) { - // Receive more data from the host using the OUT endpoint. - read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet); - } else { - _num_packets_int_in_abort++; - if (_num_packets_int_in_abort >= NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) { - return; - } - // Abort the transfer if enough data was sent. - memset(_endpoint_buffs[EP_INT_IN], _num_packets_int_in_abort, (*_endpoint_configs)[EP_INT_IN].max_packet); - write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet); - if (_num_packets_int_in_abort == NUM_PACKETS_UNTIL_ABORT) { - endpoint_abort(_endpoints[EP_INT_IN]); - // Verify that buffer given in write_start is not used after the - // call to endpoint_abort(), by changing the buffer contents. - // The test will fail if the host receives new buffer content. - memset(_endpoint_buffs[EP_INT_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_INT_IN].max_packet); - } - } -} - -void USBEndpointTester::_cb_iso_out() -{ - _cnt_cb_iso_out++; - uint32_t rx_size = read_finish(_endpoints[EP_ISO_OUT]); - // Send data back to host using the IN endpoint. - memset(_endpoint_buffs[EP_ISO_IN], 0, (*_endpoint_configs)[EP_ISO_IN].max_packet); - memcpy(_endpoint_buffs[EP_ISO_IN], _endpoint_buffs[EP_ISO_OUT], rx_size); - write_start(_endpoints[EP_ISO_IN], _endpoint_buffs[EP_ISO_IN], rx_size); -} - -void USBEndpointTester::_cb_iso_in() -{ - _cnt_cb_iso_in++; - write_finish(_endpoints[EP_ISO_IN]); - // Receive more data from the host using the OUT endpoint. - read_start(_endpoints[EP_ISO_OUT], _endpoint_buffs[EP_ISO_OUT], (*_endpoint_configs)[EP_ISO_OUT].max_packet); -} - -void USBEndpointTester::start_ep_in_abort_test() -{ - memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet); - memset(_endpoint_buffs[EP_INT_IN], 0, (*_endpoint_configs)[EP_INT_IN].max_packet); - - write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet); - write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet); -} - -#endif //USB_DEVICE_TESTS diff --git a/TESTS/usb_device/basic/USBEndpointTester.h b/TESTS/usb_device/basic/USBEndpointTester.h deleted file mode 100644 index cf649dd..0000000 --- a/TESTS/usb_device/basic/USBEndpointTester.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef USB_ENDPOINT_TESTER_H -#define USB_ENDPOINT_TESTER_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice_Types.h" -#include "events/EventQueue.h" -#include "EventFlags.h" -#include "platform/NonCopyable.h" - -#include "USBDevice.h" - -#define NUM_ENDPOINTS 6 // Not including CTRL OUT/IN - -class USBEndpointTester: public USBDevice, private mbed::NonCopyable { - -public: - USBEndpointTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, - bool abort_transfer_test); - virtual ~USBEndpointTester(); - const char *get_serial_desc_string(); - void start_ep_in_abort_test(); - - uint32_t get_cnt_cb_set_conf() const - { - return _cnt_cb_set_conf; - } - uint32_t get_cnt_cb_set_intf() const - { - return _cnt_cb_set_intf; - } - uint32_t get_cnt_cb_bulk_out() const - { - return _cnt_cb_bulk_out; - } - uint32_t get_cnt_cb_bulk_in() const - { - return _cnt_cb_bulk_in; - } - uint32_t get_cnt_cb_int_out() const - { - return _cnt_cb_int_out; - } - uint32_t get_cnt_cb_int_in() const - { - return _cnt_cb_int_in; - } - uint32_t get_cnt_cb_iso_out() const - { - return _cnt_cb_iso_out; - } - uint32_t get_cnt_cb_iso_in() const - { - return _cnt_cb_iso_in; - } - - struct ep_config_t { - bool dir_in; - usb_ep_type_t type; - uint32_t max_packet; - ep_cb_t callback; - }; - -protected: - rtos::EventFlags flags; - uint8_t *ctrl_buf; - - bool _abort_transfer_test; - usb_ep_t _endpoints[NUM_ENDPOINTS]; - uint8_t *_endpoint_buffs[NUM_ENDPOINTS]; - ep_config_t (*_endpoint_configs)[NUM_ENDPOINTS]; - - static ep_config_t _intf_config_max[NUM_ENDPOINTS]; - static ep_config_t _intf_config0[NUM_ENDPOINTS]; - static ep_config_t _intf_config1[NUM_ENDPOINTS]; - static ep_config_t _intf_config2[NUM_ENDPOINTS]; - static ep_config_t _intf_config3[NUM_ENDPOINTS]; - static ep_config_t _intf_config4[NUM_ENDPOINTS]; - - volatile uint32_t _cnt_cb_set_conf; - volatile uint32_t _cnt_cb_set_intf; - volatile uint32_t _cnt_cb_bulk_out; - volatile uint32_t _cnt_cb_bulk_in; - volatile uint32_t _cnt_cb_int_out; - volatile uint32_t _cnt_cb_int_in; - volatile uint32_t _cnt_cb_iso_out; - volatile uint32_t _cnt_cb_iso_in; - - volatile uint32_t _num_packets_bulk_out_abort; - volatile uint32_t _num_packets_bulk_in_abort; - volatile uint32_t _num_packets_int_out_abort; - volatile uint32_t _num_packets_int_in_abort; - - virtual const uint8_t *configuration_desc(uint8_t index); - virtual void callback_state_change(DeviceState new_state); - virtual void callback_request(const setup_packet_t *setup); - virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); - virtual void callback_set_configuration(uint8_t configuration); - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - - void _setup_non_zero_endpoints(); - bool _setup_interface(uint16_t interface, uint8_t alternate); - - virtual void _cb_bulk_out(); - virtual void _cb_bulk_in(); - virtual void _cb_int_out(); - virtual void _cb_int_in(); - virtual void _cb_iso_out(); - virtual void _cb_iso_in(); - -private: - const char *get_desc_string(const uint8_t *desc); - bool _request_rw_restart(const setup_packet_t *setup); - bool _request_abort_buff_check(const setup_packet_t *setup); -}; - -#endif diff --git a/TESTS/usb_device/basic/USBTester.cpp b/TESTS/usb_device/basic/USBTester.cpp deleted file mode 100644 index 743f217..0000000 --- a/TESTS/usb_device/basic/USBTester.cpp +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if USB_DEVICE_TESTS - -#include "stdint.h" -#include "USBTester.h" -#include "events/mbed_shared_queues.h" -#include "EndpointResolver.h" - -#define DEFAULT_CONFIGURATION (1) -#define LAST_CONFIGURATION (2) - -#define VENDOR_TEST_CTRL_IN 1 -#define VENDOR_TEST_CTRL_OUT 2 -#define VENDOR_TEST_CTRL_IN_SIZES 3 -#define VENDOR_TEST_CTRL_OUT_SIZES 4 - -#define MAX_EP_SIZE 64 -#define MIN_EP_SIZE 8 - -#define CTRL_BUF_SIZE (2048) - -#define EVENT_READY (1 << 0) - -USBTester::USBTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBDevice(phy, vendor_id, product_id, product_release), interface_0_alt_set(NONE), - interface_1_alt_set(NONE), configuration_set(NONE), reset_count(0), - suspend_count(0), resume_count(0) -{ - - EndpointResolver resolver(endpoint_table()); - - resolver.endpoint_ctrl(64); - bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, 64); - bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, 64); - int_in = resolver.endpoint_in(USB_EP_TYPE_INT, 64); - int_out = resolver.endpoint_out(USB_EP_TYPE_INT, 64); - MBED_ASSERT(resolver.valid()); - configuration_desc(0); - ctrl_buf = new uint8_t[CTRL_BUF_SIZE]; - init(); - USBDevice::connect(); - flags.wait_any(EVENT_READY, osWaitForever, false); - -} - -USBTester::~USBTester() -{ - deinit(); - delete[] ctrl_buf; -} - - -const char *USBTester::get_desc_string(const uint8_t *desc) -{ - static char ret_string[128] = { }; - const uint8_t desc_size = desc[0] - 2; - const uint8_t *desc_str = &desc[2]; - uint32_t j = 0; - for (uint32_t i = 0; i < desc_size; i += 2, j++) { - ret_string[j] = desc_str[i]; - } - ret_string[j] = '\0'; - return ret_string; -} - -void USBTester::suspend(bool suspended) -{ - if (suspended) { - ++suspend_count; - } else { - ++resume_count; - } -} - -const char *USBTester::get_serial_desc_string() -{ - return get_desc_string(string_iserial_desc()); -} - -const char *USBTester::get_iinterface_desc_string() -{ - return get_desc_string(string_iserial_desc()); -} - -const char *USBTester::get_iproduct_desc_string() -{ - return get_desc_string(string_iserial_desc()); -} - -void USBTester::callback_state_change(DeviceState new_state) -{ - if (new_state == Configured) { - flags.set(EVENT_READY); - } else { - flags.clear(EVENT_READY); - configuration_set = NONE; - interface_0_alt_set = NONE; - interface_1_alt_set = NONE; - } -} - -void USBTester::callback_reset() -{ - ++reset_count; -} - -void USBTester::callback_request(const setup_packet_t *setup) -{ - /* Called in ISR context */ - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - /* Process vendor-specific requests */ - if (setup->bmRequestType.Type == VENDOR_TYPE) { - switch (setup->bRequest) { - case VENDOR_TEST_CTRL_IN: - result = Send; - data = ctrl_buf; - size = setup->wValue < CTRL_BUF_SIZE ? setup->wValue : CTRL_BUF_SIZE; - break; - case VENDOR_TEST_CTRL_OUT: - result = Receive; - data = ctrl_buf; - size = setup->wValue < 8 ? setup->wValue : 8; - break; - case VENDOR_TEST_CTRL_IN_SIZES: - result = Send; - data = ctrl_buf; - size = setup->wLength; - break; - case VENDOR_TEST_CTRL_OUT_SIZES: - result = Receive; - data = ctrl_buf; - size = setup->wValue; - break; - default: - result = PassThrough; - break; - } - } - - complete_request(result, data, size); -} - -void USBTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - if (aborted) { - complete_request_xfer_done(false); - return; - } - - bool result = false; - if (setup->bmRequestType.Type == VENDOR_TYPE) { - switch (setup->bRequest) { - case VENDOR_TEST_CTRL_IN: - result = true; - break; - case VENDOR_TEST_CTRL_OUT: - result = true; - break; - case VENDOR_TEST_CTRL_OUT_SIZES: - result = true; - break; - case VENDOR_TEST_CTRL_IN_SIZES: - result = true; - break; - default: - result = false; - break; - } - } - complete_request_xfer_done(result); -} - -// Called in ISR context -// Set configuration. Return false if the -// configuration is not supported. -void USBTester::callback_set_configuration(uint8_t configuration) -{ - bool ret = false; - - if (configuration >= DEFAULT_CONFIGURATION && configuration <= LAST_CONFIGURATION) { - endpoint_remove_all(); - ret = set_configuration(configuration); - } - - complete_set_configuration(ret); -} - -bool USBTester::setup_iterface(uint8_t ep_in, uint8_t ep_out, uint32_t ep_size, usb_ep_type_t ep_type, - uint8_t *buf, uint32_t buf_size, void (USBTester::*callback)()) -{ - bool success = false; - - success = endpoint_add(ep_in, ep_size, ep_type); - success &= endpoint_add(ep_out, ep_size, ep_type, callback); - success &= read_start(ep_out, buf, buf_size); - return success; -} - -void USBTester::remove_iterface(uint16_t interface) -{ - if (configuration_set == 1) { - if (interface == 0) { - endpoint_remove(bulk_in); - endpoint_remove(bulk_out); - interface_0_alt_set = NONE; - } - if (interface == 1) { - endpoint_remove(int_in); - endpoint_remove(int_out); - interface_1_alt_set = NONE; - } - } - if (configuration_set == 2) { - if (interface == 0) { - endpoint_remove(int_in); - endpoint_remove(int_out); - interface_0_alt_set = NONE; - } - if (interface == 1) { - endpoint_remove(bulk_in); - endpoint_remove(bulk_out); - interface_1_alt_set = NONE; - } - } -} - -bool USBTester::set_configuration(uint16_t configuration) -{ - bool success = false; - // set 0 alt setting for each interface - if (configuration == 1) { - // interface 0 alternate 0 - success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, - bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); - // interface 1 alternate 0 - success &= setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, - int_buf, sizeof(int_buf), &USBTester::epint_out_callback); - } else if (configuration == 2) { - // interface 0 alternate 0 - success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, - int_buf, sizeof(int_buf), &USBTester::epint_out_callback); - // interface 1 alternate 0 - success &= setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, - bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); - } - if (success) { - configuration_set = configuration; - interface_0_alt_set = interface_1_alt_set = 0; - } - return success; -} - -void USBTester::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - complete_set_interface(set_interface(interface, alternate)); -} - -bool USBTester::set_interface(uint16_t interface, uint16_t alternate) -{ - bool success = false; - - if (interface == 0) { - if (configuration_set == 1) { - if (alternate == 0) { - remove_iterface(interface); - success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, - bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); - } - if (alternate == 1) { - remove_iterface(interface); - success = setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, - bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); - } - } - if (configuration_set == 2) { - if (alternate == 0) { - remove_iterface(interface); - success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, - int_buf, sizeof(int_buf), &USBTester::epint_out_callback); - } - if (alternate == 1) { - remove_iterface(interface); - success = setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, - int_buf, sizeof(int_buf), &USBTester::epint_out_callback); - } - } - if (success) { - interface_0_alt_set = alternate; - } - } - if (interface == 1) { - if (configuration_set == 1) { - if (alternate == 0) { - remove_iterface(interface); - success = setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, - int_buf, sizeof(int_buf), &USBTester::epint_out_callback); - } - if (alternate == 1) { - remove_iterface(interface); - success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, - int_buf, sizeof(int_buf), &USBTester::epint_out_callback); - } - } - if (configuration_set == 2) { - if (alternate == 0) { - remove_iterface(interface); - success = setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, - bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); - } - if (alternate == 1) { - remove_iterface(interface); - success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, - bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); - } - } - if (success) { - interface_1_alt_set = alternate; - } - } - return success; -} - -const uint8_t *USBTester::device_desc() -{ - uint8_t ep0_size = endpoint_max_packet_size(0x00); - uint8_t device_descriptor_temp[] = { - 18, // bLength - 1, // bDescriptorType - 0x10, 0x01, // bcdUSB - 0, // bDeviceClass - 0, // bDeviceSubClass - 0, // bDeviceProtocol - ep0_size, // bMaxPacketSize0 - (uint8_t)(LSB(vendor_id)), (uint8_t)(MSB(vendor_id)), // idVendor - (uint8_t)(LSB(product_id)), (uint8_t)(MSB(product_id)),// idProduct - 0x00, 0x01, // bcdDevice - 1, // iManufacturer - 2, // iProduct - 3, // iSerialNumber - 2 // bNumConfigurations - }; - MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); - memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); - return device_descriptor; -} - -const uint8_t *USBTester::string_iinterface_desc() -{ - static const uint8_t string_iinterface_descriptor[] = { - 0x08, - STRING_DESCRIPTOR, - 'C', 0, 'D', 0, 'C', 0, - }; - return string_iinterface_descriptor; -} - -const uint8_t *USBTester::string_iproduct_desc() -{ - static const uint8_t string_iproduct_descriptor[] = { - 0x22, - STRING_DESCRIPTOR, - 'M', 0, 'B', 0, 'E', 0, 'D', 0, ' ', 0, - 'T', 0, 'E', 0, 'S', 0, 'T', 0, ' ', 0, - 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0, - }; - return string_iproduct_descriptor; -} - - -#define CONFIG_1_DESC_SIZE (9+9+7+7 + 9+7+7 + 9+7+7 + 9+7+7) -#define CONFIG_2_DESC_SIZE (9+9+7+7 + 9+7+7 + 9+7+7 + 9+7+7) - -const uint8_t *USBTester::configuration_desc(uint8_t index) -{ - static const uint8_t config_1_descriptor[] = { - // configuration descriptor - CONFIGURATION_DESCRIPTOR_LENGTH,// bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(CONFIG_1_DESC_SIZE), // wTotalLength - MSB(CONFIG_1_DESC_SIZE), - 2, // bNumInterfaces - 1, // bConfigurationValue - 0, // iConfiguration - 0x80, // bmAttributes - 50, // bMaxPower - - // Interface 0 setting 0 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 0, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_in, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_out, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // Interface 0 setting 1 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 1, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_in, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_out, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // Interface 1 setting 0 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 1, // bInterfaceNumber - 0, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // Interface 1 setting 1 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 1, // bInterfaceNumber - 1, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1 // bInterval - }; - - static const uint8_t config_2_descriptor[] = { - // configuration descriptor - CONFIGURATION_DESCRIPTOR_LENGTH,// bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(CONFIG_2_DESC_SIZE), // wTotalLength - MSB(CONFIG_2_DESC_SIZE), - 2, // bNumInterfaces - 2, // bConfigurationValue - 0, // iConfiguration - 0x80, // bmAttributes - 50, // bMaxPower - - // Interface 0 setting 0 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 0, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // Interface 0 setting 1 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 1, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=interrupt) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // Interface 1 setting 0 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 1, // bInterfaceNumber - 0, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_in, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_out, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // Interface 1 setting 1 - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - INTERFACE_DESCRIPTOR_LENGTH,// bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 1, // bInterfaceNumber - 1, // bAlternateSetting - 2, // bNumEndpoints - 0xFF, // bInterfaceClass - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol - 0, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_in, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - bulk_out, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) - 1 // bInterval - }; - - if (index == 0) { - return config_1_descriptor; - } else if (index == 1) { - return config_2_descriptor; - } else { - return NULL; - } -} - -void USBTester::epint_out_callback() -{ - read_finish(int_out); - read_start(int_out, int_buf, sizeof(int_buf)); -} -void USBTester::epbulk_out_callback() -{ - read_finish(bulk_out); - read_start(bulk_out, bulk_buf, sizeof(bulk_buf)); -} - -#endif //USB_DEVICE_TESTS diff --git a/TESTS/usb_device/basic/USBTester.h b/TESTS/usb_device/basic/USBTester.h deleted file mode 100644 index d131ab3..0000000 --- a/TESTS/usb_device/basic/USBTester.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef USB_TESTER_H -#define USB_TESTER_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice_Types.h" -#include "events/EventQueue.h" -#include "EventFlags.h" -#include "platform/NonCopyable.h" - -#include "USBDevice.h" - -class USBTester: public USBDevice, private mbed::NonCopyable { -public: - - /* - * Constructor - * - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); - - virtual ~USBTester(); - - /* - * - * @returns descriptor string in ASCII - */ - const char *get_serial_desc_string(); - const char *get_iproduct_desc_string(); - const char *get_iinterface_desc_string(); - uint32_t get_reset_count() const - { - return reset_count; - } - uint32_t get_suspend_count() const - { - return suspend_count; - } - uint32_t get_resume_count() const - { - return resume_count; - } - void clear_reset_count() - { - reset_count = 0; - } - void clear_suspend_count() - { - suspend_count = 0; - } - void clear_resume_count() - { - resume_count = 0; - } - -private: - - enum { NONE = -1 }; - const char *get_desc_string(const uint8_t *desc); - virtual void suspend(bool suspended); - bool set_configuration(uint16_t configuration); - bool set_interface(uint16_t interface, uint16_t alternate); - bool setup_iterface(uint8_t ep_in, uint8_t ep_out, uint32_t ep_size, usb_ep_type_t ep_type, - uint8_t *buf, uint32_t buf_size, void (USBTester::*callback)()); - void remove_iterface(uint16_t interface); - int16_t interface_0_alt_set; - int16_t interface_1_alt_set; - int16_t configuration_set; - -protected: - - /* - * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. - * - * @returns pointer to the device descriptor - */ - virtual const uint8_t *device_desc(); - - /* - * Get string product descriptor - * - * @returns pointer to the string product descriptor - */ - virtual const uint8_t *string_iproduct_desc(); - - /* - * Get string interface descriptor - * - * @returns pointer to the string interface descriptor - */ - virtual const uint8_t *string_iinterface_desc(); - - /* - * Get configuration descriptor - * - * @param index descriptor index - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index); - -protected: - uint8_t bulk_in; - uint8_t bulk_out; - uint8_t bulk_buf[64]; - uint8_t int_in; - uint8_t int_out; - uint8_t int_buf[64]; - rtos::EventFlags flags; - volatile uint32_t reset_count; - volatile uint32_t suspend_count; - volatile uint32_t resume_count; - - virtual void callback_state_change(DeviceState new_state); - virtual void callback_request(const setup_packet_t *setup); - virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); - virtual void callback_set_configuration(uint8_t configuration); - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - virtual void epbulk_out_callback(); - virtual void epint_out_callback(); - virtual void callback_reset(); - uint8_t *ctrl_buf; - -}; - -#endif diff --git a/TESTS/usb_device/basic/main.cpp b/TESTS/usb_device/basic/main.cpp deleted file mode 100644 index 2667af2..0000000 --- a/TESTS/usb_device/basic/main.cpp +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - -#include -#include -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity/unity.h" -#include "utest/utest.h" - -#include "USBTester.h" -#include "USBEndpointTester.h" -#include "usb_phy_api.h" - -// TODO -// suspend resume test: implement host side USB suspend/resume -// sync frame test: add test on isochronous endpoint - -// Uncomment/remove this when host suspend_resume_test part will be implemented -//#define SUSPEND_RESUME_TEST_SUPPORTED - -// If disconnect() + connect() occur too fast the reset event will be dropped. -// At a minimum there should be a 200us delay between disconnect and connect. -// To be on the safe side I would recommend a 1ms delay, so the host controller -// has an entire USB frame to detect the disconnect. -#define MIN_DISCONNECT_TIME_US 1000 - -#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#error [NOT_SUPPORTED] USB Device not supported for this target -#else - - -using namespace utest::v1; - -// Print callback counters for endpoint tests -#define EP_DBG 0 - -static USBPhy *get_phy() -{ - return get_usb_phy(); -} - - -/** Control basic tests - - Test device configuration/deconfiguration - Given an initialized USB (HOST <---> DUT connection established) - When device configuration is checked just after initialization - Then get_configuration returns 1 (default configuration is set) - When device is deconfigured - Then get_configuration returns 0 (no configuration is set) - When each from supported configurations is set - Then the configuration is set correctly - - Test device interface setting - Given an initialized USB (HOST <---> DUT connection established) - When each altsetting from every supported configuration is set - Then the interface altsetting is set correctly - - Test device/interface/endpoint status - Given an initialized USB (HOST <---> DUT connection established) - When device status is checked - Then status is within allowed values (see status bits description below) - When control endpoint status is checked - Then control endpoint status is 0 - When status of each interface from every supported configuration is checked - Then interface status is 0 - When status of each endpoint in every allowed device interface/configuration combination is checked - Then endpoint status is 0 (not halted) - - Test set/clear feature on device/interface/endpoint - Given an initialized USB (HOST <---> DUT connection established) - When for each endpoint in every allowed interface/configuration combination the feature is set and then cleared - Then selected feature is set/cleared accordingly - - Test device/configuration/interface/endpoint descriptors - Given an initialized USB (HOST <---> DUT connection established) - When device descriptor is read - Then the descriptor content is valid - When configuration descriptor is read - Then the descriptor content is valid - When interface descriptor is read - Then the error is thrown since it is not directly accessible - When endpoint descriptor is read - Then the error is thrown since it is not directly accessible - - Test descriptor setting - Given an initialized USB (HOST <---> DUT connection established) - When device descriptor is to be set - Then error is thrown since descriptor setting command is not supported by Mbed -*/ -void control_basic_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - char str[128] = {}; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - sprintf(str, "%s %d %d", serial.get_serial_desc_string(), vendor_id, product_id); - greentea_send_kv("control_basic_test", str); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - - -/** Test control endpoint stall on invalid request - - Given an initialized USB (HOST <---> DUT connection established) - When unsupported request to control endpoint is to be sent - Then the endpoint is stalled and error is thrown - -*/ -void control_stall_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - greentea_send_kv("control_stall_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - - -/** Test various data sizes in control transfer - - Given an initialized USB (HOST <---> DUT connection established) - When control data in each tested size is sent - Then read data should match sent data - -*/ -void control_sizes_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - greentea_send_kv("control_sizes_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - - -/** Test various patterns of control transfers - - Given an initialized USB (HOST <---> DUT connection established) - When stress control transfer with a data in stage is performed - Then transfer ends with success - When stress control transfer with a data out stage followed by a control transfer with a data in stage is performed - Then transfer ends with success - When stress control transfer with a data out stage is performed - Then transfer ends with success - -*/ -void control_stress_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - greentea_send_kv("control_stress_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - -/** Test data correctness for every OUT/IN endpoint pair - * - * Given a USB device with multiple OUT/IN endpoint pairs - * When the host sends random payloads up to wMaxPacketSize in size - * to an OUT endpoint of the device, - * and then the device sends data back to host using an IN endpoint - * Then data sent and received by host is equal for every endpoint pair - */ -void ep_test_data_correctness() -{ - 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_correctness", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); -#if EP_DBG - ThisThread::sleep_for(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 endpoint halt for every OUT/IN endpoint pair - * - * Given a USB device with multiple OUT/IN endpoint pairs - * When the host issues an endpoint halt control request at a random point - * of OUT or IN transfer - * Then the endpoint is stalled and all further transfers fail - */ -void ep_test_halt() -{ - 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_halt", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); -#if EP_DBG - ThisThread::sleep_for(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 simultaneous data transfers for multiple OUT/IN endpoint pairs - * - * Given a USB device with multiple OUT/IN endpoint pairs - * When multiple OUT and IN endpoints are used to transfer random data in parallel - * Then all transfers succeed - * and for every endpoint pair, data received by host equals data sent by host - */ -void ep_test_parallel_transfers() -{ - 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_parallel_transfers", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); -#if EP_DBG - ThisThread::sleep_for(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 simultaneous data transfers in parallel with control transfers - * - * Given a USB device with multiple OUT/IN endpoint pairs - * When multiple OUT and IN endpoints are used to transfer random data - * and control requests are processed in parallel - * Then all transfers succeed - * and for every endpoint pair, data received by host equals data sent by host - */ -void ep_test_parallel_transfers_ctrl() -{ - 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_parallel_transfers_ctrl", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); -#if EP_DBG - ThisThread::sleep_for(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 aborting data transfer for every OUT/IN endpoint pair - * - * Given a USB device with multiple OUT/IN endpoint pairs - * When a device aborts an in progress data transfer - * Then no more data is transmitted - * and endpoint buffer is correctly released on the device end - */ -void ep_test_abort() -{ - 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, true); - greentea_send_kv("ep_test_abort", serial.get_serial_desc_string()); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); -#if EP_DBG - ThisThread::sleep_for(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 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 - ThisThread::sleep_for(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) - When USB device is reset repeatedly - Then the USB is operational with no errors - -*/ -void device_reset_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - - greentea_send_kv("reset_support", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - if (strcmp(_value, "false") != 0) { - - USBTester serial(get_phy(), vendor_id, product_id, product_release); - serial.clear_reset_count(); - greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); - while (serial.get_reset_count() == 0); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - - while (!serial.configured()); - - serial.clear_reset_count(); - greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); - while (serial.get_reset_count() == 0); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - - while (!serial.configured()); - - serial.clear_reset_count(); - greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); - while (serial.get_reset_count() == 0); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - - while (!serial.configured()); - - greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - - -/** Test USB implementation against repeated reconnection - - Given an initialized USB (HOST <---> DUT connection established) - When USB device is disconnected and then connected repeatedly - Then the USB is operational with no errors - -*/ -void device_soft_reconnection_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - const uint32_t reconnect_try_count = 3; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - - greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - - for (int i = 0; i < reconnect_try_count; i++) { - serial.disconnect(); - wait_us(MIN_DISCONNECT_TIME_US); - serial.connect(); - greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } - - serial.disconnect(); - wait_us(MIN_DISCONNECT_TIME_US); - serial.connect(); - serial.disconnect(); - wait_us(MIN_DISCONNECT_TIME_US); - serial.connect(); - serial.disconnect(); - wait_us(MIN_DISCONNECT_TIME_US); - serial.connect(); - greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - - -#if SUSPEND_RESUME_TEST_SUPPORTED -/** Test USB implementation against repeated suspend and resume - - Given an initialized USB (HOST <---> DUT connection established) - When USB device is suspended and then resumed repeatedly - Then the USB is operational with no errors - -*/ -void device_suspend_resume_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - greentea_send_kv("device_suspend_resume_test", serial.get_serial_desc_string()); - printf("[1] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count()); - serial.clear_suspend_count(); - serial.clear_resume_count(); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - printf("[2] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count()); - TEST_ASSERT_EQUAL_STRING("pass", _key); - ThisThread::sleep_for(5000); - printf("[3] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count()); - } -} -#endif - - -/** Test USB implementation against repeated initialization and deinitialization - - Given an initialized USB (HOST <---> DUT connection established) - When USB device is deinitialized and then initialized repeatedly - Then the USB is operational with no errors - -*/ -void repeated_construction_destruction_test() -{ - uint16_t vendor_id = 0x0d28; - uint16_t product_id = 0x0205; - uint16_t product_release = 0x0001; - char _key[11] = {}; - char _value[128] = {}; - - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - TEST_ASSERT_EQUAL(true, serial.configured()); - } - - wait_us(MIN_DISCONNECT_TIME_US); - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - TEST_ASSERT_EQUAL(true, serial.configured()); - } - - wait_us(MIN_DISCONNECT_TIME_US); - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - TEST_ASSERT_EQUAL(true, serial.configured()); - } - - wait_us(MIN_DISCONNECT_TIME_US); - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - TEST_ASSERT_EQUAL(true, serial.configured()); - greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } - - wait_us(MIN_DISCONNECT_TIME_US); - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - TEST_ASSERT_EQUAL(true, serial.configured()); - greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } - - wait_us(MIN_DISCONNECT_TIME_US); - { - USBTester serial(get_phy(), vendor_id, product_id, product_release); - TEST_ASSERT_EQUAL(true, serial.configured()); - greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string()); - // Wait for host before terminating - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("pass", _key); - } -} - -Case cases[] = { - Case("usb control basic test", control_basic_test), - Case("usb control stall test", control_stall_test), - Case("usb control sizes test", control_sizes_test), - Case("usb control stress test", control_stress_test), - Case("usb device reset test", device_reset_test), - Case("usb soft reconnection test", device_soft_reconnection_test), -#if SUSPEND_RESUME_TEST_SUPPORTED - Case("usb device suspend/resume test", device_suspend_resume_test), -#endif - Case("usb repeated construction destruction test", repeated_construction_destruction_test), - Case("endpoint test data correctness", ep_test_data_correctness), - 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 data toggle reset", ep_test_data_toggle) -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(180, "pyusb_basic"); - return greentea_test_setup_handler(number_of_cases); -} - -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); - -int main() -{ - Harness::run(specification); -} - -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(USB_DEVICE_TESTS) diff --git a/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg b/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg deleted file mode 100644 index 71e084a..0000000 --- a/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Zadig device configuration -# Mbed OS USB test device -- basic tests -[device] -Description = "MBED TEST DEVICE" -VID = 0x0D28 -PID = 0x0205 diff --git a/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg b/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg deleted file mode 100644 index 80caf34..0000000 --- a/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Zadig device configuration -# Mbed OS USB test device -- endpoint tests -[device] -Description = "USB DEVICE" -VID = 0x0D28 -PID = 0x0206 diff --git a/TESTS/usb_device/hid/main.cpp b/TESTS/usb_device/hid/main.cpp deleted file mode 100644 index 551bac2..0000000 --- a/TESTS/usb_device/hid/main.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - -#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#error [NOT_SUPPORTED] USB Device not supported for this target -#else - -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" -#include "mbed.h" -#include -#include "usb_phy_api.h" -#include "USBHID.h" -#include "USBMouse.h" -#include "USBKeyboard.h" -#include "hal/us_ticker_api.h" - -// Reuse the VID & PID from basic USB test. -#define USB_HID_VID 0x0d28 -#define USB_HID_PID_GENERIC 0x0206 -#define USB_HID_PID_KEYBOARD 0x0206 -#define USB_HID_PID_MOUSE 0x0206 -#define USB_HID_PID_GENERIC2 0x0007 - -#define MSG_VALUE_LEN 24 -#define MSG_KEY_LEN 24 -#define MSG_KEY_DEVICE_READY "dev_ready" -#define MSG_KEY_HOST_READY "host_ready" -#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn" -#define MSG_KEY_TEST_GET_DESCRIPTOR_HID "test_get_desc_hid" -#define MSG_KEY_TEST_GET_DESCRIPTOR_CFG "test_get_desc_cfg" -#define MSG_KEY_TEST_REQUESTS "test_requests" -#define MSG_KEY_TEST_RAW_IO "test_raw_io" - -#define MSG_KEY_TEST_CASE_FAILED "fail" -#define MSG_KEY_TEST_CASE_PASSED "pass" -#define MSG_VALUE_DUMMY "0" -#define MSG_VALUE_NOT_SUPPORTED "not_supported" - -#define RAW_IO_REPS 16 - -#define USB_DEV_SN_LEN (32) // 32 hex digit UUID -#define NONASCII_CHAR ('?') -#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2) - -const char *default_serial_num = "0123456789"; -char usb_dev_sn[USB_DEV_SN_LEN + 1]; - -using utest::v1::Case; -using utest::v1::Specification; -using utest::v1::Harness; - -/** - * Convert a USB string descriptor to C style ASCII - * - * The string placed in str is always null-terminated which may cause the - * loss of data if n is to small. If the length of descriptor string is less - * than n, additional null bytes are written to str. - * - * @param str output buffer for the ASCII string - * @param usb_desc USB string descriptor - * @param n size of str buffer - * @returns number of non-null bytes returned in str or -1 on failure - */ -int usb_string_desc2ascii(char *str, const uint8_t *usb_desc, size_t n) -{ - if (str == NULL || usb_desc == NULL || n < 1) { - return -1; - } - // bDescriptorType @ offset 1 - if (usb_desc[1] != STRING_DESCRIPTOR) { - return -1; - } - // bLength @ offset 0 - const size_t bLength = usb_desc[0]; - if (bLength % 2 != 0) { - return -1; - } - size_t s, d; - for (s = 0, d = 2; s < n - 1 && d < bLength; s++, d += 2) { - // handle non-ASCII characters - if (usb_desc[d] > 0x7f || usb_desc[d + 1] != 0) { - str[s] = NONASCII_CHAR; - } else { - str[s] = usb_desc[d]; - } - } - int str_len = s; - for (; s < n; s++) { - str[s] = '\0'; - } - return str_len; -} - -/** - * Convert a C style ASCII to a USB string descriptor - * - * @param usb_desc output buffer for the USB string descriptor - * @param str ASCII string - * @param n size of usb_desc buffer, even number - * @returns number of bytes returned in usb_desc or -1 on failure - */ -int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n) -{ - if (str == NULL || usb_desc == NULL || n < 4) { - return -1; - } - if (n % 2 != 0) { - return -1; - } - size_t s, d; - // set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string - memset(usb_desc, 0, n); - for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) { - usb_desc[d] = str[s]; - } - // set bLength @ offset 0 - usb_desc[0] = d; - // set bDescriptorType @ offset 1 - usb_desc[1] = STRING_DESCRIPTOR; - return d; -} - -class TestUSBHID: public USBHID { -private: - uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; -public: - TestUSBHID(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num, uint8_t output_report_length = 64, uint8_t input_report_length = 64) : - USBHID(get_usb_phy(), output_report_length, input_report_length, vendor_id, product_id, 0x01) - { - init(); - int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); - if (rc < 0) { - ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); - } - } - - virtual ~TestUSBHID() - { - deinit(); - } - - virtual const uint8_t *string_iserial_desc() - { - return (const uint8_t *) _serial_num_descriptor; - } - - // Make this accessible for tests (public). - using USBHID::report_desc_length; -}; - -class TestUSBMouse: public USBMouse { -private: - uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; -public: - TestUSBMouse(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) : - USBMouse(get_usb_phy(), REL_MOUSE, vendor_id, product_id, 0x01) - { - init(); - int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); - if (rc < 0) { - ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); - } - } - - virtual ~TestUSBMouse() - { - deinit(); - } - - virtual const uint8_t *string_iserial_desc() - { - return (const uint8_t *) _serial_num_descriptor; - } - - // Make this accessible for tests (public). - using USBHID::report_desc_length; -}; - -class TestUSBKeyboard: public USBKeyboard { -private: - uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; -public: - TestUSBKeyboard(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) : - USBKeyboard(get_usb_phy(), vendor_id, product_id, 0x01) - { - init(); - int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); - if (rc < 0) { - ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); - } - } - - virtual ~TestUSBKeyboard() - { - deinit(); - } - - virtual const uint8_t *string_iserial_desc() - { - return (const uint8_t *) _serial_num_descriptor; - } - - // Make this accessible for tests (public). - using USBHID::report_desc_length; -}; - -/** Test Get_Descriptor request with the HID class descriptors - * - * Given a USB HID class device connected to a host, - * when the host issues the Get_Descriptor(HID) request, - * then the device returns the HID descriptor. - * - * When the host issues the Get_Descriptor(Report) request, - * then the device returns the Report descriptor - * and the size of the descriptor is equal to USBHID::report_desc_length(). - * - * Details in USB Device Class Definition for HID, v1.11, paragraph 7.1. - */ -template -void test_get_hid_class_desc() -{ - T usb_hid(USB_HID_VID, PID, usb_dev_sn); - usb_hid.connect(); - greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_HID, MSG_VALUE_DUMMY); - - char key[MSG_KEY_LEN + 1] = { }; - char value[MSG_VALUE_LEN + 1] = { }; - greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); - TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key); - uint16_t host_report_desc_len; - int num_args = sscanf(value, "%04hx", &host_report_desc_len); - TEST_ASSERT_MESSAGE(num_args != 0 && num_args != EOF, "Invalid data received from host."); - TEST_ASSERT_EQUAL_UINT16(usb_hid.report_desc_length(), host_report_desc_len); -} - -/** Test Get_Descriptor request with the Configuration descriptor - * - * Given a USB HID class device connected to a host, - * when the host issues the Get_Descriptor(Configuration) request, - * then the device returns the Configuration descriptor and a HID - * descriptor for each HID interface. - * - * Details in USB Device Class Definition for HID, v1.11, paragraph 7.1. - */ -template -void test_get_configuration_desc() -{ - T usb_hid(USB_HID_VID, PID, usb_dev_sn); - usb_hid.connect(); - greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_CFG, MSG_VALUE_DUMMY); - - char key[MSG_KEY_LEN + 1] = { }; - char value[MSG_VALUE_LEN + 1] = { }; - greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); - TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key); -} - -/** Test HID class requests - * - * Given a USB HID class device connected to a host, - * when the host issues a request specific to the HID class device type, - * then the device returns valid data. - * - * Details in USB Device Class Definition for HID, v1.11, - * paragraph 7.2 and Appendix G. - */ -template -void test_class_requests() -{ - T usb_hid(USB_HID_VID, PID, usb_dev_sn); - usb_hid.connect(); - greentea_send_kv(MSG_KEY_TEST_REQUESTS, MSG_VALUE_DUMMY); - - char key[MSG_KEY_LEN + 1] = { }; - char value[MSG_VALUE_LEN + 1] = { }; - greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); - TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key); -} - -/** Test send & read - * - * Given a USB HID class device connected to a host, - * when the device sends input reports with a random data to the host - * and the host sends them back to the device, - * then received output report data is equal to the input report data. - */ -template // Range [1, MAX_HID_REPORT_SIZE]. -void test_generic_raw_io() -{ - TestUSBHID usb_hid(USB_HID_VID, USB_HID_PID_GENERIC2, usb_dev_sn, REPORT_SIZE, REPORT_SIZE); - usb_hid.connect(); - greentea_send_kv(MSG_KEY_TEST_RAW_IO, REPORT_SIZE); - - // Wait for the host HID driver to complete setup. - char key[MSG_KEY_LEN + 1] = { }; - char value[MSG_VALUE_LEN + 1] = { }; - greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); - TEST_ASSERT_EQUAL_STRING(MSG_KEY_HOST_READY, key); - if (strcmp(value, MSG_VALUE_NOT_SUPPORTED) == 0) { - TEST_IGNORE_MESSAGE("Test case not supported by host plarform."); - return; - } - - // Report ID omitted here. There are no Report ID tags in the Report descriptor. - HID_REPORT input_report = {}; - HID_REPORT output_report = {}; - for (size_t r = 0; r < RAW_IO_REPS; r++) { - for (size_t i = 0; i < REPORT_SIZE; i++) { - input_report.data[i] = (uint8_t)(rand() % 0x100); - } - input_report.length = REPORT_SIZE; - output_report.length = 0; - TEST_ASSERT(usb_hid.send(&input_report)); - TEST_ASSERT(usb_hid.read(&output_report)); - TEST_ASSERT_EQUAL_UINT32(input_report.length, output_report.length); - TEST_ASSERT_EQUAL_UINT8_ARRAY(input_report.data, output_report.data, REPORT_SIZE); - } -} - -utest::v1::status_t testsuite_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(45, "usb_device_hid"); - srand((unsigned) ticker_read_us(get_us_ticker_data())); - - utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases); - if (status != utest::v1::STATUS_CONTINUE) { - return status; - } - - char key[MSG_KEY_LEN + 1] = { }; - char usb_dev_uuid[USB_DEV_SN_LEN + 1] = { }; - - greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); - greentea_parse_kv(key, usb_dev_uuid, MSG_KEY_LEN, USB_DEV_SN_LEN + 1); - - if (strcmp(key, MSG_KEY_SERIAL_NUMBER) != 0) { - utest_printf("Invalid message key.\n"); - return utest::v1::STATUS_ABORT; - } - - strncpy(usb_dev_sn, usb_dev_uuid, USB_DEV_SN_LEN + 1); - return status; -} - -Case cases[] = { - Case("Configuration descriptor, generic", test_get_configuration_desc), - Case("Configuration descriptor, keyboard", test_get_configuration_desc), - Case("Configuration descriptor, mouse", test_get_configuration_desc), - - Case("HID class descriptors, generic", test_get_hid_class_desc), - Case("HID class descriptors, keyboard", test_get_hid_class_desc), - Case("HID class descriptors, mouse", test_get_hid_class_desc), - - // HID class requests not supported by Mbed - // Case("HID class requests, generic", test_class_requests), - // Case("HID class requests, keyboard", test_class_requests), - // Case("HID class requests, mouse", test_class_requests), - - Case("Raw input/output, 1-byte reports", test_generic_raw_io<1>), - Case("Raw input/output, 20-byte reports", test_generic_raw_io<20>), - Case("Raw input/output, 64-byte reports", test_generic_raw_io<64>), -}; - -Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(USB_DEVICE_TESTS) diff --git a/TESTS/usb_device/hid/requirements.txt b/TESTS/usb_device/hid/requirements.txt deleted file mode 100644 index f9453c9..0000000 --- a/TESTS/usb_device/hid/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -hidapi>=0.7.99,<0.8.0 diff --git a/TESTS/usb_device/msd/TestUSBMSD.h b/TESTS/usb_device/msd/TestUSBMSD.h deleted file mode 100644 index 0c40042..0000000 --- a/TESTS/usb_device/msd/TestUSBMSD.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef Test_USBMSD_H -#define Test_USBMSD_H - -#include "USBMSD.h" - - -#define USB_DEV_SN_LEN (32) // 32 hex digit UUID -#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2) - -/** - * Convert a C style ASCII to a USB string descriptor - * - * @param usb_desc output buffer for the USB string descriptor - * @param str ASCII string - * @param n size of usb_desc buffer, even number - * @returns number of bytes returned in usb_desc or -1 on failure - */ -int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n) -{ - if (str == NULL || usb_desc == NULL || n < 4) { - return -1; - } - if (n % 2 != 0) { - return -1; - } - size_t s, d; - // set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string - memset(usb_desc, 0, n); - for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) { - usb_desc[d] = str[s]; - } - // set bLength @ offset 0 - usb_desc[0] = d; - // set bDescriptorType @ offset 1 - usb_desc[1] = STRING_DESCRIPTOR; - return d; -} - -class TestUSBMSD: public USBMSD { -public: - TestUSBMSD(BlockDevice *bd, bool connect_blocking = true, uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, - uint16_t product_release = 0x0001) - : USBMSD(bd, connect_blocking, vendor_id, product_id, product_release) - { - - } - - virtual ~TestUSBMSD() - { - - } - - uint32_t get_read_counter() - { - return read_counter; - } - - uint32_t get_program_counter() - { - return program_counter; - } - - void reset_counters() - { - read_counter = program_counter = erase_counter = 0; - } - - static void setup_serial_number() - { - char _key[128] = { 0 }; - char _value[128] = { 0 }; - - greentea_send_kv("get_serial_number", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("serial_number", _key); - usb_dev_sn[USB_DEV_SN_LEN] = '\0'; - memcpy(usb_dev_sn, _value, USB_DEV_SN_LEN); - ascii2usb_string_desc(_serial_num_descriptor, usb_dev_sn, USB_DEV_SN_DESC_SIZE); - } - - virtual const uint8_t *string_iserial_desc() - { - return (const uint8_t *)_serial_num_descriptor; - } - - static volatile uint32_t read_counter; - static volatile uint32_t program_counter; - static volatile uint32_t erase_counter; - -protected: - virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count) - { - read_counter++; - return USBMSD::disk_read(data, block, count); - } - - virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count) - { - erase_counter++; - program_counter++; - - return USBMSD::disk_write(data, block, count); - } -private: - static uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; - static char usb_dev_sn[USB_DEV_SN_LEN + 1]; -}; - -uint8_t TestUSBMSD::_serial_num_descriptor[USB_DEV_SN_DESC_SIZE] = { 0 }; -char TestUSBMSD::usb_dev_sn[USB_DEV_SN_LEN + 1] = { 0 }; - - -volatile uint32_t TestUSBMSD::read_counter = 0; -volatile uint32_t TestUSBMSD::program_counter = 0; -volatile uint32_t TestUSBMSD::erase_counter = 0; - -#endif // Test_USBMSD_H diff --git a/TESTS/usb_device/msd/main.cpp b/TESTS/usb_device/msd/main.cpp deleted file mode 100644 index a6e03f0..0000000 --- a/TESTS/usb_device/msd/main.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) 2019-2020, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - -#if !defined(MBED_CONF_RTOS_PRESENT) -#error [NOT_SUPPORTED] USB stack and test cases require RTOS to run. -#else - -#include -#include -#include /* srand, rand */ -#include "greentea-client/test_env.h" -#include "unity/unity.h" -#include "utest/utest.h" - -#include "mbed.h" -#include "USBMSD.h" -#include "TestUSBMSD.h" - -#include "HeapBlockDevice.h" -#include "FATFileSystem.h" - -// TARGET_NANO100 SRAM 16KB can't afford mass-storage-disk test, so skip usb_msd_test. -#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE || TARGET_NANO100 -#error [NOT_SUPPORTED] USB Device not supported for this target -#else - - -#define OS_WINDOWS 1 -#define OS_LINUX 2 -#define OS_MAC 3 - -// Host side unmount was disabled for windows machines. -// PowerShell execution policies/restrictions cause that -// on some windows machines unmount is failing -// To re-enable it comment out below line. -#define DISABLE_HOST_SIDE_UMOUNT - -#ifdef MIN -#undef MIN -#endif -#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) - -#define DEFAULT_BLOCK_SIZE 512 -#define HEAP_BLOCK_DEVICE_SIZE (128 * DEFAULT_BLOCK_SIZE) -#define MIN_HEAP_SIZE (HEAP_BLOCK_DEVICE_SIZE + 6144) - - -/* TODO: - * - * Test if slave(DUT) can force host to refresh mounted fs. (not supported in USBMSD yet) - * - */ - -#define TEST_DIR "usb_msd_test_data" -#define TEST_FILE "usb_msd_test_file" -#define TEST_STRING "usb_msd_test_string" - -using namespace utest::v1; - -uint32_t prev_read_counter = 0; -uint32_t prev_program_counter = 0; -extern uint32_t mbed_heap_size; - -static char _key[256] = { 0 }; -static char _value[128] = { 0 }; - -static volatile bool msd_process_done = false; - - -FATFileSystem heap_fs("heap_fs"); - -Semaphore media_remove_event(0, 1); - - -/** Creates heap block device - * - */ -BlockDevice *get_heap_block_device() -{ - // create 64kB heap block device - if (mbed_heap_size >= MIN_HEAP_SIZE) { - static HeapBlockDevice bd(128 * DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE); - bd.init(); - return &bd; - } else { - return NULL; - } -} - -uint64_t get_fs_mount_size(FileSystem *fs) -{ - struct statvfs stat; - fs->statvfs(fs->getName(), &stat); - uint64_t size = stat.f_bsize * stat.f_blocks; - return size; -} - -/** - * Create test data - * - * @param fs_root filesystem path - */ -static bool test_files_create(const char *fs_root, const char *test_file = TEST_FILE, const char *test_string = TEST_STRING) -{ - char path[128]; - sprintf(path, "/%s/%s", fs_root, TEST_DIR); - int ret = mkdir(path, 0777); - if (ret != 0 && errno != EEXIST) { - utest_printf("mkdir failed!!! errno: %d\n", errno); - return false; - } - sprintf(path, "/%s/%s/%s", fs_root, TEST_DIR, test_file); - FILE *f = fopen(path, "w"); - if (f == NULL) { - utest_printf("fopen failed!!! errno: %d\n", errno); - return false; - } - fprintf(f, test_string); - fflush(f); - fclose(f); - return true; -} - -/** - * Remove test data - * - * @param fs_root filesystem path - */ -static void test_files_remove(const char *fs_root) -{ - DIR *dir; - struct dirent *dp; - char path[512]; - - sprintf(path, "/%s/%s", fs_root, TEST_DIR); - dir = opendir(path); - if (dir == NULL) { - return; - } - while ((dp = readdir(dir)) != NULL) { - sprintf(path, "/%s/%s/%s", fs_root, TEST_DIR, dp->d_name); - remove(path); - } - sprintf(path, "/%s/%s", fs_root, TEST_DIR); - remove(path); -} - -/** - * Check if test data exist - * - * @param fs_root filesystem path - * @return true if data exist - */ -static bool test_files_exist(const char *fs_root, const char *test_file = TEST_FILE, const char *test_string = TEST_STRING) -{ - char path[128]; - char str[512] = { 0 }; - sprintf(path, "/%s/%s/%s", fs_root, TEST_DIR, test_file); - FILE *f = fopen(path, "r"); - if (f != NULL) { - fscanf(f, "%s", str); - if (strcmp(test_string, str) == 0) { - return true; - } - } - return false; -} - -/** - * Mounts a filesystem to a block device - * - * @param bd block device - * @param fs filesystem - * @return true if success, false otherwise - */ -static bool prepare_storage(BlockDevice *bd, FileSystem *fs) -{ - const char *fs_root = fs->getName(); - int err = fs->mount(bd); - if (err) { - utest_printf("%s filesystem mount failed\ntry to reformat device... ", fs->getName()); - err = fs->reformat(bd); - if (err) { - utest_printf("failed !!!\n"); - return false; - } else { - utest_printf("succeed\n"); - } - } - // remove old test data - test_files_remove(fs_root); - - return true; -} - -void run_processing(Semaphore *sem) -{ - sem->release(); -} - -void msd_process(USBMSD *msd) -{ - Semaphore proc; - msd->attach(callback(run_processing, &proc)); - while (!msd_process_done) { - proc.try_acquire_for(100); - msd->process(); - if (msd->media_removed()) { - media_remove_event.release(); - } - } - msd->attach(NULL); -} - - -// wait until msd negotiation is done (no r/w disk operation for at least 1s) -// max wait time is 15s -#define WAIT_MSD_COMMUNICATION_DONE() \ - for (int x = 0; x < 15; x++) { \ - prev_read_counter = usb.get_read_counter();\ - prev_program_counter = usb.get_program_counter();\ - ThisThread::sleep_for(1000);\ - if ((usb.get_read_counter() == prev_read_counter) && \ - (usb.get_program_counter() == prev_program_counter)) {\ - break;\ - }\ - } - - -#define TEST_ASSERT_EQUAL_STRING_LOOP(expected, actual, loop_index) \ - if (strcmp(expected, actual) != 0) { \ - char str[128]; \ - sprintf(str, "expected %s was %s (loop index: %lu)", expected, actual, loop_index); \ - TEST_ASSERT_MESSAGE(false, str); \ - } - -#define TEST_ASSERT_EQUAL_LOOP(expected, actual, loop_index) \ - if (expected != actual) { \ - char str[128]; \ - sprintf(str, "expected %d was %d (loop index: %lu)", expected, actual, loop_index); \ - TEST_ASSERT_MESSAGE(false, str); \ - } - - -/** Initialize storages - * - * Given the DUT USB mass storage device - * When DUT has enought heap memory - * Then initialize heap block device for tests - * When DUT has any falsh block device - * Then initialize it for tests - */ -void storage_init() -{ - TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation"); - FATFileSystem::format(get_heap_block_device()); - bool result = prepare_storage(get_heap_block_device(), &heap_fs); - TEST_ASSERT_MESSAGE(result, "heap storage initialisation failed"); -} - -/** Test mass storage device mount and unmount - * - * Given the DUT USB mass storage device connected to the host - * When DUT call @USBMSD::connect - * Then host detects mass storage device is mounted as removable disk drive and it reports valid filesystem size - * When DUT call @USBMSD::disconnect - * Then host detects mass storage device is unmounted (ejected) - * - * Given the DUT USB mass storage device connected to the host and mounted - * When host unmounts (ejects) mass storage device - * Then DUT detects media remove event - */ -template -void mount_unmount_test(BlockDevice *bd, FileSystem *fs) -{ - Thread msd_thread(osPriorityHigh); - TestUSBMSD usb(bd, false); - msd_process_done = false; - msd_thread.start(callback(msd_process, &usb)); - - for (uint32_t i = 1; i <= N; i++) { - // mount - usb.connect(); - WAIT_MSD_COMMUNICATION_DONE(); - // check if device is mounted on host side - greentea_send_kv("check_if_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - - TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); - - greentea_send_kv("get_mounted_fs_size", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - uint64_t ret_size = atoll(_key); - TEST_ASSERT_EQUAL_UINT64(get_fs_mount_size(fs), ret_size); - - // unmount - usb.disconnect(); - // check if device is detached on host side - greentea_send_kv("check_if_not_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); - } - - for (uint32_t i = 1; i <= N; i++) { - // mount - usb.connect(); - WAIT_MSD_COMMUNICATION_DONE(); - // check if device is mounted on host side - greentea_send_kv("check_if_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); - - greentea_send_kv("get_mounted_fs_size", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - uint64_t ret_size = atoll(_key); - TEST_ASSERT_EQUAL_UINT64(get_fs_mount_size(fs), ret_size); - -#ifdef DISABLE_HOST_SIDE_UMOUNT - greentea_send_kv("get_os_type", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - int32_t os_type = atoi(_value); - if (os_type != OS_WINDOWS) { -#endif - // unmount msd device on host side - greentea_send_kv("unmount", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); - - // wait for unmount event (set 10s timeout) - media_remove_event.try_acquire_for(10000); - if (!usb.media_removed()) { - TEST_ASSERT_EQUAL_LOOP(true, usb.media_removed(), i); - } - - // unmount since media_removed doesn't disconnects device side - usb.disconnect(); -#ifdef DISABLE_HOST_SIDE_UMOUNT - } else { - // unmount - usb.disconnect(); - } -#endif - // check if device is detached on host side - greentea_send_kv("check_if_not_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); - } - - // mount - usb.connect(); - WAIT_MSD_COMMUNICATION_DONE(); - // check if device is mounted on host side - greentea_send_kv("check_if_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - // unmount - usb.disconnect(); - // check if device is detached on host side - greentea_send_kv("check_if_not_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - msd_process_done = true; // terminate msd_thread - msd_thread.join(); -} - -/** Test mass storage device mount and unmount together with underlying file system operations - * - * Given the DUT USB mass storage device connected to the host - * When DUT call @USBMSD::connect - * Then host detects that mass storage device is mounted as removable disk drive and test files are present - * When DUT call @USBMSD::disconnect - * Then host detects mass storage device is unmounted (ejected) - * - * Given the DUT USB mass storage device connected to the host and already mounted - * When host unmounts (ejects) mass storage device - * Then DUT detects media remove event - */ -void mount_unmount_and_data_test(BlockDevice *bd, FileSystem *fs) -{ - const char *fs_root = fs->getName(); - Thread msd_thread(osPriorityHigh); - TestUSBMSD usb(bd, false); - msd_process_done = false; - msd_thread.start(callback(msd_process, &usb)); - - // mount - usb.connect(); - WAIT_MSD_COMMUNICATION_DONE(); - // check if device is mounted on host side - greentea_send_kv("check_if_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - greentea_send_kv("check_file_exist", TEST_DIR " " TEST_FILE " " TEST_STRING); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("non-exist", _key); - - usb.disconnect(); - // check if device is detached on host side - greentea_send_kv("check_if_not_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - test_files_create(fs_root); - TEST_ASSERT(test_files_exist(fs_root)); - - usb.connect(); - WAIT_MSD_COMMUNICATION_DONE(); - // check if device is mounted on host side - greentea_send_kv("check_if_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - greentea_send_kv("check_file_exist", TEST_DIR " " TEST_FILE " " TEST_STRING); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("exist", _key); - - greentea_send_kv("delete_files", TEST_DIR " " TEST_FILE); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - do { - ThisThread::sleep_for(1); - } while (test_files_exist(fs_root)); - TEST_ASSERT_EQUAL(false, test_files_exist(fs_root)); - - usb.disconnect(); - // check if device is detached on host side - greentea_send_kv("check_if_not_mounted", 0); - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - TEST_ASSERT_EQUAL_STRING("passed", _key); - - msd_process_done = true; // terminate msd_thread - msd_thread.join(); - test_files_remove(fs_root); -} - -void heap_block_device_mount_unmount_test() -{ - TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation"); - mount_unmount_test<3>(get_heap_block_device(), &heap_fs); -} - -void heap_block_device_mount_unmount_and_data_test() -{ - TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation"); - mount_unmount_and_data_test(get_heap_block_device(), &heap_fs); -} - - -Case cases[] = { - Case("storage initialization", storage_init), - - Case("mount/unmount test - Heap block device", heap_block_device_mount_unmount_test), - Case("mount/unmount and data test - Heap block device", heap_block_device_mount_unmount_and_data_test), -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(300, "pyusb_msd"); - utest::v1::status_t status = greentea_test_setup_handler(number_of_cases); - TestUSBMSD::setup_serial_number(); - return status; -} - -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); - -int main() -{ - Harness::run(specification); -} - -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(MBED_CONF_RTOS_PRESENT) -#endif // !defined(USB_DEVICE_TESTS) diff --git a/TESTS/usb_device/serial/main.cpp b/TESTS/usb_device/serial/main.cpp deleted file mode 100644 index 7f16ef5..0000000 --- a/TESTS/usb_device/serial/main.cpp +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - -#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#error [NOT_SUPPORTED] USB Device not supported for this target -#else - -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" -#include "mbed.h" -#include -#include "usb_phy_api.h" -#include "USBCDC.h" -#include "USBSerial.h" -#include "hal/us_ticker_api.h" - -#define USB_CDC_VID 0x1f00 -#define USB_CDC_PID 0x2013 -#define USB_SERIAL_VID 0x1f00 -#define USB_SERIAL_PID 0x2012 - -#define MSG_KEY_LEN 24 -#define MSG_VALUE_DUMMY "0" -#define MSG_KEY_DEVICE_READY "ready" -#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn" -#define MSG_KEY_PORT_OPEN_WAIT "port_open_wait" -#define MSG_KEY_PORT_OPEN_CLOSE "port_open_close" -#define MSG_KEY_SEND_BYTES_SINGLE "send_single" -#define MSG_KEY_SEND_BYTES_MULTIPLE "send_multiple" -#define MSG_KEY_LOOPBACK "loopback" -#define MSG_KEY_CHANGE_LINE_CODING "change_lc" - -#define TX_BUFF_SIZE 32 -#define RX_BUFF_SIZE 32 - -// The size of every data chunk the host sends (for each value from a -// known sequence) during 'CDC RX multiple' test cases is -// HOST_RX_BUFF_SIZE_RATIO times the size of RX_BUFF_SIZE input buffer. -// 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 1ms - -#define CDC_LOOPBACK_REPS 1200 -#define SERIAL_LOOPBACK_REPS 100 -#define USB_RECONNECT_DELAY_MS 1ms - -#define LINE_CODING_SEP (',') -#define LINE_CODING_DELIM (';') - -#define USB_DEV_SN_LEN (32) // 32 hex digit UUID -#define NONASCII_CHAR ('?') -#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2) - -const char *default_serial_num = "0123456789"; -char usb_dev_sn[USB_DEV_SN_LEN + 1]; - -using utest::v1::Case; -using utest::v1::Specification; -using utest::v1::Harness; - -typedef struct LineCoding { - // bits per second - int baud; - - // 5, 6, 7, 8 or 16 - int bits; - - // 0 -- None, - // 1 -- Odd, - // 2 -- Even, - // 3 -- Mark, - // 4 -- Space - int parity; - - // 0 -- 1 Stop bit, - // 1 -- 1.5 Stop bits, - // 2 -- 2 Stop bits - int stop; - - int get_num_diffs(LineCoding const &other) const - { - int diffs = 0; - if (baud != other.baud) { - diffs++; - } - if (bits != other.bits) { - diffs++; - } - if (parity != other.parity) { - diffs++; - } - if (stop != other.stop) { - diffs++; - } - return diffs; - } -} line_coding_t; - -line_coding_t default_lc = { 9600, 8, 0, 0 }; - -// There is no POSIX support for 1.5 stop bits. -// Do not set stop bits to 1.5 to keep tests compatible with all supported -// host systems. -line_coding_t test_codings[] = { - { 9600, 5, 0, 2 }, - { 4800, 7, 2, 0 }, - { 19200, 8, 0, 2 }, - { 115200, 8, 0, 0 }, - { 38400, 8, 1, 0 }, - { 1200, 8, 0, 0 }, - { 19200, 8, 0, 0 }, - { 2400, 7, 2, 0 }, - { 9600, 8, 0, 0 }, - { 57600, 8, 0, 0 }, -}; -static CircularBuffer lc_data; - -#define EF_SEND (1ul << 0) -EventFlags event_flags; - -/** - * Convert a USB string descriptor to C style ASCII - * - * The string placed in str is always null-terminated which may cause the - * loss of data if n is to small. If the length of descriptor string is less - * than n, additional null bytes are written to str. - * - * @param str output buffer for the ASCII string - * @param usb_desc USB string descriptor - * @param n size of str buffer - * @returns number of non-null bytes returned in str or -1 on failure - */ -int usb_string_desc2ascii(char *str, const uint8_t *usb_desc, size_t n) -{ - if (str == NULL || usb_desc == NULL || n < 1) { - return -1; - } - // bDescriptorType @ offset 1 - if (usb_desc[1] != STRING_DESCRIPTOR) { - return -1; - } - // bLength @ offset 0 - const size_t bLength = usb_desc[0]; - if (bLength % 2 != 0) { - return -1; - } - size_t s, d; - for (s = 0, d = 2; s < n - 1 && d < bLength; s++, d += 2) { - // handle non-ASCII characters - if (usb_desc[d] > 0x7f || usb_desc[d + 1] != 0) { - str[s] = NONASCII_CHAR; - } else { - str[s] = usb_desc[d]; - } - } - int str_len = s; - for (; s < n; s++) { - str[s] = '\0'; - } - return str_len; -} - -/** - * Convert a C style ASCII to a USB string descriptor - * - * @param usb_desc output buffer for the USB string descriptor - * @param str ASCII string - * @param n size of usb_desc buffer, even number - * @returns number of bytes returned in usb_desc or -1 on failure - */ -int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n) -{ - if (str == NULL || usb_desc == NULL || n < 4) { - return -1; - } - if (n % 2 != 0) { - return -1; - } - size_t s, d; - // set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string - memset(usb_desc, 0, n); - for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) { - usb_desc[d] = str[s]; - } - // set bLength @ offset 0 - usb_desc[0] = d; - // set bDescriptorType @ offset 1 - usb_desc[1] = STRING_DESCRIPTOR; - return d; -} - -class TestUSBCDC: public USBCDC { -private: - uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; -public: - TestUSBCDC(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, - const char *serial_number = default_serial_num) : - USBCDC(get_usb_phy(), vendor_id, product_id, product_release) - { - init(); - int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); - if (rc < 0) { - ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); - } - } - - virtual ~TestUSBCDC() - { - deinit(); - } - - virtual const uint8_t *string_iserial_desc() - { - return (const uint8_t *) _serial_num_descriptor; - } -}; - -class TestUSBSerial: public USBSerial { -private: - uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; -public: - TestUSBSerial(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, - const char *serial_number = default_serial_num) : - USBSerial(get_usb_phy(), vendor_id, product_id, product_release) - { - int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); - if (rc < 0) { - ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); - } - } - - virtual ~TestUSBSerial() - { - deinit(); - } - - virtual const uint8_t *string_iserial_desc() - { - return (const uint8_t *) _serial_num_descriptor; - } -}; - -/** Test CDC USB reconnect - * - * Given the host has successfully opened the port of a USB CDC device - * When the USB device disconnects and connects again - * Then the host is able to successfully open the port again - */ -void test_cdc_usb_reconnect() -{ - TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); - TEST_ASSERT_FALSE(usb_cdc.configured()); - TEST_ASSERT_FALSE(usb_cdc.ready()); - - // Connect the USB device. - usb_cdc.connect(); - // Wait for the USB enumeration to complete. - while (!usb_cdc.configured()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_cdc.configured()); - TEST_ASSERT_FALSE(usb_cdc.ready()); - - 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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - usb_cdc.wait_ready(); - TEST_ASSERT_TRUE(usb_cdc.configured()); - TEST_ASSERT_TRUE(usb_cdc.ready()); - - // Disconnect the USB device. - usb_cdc.disconnect(); - TEST_ASSERT_FALSE(usb_cdc.configured()); - TEST_ASSERT_FALSE(usb_cdc.ready()); - - ThisThread::sleep_for(USB_RECONNECT_DELAY_MS); - // Connect the USB device again. - usb_cdc.connect(); - // Wait for the USB enumeration to complete. - while (!usb_cdc.configured()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_cdc.configured()); - TEST_ASSERT_FALSE(usb_cdc.ready()); - - 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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - usb_cdc.wait_ready(); - TEST_ASSERT_TRUE(usb_cdc.configured()); - TEST_ASSERT_TRUE(usb_cdc.ready()); - - // Disconnect the USB device again. - usb_cdc.disconnect(); - TEST_ASSERT_FALSE(usb_cdc.configured()); - TEST_ASSERT_FALSE(usb_cdc.ready()); -} - -/** Test CDC receive single bytes - * - * Given the USB CDC device connected to a host - * When the host transmits a known sequence one byte at a time - * Then every byte received by the device matches the sequence - */ -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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - usb_cdc.wait_ready(); - uint8_t buff = 0x01; - for (int expected = 0xff; expected >= 0; expected--) { - TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); - TEST_ASSERT_EQUAL_UINT8(expected, buff); - } - for (int expected = 0; expected <= 0xff; expected++) { - TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); - TEST_ASSERT_EQUAL_UINT8(expected, buff); - } - // Wait for the host to close its port. - while (usb_cdc.ready()) { - ThisThread::sleep_for(1ms); - } - usb_cdc.disconnect(); -} - -void tx_thread_fun(USBCDC *usb_cdc) -{ - uint8_t buff_val = 0; - uint8_t buff[TX_BUFF_SIZE] = { 0 }; - while (event_flags.get() & EF_SEND) { - if (!usb_cdc->send(buff, TX_BUFF_SIZE)) { - ThisThread::sleep_for(1ms); - continue; - } - buff_val++; - memset(buff, buff_val, TX_BUFF_SIZE); - } -} - -void tx_ticker_fun(USBCDC *usb_cdc, uint8_t start_val, uint8_t size = TX_BUFF_SIZE) -{ - static uint8_t buff_val = start_val; - uint32_t actual_tx = 0; - uint8_t buff[TX_BUFF_SIZE] = { 0 }; - memset(buff, buff_val, size); - usb_cdc->send_nb(buff, size, &actual_tx); - TEST_ASSERT_EQUAL_UINT8(size, actual_tx); - buff_val++; -} - -/** Test CDC receive single bytes concurrently - * - * Given the USB CDC device connected to a host - * When the host transmits a known sequence one byte at a time - * and at the same time the device transmits data to host - * Then every byte received by the device matches the sequence - */ -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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - usb_cdc.wait_ready(); -#if defined(MBED_CONF_RTOS_PRESENT) - Thread tx_thread; - event_flags.set(EF_SEND); - tx_thread.start(mbed::callback(tx_thread_fun, &usb_cdc)); -#else - Ticker t; - t.attach([&] { tx_ticker_fun(&usb_cdc, 0, 1); }, 2ms); -#endif - - uint8_t buff = 0x01; - for (int expected = 0xff; expected >= 0; expected--) { - TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); - TEST_ASSERT_EQUAL_UINT8(expected, buff); - } - for (int expected = 0; expected <= 0xff; expected++) { - TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); - TEST_ASSERT_EQUAL_UINT8(expected, buff); - } - -#if defined(MBED_CONF_RTOS_PRESENT) - event_flags.clear(EF_SEND); - tx_thread.join(); -#else - t.detach(); -#endif - // Wait for the host to close its port. - while (usb_cdc.ready()) { - ThisThread::sleep_for(1ms); - } - usb_cdc.disconnect(); -} - -/** Test CDC receive multiple bytes - * - * Given the USB CDC device connected to a host - * When the host transmits chunks of data following a known sequence - * Then every chunk received by the device matches the sequence - */ -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(); - ThisThread::sleep_for(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 }; - for (int expected = 0xff; expected >= 0; expected--) { - for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { - memset(expected_buff, expected, RX_BUFF_SIZE); - TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); - TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); - } - } - for (int expected = 0; expected <= 0xff; expected++) { - for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { - memset(expected_buff, expected, RX_BUFF_SIZE); - TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); - TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); - } - } - // Wait for the host to close its port. - while (usb_cdc.ready()) { - ThisThread::sleep_for(1ms); - } - usb_cdc.disconnect(); -} - -/** Test CDC receive multiple bytes concurrently - * - * Given the USB CDC device connected to a host - * When the host transmits chunks of data following a known sequence - * and at the same time the device transmits data to host - * Then every chunk received by the device matches the sequence - */ -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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - usb_cdc.wait_ready(); -#if defined(MBED_CONF_RTOS_PRESENT) - Thread tx_thread; - event_flags.set(EF_SEND); - tx_thread.start(mbed::callback(tx_thread_fun, &usb_cdc)); -#else - Ticker t; - t.attach([&] { tx_ticker_fun(&usb_cdc, 0); }, 3ms); -#endif - uint8_t buff[RX_BUFF_SIZE] = { 0 }; - uint8_t expected_buff[RX_BUFF_SIZE] = { 0 }; - for (int expected = 0xff; expected >= 0; expected--) { - for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { - memset(expected_buff, expected, RX_BUFF_SIZE); - TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); - TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); - } - } - for (int expected = 0; expected <= 0xff; expected++) { - for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { - memset(expected_buff, expected, RX_BUFF_SIZE); - TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); - TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); - } - } -#if defined(MBED_CONF_RTOS_PRESENT) - event_flags.clear(EF_SEND); - tx_thread.join(); -#else - t.detach(); -#endif - // Wait for the host to close its port. - while (usb_cdc.ready()) { - ThisThread::sleep_for(1ms); - } - usb_cdc.disconnect(); -} - -/** Test CDC loopback - * - * Given the USB CDC device connected to a host - * When the device transmits random bytes to host - * and the host transmits them back to the device - * Then every byte received by the device is equal to byte preciously sent - */ -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(); - ThisThread::sleep_for(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++) { - tx_buff = (uint8_t)(rand() % 0x100); - rx_buff = (uint8_t)(tx_buff + 1); - TEST_ASSERT(usb_cdc.send(&tx_buff, 1)); - TEST_ASSERT(usb_cdc.receive(&rx_buff, 1, NULL)); - TEST_ASSERT_EQUAL_UINT8(tx_buff, rx_buff); - } - // Wait for the host to close its port. - while (usb_cdc.ready()) { - ThisThread::sleep_for(1ms); - } - usb_cdc.disconnect(); -} - -/** Test Serial USB reconnect - * - * Given the host has successfully opened the port of a USB Serial device - * When the USB device disconnects and connects again - * Then the host is able to successfully open the port again - */ -void test_serial_usb_reconnect() -{ - TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); - TEST_ASSERT_FALSE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - // Connect the USB device. - usb_serial.connect(); - // Wait for the USB enumeration to complete. - while (!usb_serial.configured()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - 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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - while (!usb_serial.connected()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_TRUE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - // Disconnect the USB device. - usb_serial.disconnect(); - TEST_ASSERT_FALSE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - ThisThread::sleep_for(USB_RECONNECT_DELAY_MS); - // Connect the USB device again. - usb_serial.connect(); - // Wait for the USB enumeration to complete. - while (!usb_serial.configured()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - 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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - while (!usb_serial.connected()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_TRUE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - // Disconnect the USB device again. - usb_serial.disconnect(); - TEST_ASSERT_FALSE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); -} - -/** Test Serial terminal reopen - * - * Given the host has successfully opened the port of a USB Serial device - * When the host closes its port - * Then the host is able to successfully open the port again - */ -void test_serial_term_reopen() -{ - TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); - 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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - while (!usb_serial.connected()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_TRUE(usb_serial.ready()); - TEST_ASSERT_TRUE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - // Wait for the host to close the terminal. - while (usb_serial.ready()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.ready()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - 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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - while (!usb_serial.connected()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_TRUE(usb_serial.ready()); - TEST_ASSERT_TRUE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - // Wait for the host to close the terminal again. - while (usb_serial.ready()) { - ThisThread::sleep_for(1ms); - } - TEST_ASSERT_TRUE(usb_serial.configured()); - TEST_ASSERT_FALSE(usb_serial.ready()); - TEST_ASSERT_FALSE(usb_serial.connected()); - TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); - - usb_serial.disconnect(); -} - -/** Test Serial getc - * - * Given the USB Serial device connected to a host - * When the host transmits a known sequence one byte at a time - * Then every byte received by the device matches the sequence - */ -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); -#if LINUX_HOST_DTR_FIX - usb_serial.wait_ready(); - ThisThread::sleep_for(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()); - } - for (int expected = 0; expected <= 0xff; expected++) { - TEST_ASSERT_EQUAL_INT(expected, usb_serial.getc()); - } - // Wait for the host to close its port. - while (usb_serial.ready()) { - ThisThread::sleep_for(1ms); - } - usb_serial.disconnect(); -} - -/** Test Serial printf & scanf - * - * Given the USB Serial device connected to a host - * When the device transmits a formatted string with a random value - * using the printf method - * and the host sends it back to the device - * Then the device can successfully read the value using scanf method - * and the value received is equal value sent - */ -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(); - ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); -#endif - usb_serial.wait_ready(); - static const char fmt[] = "Formatted\nstring %i."; - int tx_val, rx_val, rc; - for (int i = 0; i < SERIAL_LOOPBACK_REPS; i++) { - tx_val = rand(); - rx_val = tx_val + 1; - rc = usb_serial.printf(fmt, tx_val); - TEST_ASSERT(rc > 0); - rc = usb_serial.scanf(fmt, &rx_val); - TEST_ASSERT(rc == 1); - TEST_ASSERT_EQUAL_INT(tx_val, rx_val); - } - // Wait for the host to close its port. - while (usb_serial.ready()) { - ThisThread::sleep_for(1ms); - } - usb_serial.disconnect(); -} - -void line_coding_changed_cb(int baud, int bits, int parity, int stop) -{ - line_coding_t lc = { - .baud = baud, - .bits = bits, - .parity = parity, - .stop = stop - }; - lc_data.push(lc); - event_flags.set(EF_SEND); -} - -/** Test Serial / CDC line coding change - * - * Given the device transmits a set of line coding params to host - * When the host updates serial port settings - * Then line_coding_changed() callback is called - * and the line coding is set as expected - */ -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(); - ThisThread::sleep_for(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]; - line_coding_t *lc_prev = &default_lc; - line_coding_t *lc_expected = NULL; - int num_expected_callbacks, rc; - for (size_t i = 0; i < num_line_codings; i++) { - line_coding_t lc_actual = {0}; - lc_expected = &(test_codings[i]); - num_expected_callbacks = lc_prev->get_num_diffs(*lc_expected); - rc = usb_serial.printf("%06i,%02i,%01i,%01i%c", lc_expected->baud, lc_expected->bits, lc_expected->parity, - lc_expected->stop, LINE_CODING_DELIM); - TEST_ASSERT(rc > 0); - // The pyserial Python module does not update all line coding params - // at once. It updates params one by one instead, and since every - // update is followed by port reconfiguration we get multiple - // calls to line_coding_changed callback on the device. - while (num_expected_callbacks > 0) { - num_expected_callbacks--; - event_flags.wait_all(EF_SEND); - lc_data.pop(lc_actual); - if (lc_expected->get_num_diffs(lc_actual) == 0) { - break; - } - } - TEST_ASSERT_EQUAL_INT(lc_expected->baud, lc_actual.baud); - TEST_ASSERT_EQUAL_INT(lc_expected->bits, lc_actual.bits); - TEST_ASSERT_EQUAL_INT(lc_expected->parity, lc_actual.parity); - TEST_ASSERT_EQUAL_INT(lc_expected->stop, lc_actual.stop); - lc_prev = lc_expected; - } - // Wait for the host to close its port. - while (usb_serial.ready()) { - ThisThread::sleep_for(1ms); - } - usb_serial.disconnect(); -} - -utest::v1::status_t testsuite_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(45, "usb_device_serial"); - srand((unsigned) ticker_read_us(get_us_ticker_data())); - - utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases); - if (status != utest::v1::STATUS_CONTINUE) { - return status; - } - - char key[MSG_KEY_LEN + 1] = { }; - char usb_dev_uuid[USB_DEV_SN_LEN + 1] = { }; - - greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); - greentea_parse_kv(key, usb_dev_uuid, MSG_KEY_LEN, USB_DEV_SN_LEN + 1); - - if (strcmp(key, MSG_KEY_SERIAL_NUMBER) != 0) { - utest_printf("Invalid message key.\n"); - return utest::v1::STATUS_ABORT; - } - - strncpy(usb_dev_sn, usb_dev_uuid, USB_DEV_SN_LEN + 1); - return status; -} - -Case cases[] = { - Case("CDC USB reconnect", test_cdc_usb_reconnect), - Case("CDC RX single bytes", test_cdc_rx_single_bytes), - Case("CDC RX single bytes concurrent", test_cdc_rx_single_bytes_concurrent), - Case("CDC RX multiple bytes", test_cdc_rx_multiple_bytes), - Case("CDC RX multiple bytes concurrent", test_cdc_rx_multiple_bytes_concurrent), - Case("CDC loopback", test_cdc_loopback), - Case("Serial USB reconnect", test_serial_usb_reconnect), - Case("Serial terminal reopen", test_serial_term_reopen), - Case("Serial getc", test_serial_getc), - Case("Serial printf/scanf", test_serial_printf_scanf), - Case("Serial line coding change", test_serial_line_coding_change), -}; - -Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(USB_DEVICE_TESTS) diff --git a/drivers/tests/TESTS/usb_device/README.md b/drivers/tests/TESTS/usb_device/README.md new file mode 100644 index 0000000..04a48f8 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/README.md @@ -0,0 +1,166 @@ +# Testing the Mbed OS USB device + +## Setup +Before running tests, please make sure to use a +[top-level requirements.txt][LN-requirements] file to install all the +required Python modules. + +``` +pip install -r requirements.txt +``` + +Additional, platform-specific setup is described below. +See also [Known issues](#known-issues). + +### Windows +1. Install a **generic USB driver** for two test devices. + 1. Download `Zadig` application from [the Zadig website][LN-zadig]. + 1. Unplug the Mbed device. + 1. Open `Zadig`. + 1. Select *Device -> Load Preset Device*. + 1. Open [TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg][LN-zadig_conf1] + 1. Choose `libusb-win32 (v1.2.6.0)` driver. + 1. Select `Install Driver` and click it. + 1. Select *Device -> Load Preset Device*. + 1. Open [TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg][LN-zadig_conf2] + 1. Choose `libusb-win32 (v1.2.6.0)` driver. + 1. Select `Install Driver` and click it. + 1. Close `Zadig`. + 1. Plug both USB interfaces (*DAPLink* and *USB device*). + +### Linux +1. Install the `hidapi` Python module, otherwise some USB HID test cases will + be skipped. This module is not installed during the initial setup due to + external dependencies for Linux. + + For Debian-based Linux distros, the dependencies can be installed as follows + (based on module's [README][LN-hidapi_readme]): + + ```bash + apt-get install python-dev libusb-1.0-0-dev libudev-dev + pip install --upgrade setuptools + ``` + + To install the `hidapi` module itself, please use the attached + [requirements.txt][LN-hid_requirements] file: + + ```bash + pip install -r TESTS/usb_device/hid/requirements.txt + ``` + +1. Update the `udev` rules for Mbed USB CDC device as follows + ([source][LN-udev_rules]): + + ```bash + sudo tee /etc/udev/rules.d/99-ttyacms.rules >/dev/null < -m -DUSB_DEVICE_TESTS -n *-tests-usb_device-* + ``` + +## Known issues + +### Insufficient user permissions on a Linux machine +Some of the tests require privileged access to the USB device. Running these +as an unprivileged user will manifest with either of the following errors: +* ``` + [HTST][INF] Device not found + ``` +* ``` + [HTST][INF] TEST ERROR: Failed with "The device has no langid". Tried 20 times. + ``` + +#### Solution +Execute tests with elevated permissions using `sudo`: +```bash +mbed test -t -m -DUSB_DEVICE_TESTS -n tests-usb_device-* --compile +sudo mbed test -t -m -n *-tests-usb_device-* --run -v +``` +Note only the `mbed test --run` command requires `sudo`. You can still +`mbed test --compile` as a normal user. + +#### Alternative solution +Add an `udev` rule to set the ownership of the device node +[as shown here][LN-libusb_permissions]. + +### Data toggle reset test fails on a Linux machine +The `tests-usb_device-basic` / `"endpoint test data toggle reset"` test fails +with the following error: +``` +[HTST][INF] TEST FAILED: Data toggle not reset when calling +ClearFeature(ENDPOINT_HALT) on an endpoint that has not been halted. +``` + +Implementations of the *xHCI* driver prior to version 4.17 of the Linux kernel did +not have the functionality necessary to test `"endpoint test data toggle reset"`. +Even though the data toggle is correctly reset on the device side, the host +side will not be synchronized and the test will falsely fail. + +#### Solution +Make sure that **at least one** of the following prerequisites is met: +* using the Linux kernel ***4.17* or newer**, +* using the ***eHCI*** USB driver instead of *xHCI*. + +Changing the USB driver may be as simple as updating one of the BIOS settings +on your machine. If you'd rather avoid rebooting, you can try +[using setpci command][LN-xhci_setpci]. + +#### Further reading +1. [the Linux kernel patch adding missing xHCI behavior][LN-linux_xhci_patch], +1. [LKML discussion explaining the details of this issue][LN-xhci_lkml_discussion]. + +### Mass storage tests are skipped on some targets +The `tests-usb_device-msd` test outputs the following message: +``` +[CONN][RXD] SKIP: Not enough heap memory for HeapBlockDevice creation +``` + +#### Solution +A device with at least *70 kB* of RAM is required to run this test. +The FAT32 filesystem cannot be mounted on a device smaller than 64 kB. + +The test can be easily extended to use any block device available in Mbed. + +### Windows 8/10: Mass storage tests are failing on test file read +By default Windows 8 and 10 access and write to removable drives shortly after they are connected. It's caused by drive indexing mechanisms. Because disk used in test has only 64kB its content can be easily corrupted by writing large files, what actually was encountered during test runs. + +To prevent Windows from writing to removable drives on connect drive indexing can be turned off with the following procedure: +- Start the program "gpedit.msc" +- Navigate to "Computer Configuration \ Administrative Templates \ Windows Components \ Search" +- Enable the policy "Do not allow locations on removable drives to be added to libraries." + +### Isochronous endpoints are skipped in loopback tests +#### Unresolved + +### Serial tests fail intermittently on a Linux machine +#### Unresolved +You may want to connect the device directly to the host machine with no hubs on the way. + + +[LN-requirements]: ../../requirements.txt +[LN-zadig]: https://zadig.akeo.ie/ +[LN-zadig_conf1]: basic/zadig_conf/mbed_os-usb_test_device1.cfg +[LN-zadig_conf2]: basic/zadig_conf/mbed_os-usb_test_device2.cfg +[LN-hidapi_readme]: https://github.com/trezor/cython-hidapi/blob/master/README.rst#install +[LN-hid_requirements]: hid/requirements.txt +[LN-udev_rules]: https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284 +[LN-libusb_permissions]: https://stackoverflow.com/questions/3738173/why-does-pyusb-libusb-require-root-sudo-permissions-on-linux/8582398#8582398 +[LN-linux_xhci_patch]: https://github.com/torvalds/linux/commit/f5249461b504d35aa1a40140983b7ec415807d9e +[LN-xhci_lkml_discussion]: https://lkml.org/lkml/2016/12/15/388 +[LN-xhci_setpci]: https://linuxmusicians.com/viewtopic.php?t=16901 + diff --git a/drivers/tests/TESTS/usb_device/basic/USBEndpointTester.cpp b/drivers/tests/TESTS/usb_device/basic/USBEndpointTester.cpp new file mode 100644 index 0000000..4b48e94 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/USBEndpointTester.cpp @@ -0,0 +1,865 @@ +/* + * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if USB_DEVICE_TESTS + +#include "stdint.h" +#include "stdlib.h" +#include "USBEndpointTester.h" +#include "events/mbed_shared_queues.h" +#include "EndpointResolver.h" + +#define DEFAULT_CONFIGURATION (1) + +#define NUM_PACKETS_UNTIL_ABORT 2 +#define NUM_PACKETS_AFTER_ABORT 8 +#define EP_ABORT_BUFF_VALUE 0xff + +/* If the host ever receives a payload with any byte set to this value, + * the device does not handle abort operation correctly. The buffer + * passed to aborted operation must not be used after call to abort(). + */ +#define FORBIDDEN_PAYLOAD_VALUE (NUM_PACKETS_AFTER_ABORT + 1) + +#define VENDOR_TEST_CTRL_IN 1 +#define VENDOR_TEST_CTRL_OUT 2 +#define VENDOR_TEST_CTRL_IN_SIZES 3 +#define VENDOR_TEST_CTRL_OUT_SIZES 4 +#define VENDOR_TEST_RW_RESTART 5 +#define VENDOR_TEST_ABORT_BUFF_CHECK 6 + +#define CTRL_BUF_SIZE (2048) + +#define EVENT_READY (1 << 0) + +#define TEST_SIZE_EP_BULK_MAX (64) +#define TEST_SIZE_EP_BULK_MIN (8) +#define TEST_SIZE_EP_BULK_0 (16) +#define TEST_SIZE_EP_BULK_1 TEST_SIZE_EP_BULK_MAX +#define TEST_SIZE_EP_BULK_2 (32) +#define TEST_SIZE_EP_BULK_3 (16) +#define TEST_SIZE_EP_BULK_4 TEST_SIZE_EP_BULK_MIN + +#define TEST_SIZE_EP_INT_MAX (64) +#define TEST_SIZE_EP_INT_MIN (1) +#define TEST_SIZE_EP_INT_0 (16) +#define TEST_SIZE_EP_INT_1 TEST_SIZE_EP_INT_MAX +#define TEST_SIZE_EP_INT_2 (32) +#define TEST_SIZE_EP_INT_3 (16) +#define TEST_SIZE_EP_INT_4 TEST_SIZE_EP_INT_MIN + + +/* According to USB spec, the wMaxPacketSize for FS isochronous endpoints + * is 1023 B. There are a couple of reasons this value is not used in tests: + * - some of the boards supported by Mbed OS have too little RAM dedicated + * for USB, making EndpointResolve::valid() fail when all the endpoints (2x + * bulk, 2x interrupt, 2x isochronous, 2x control) are configured to use + * the max value of wMaxPacketSize + * (e.g. NUCLEO_F207ZG has 1.25K of endpoint RAM), + * - given a test host with other USB devices on the bus, it is unlikely + * for the test device to be able to reserve the bandwidth associated with + * high wMaxPacketSize for iso endpoints. + */ +#define TEST_SIZE_EP_ISO_MAX (256) +#define TEST_SIZE_EP_ISO_MIN (1) +#define TEST_SIZE_EP_ISO_0 (0) +#define TEST_SIZE_EP_ISO_1 (0) +#define TEST_SIZE_EP_ISO_2 (0) +#define TEST_SIZE_EP_ISO_3 (0) +#define TEST_SIZE_EP_ISO_4 (0) + +#define EP_BULK_OUT 0 +#define EP_BULK_IN 1 +#define EP_INT_OUT 2 +#define EP_INT_IN 3 +#define EP_ISO_OUT 4 +#define EP_ISO_IN 5 + +USBEndpointTester::ep_config_t USBEndpointTester::_intf_config_max[NUM_ENDPOINTS] = { + { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_MAX, static_cast(&USBEndpointTester::_cb_bulk_out) }, + { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_MAX, static_cast(&USBEndpointTester::_cb_bulk_in) }, + { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_MAX, static_cast(&USBEndpointTester::_cb_int_out) }, + { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_MAX, static_cast(&USBEndpointTester::_cb_int_in) }, + { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_MAX, static_cast(&USBEndpointTester::_cb_iso_out) }, + { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_MAX, static_cast(&USBEndpointTester::_cb_iso_in) }, +}; + +USBEndpointTester::ep_config_t USBEndpointTester::_intf_config0[NUM_ENDPOINTS] = { + { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_0, NULL }, + { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_0, NULL }, + { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_0, NULL }, + { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_0, NULL }, + { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_0, NULL }, + { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_0, NULL }, +}; + +USBEndpointTester::ep_config_t USBEndpointTester::_intf_config1[NUM_ENDPOINTS] = { + { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_1, static_cast(&USBEndpointTester::_cb_bulk_out) }, + { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_1, static_cast(&USBEndpointTester::_cb_bulk_in) }, + { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_1, static_cast(&USBEndpointTester::_cb_int_out) }, + { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_1, static_cast(&USBEndpointTester::_cb_int_in) }, + { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_1, static_cast(&USBEndpointTester::_cb_iso_out) }, + { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_1, static_cast(&USBEndpointTester::_cb_iso_in) }, +}; + +USBEndpointTester::ep_config_t USBEndpointTester::_intf_config2[NUM_ENDPOINTS] = { + { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_2, static_cast(&USBEndpointTester::_cb_bulk_out) }, + { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_2, static_cast(&USBEndpointTester::_cb_bulk_in) }, + { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_2, static_cast(&USBEndpointTester::_cb_int_out) }, + { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_2, static_cast(&USBEndpointTester::_cb_int_in) }, + { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_2, static_cast(&USBEndpointTester::_cb_iso_out) }, + { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_2, static_cast(&USBEndpointTester::_cb_iso_in) }, +}; + +USBEndpointTester::ep_config_t USBEndpointTester::_intf_config3[NUM_ENDPOINTS] = { + { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_3, static_cast(&USBEndpointTester::_cb_bulk_out) }, + { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_3, static_cast(&USBEndpointTester::_cb_bulk_in) }, + { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_3, static_cast(&USBEndpointTester::_cb_int_out) }, + { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_3, static_cast(&USBEndpointTester::_cb_int_in) }, + { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_3, static_cast(&USBEndpointTester::_cb_iso_out) }, + { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_3, static_cast(&USBEndpointTester::_cb_iso_in) }, +}; + +USBEndpointTester::ep_config_t USBEndpointTester::_intf_config4[NUM_ENDPOINTS] = { + { false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_4, static_cast(&USBEndpointTester::_cb_bulk_out) }, + { true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_4, static_cast(&USBEndpointTester::_cb_bulk_in) }, + { false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_4, static_cast(&USBEndpointTester::_cb_int_out) }, + { true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_4, static_cast(&USBEndpointTester::_cb_int_in) }, + { false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_4, static_cast(&USBEndpointTester::_cb_iso_out) }, + { true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_4, static_cast(&USBEndpointTester::_cb_iso_in) }, +}; + +USBEndpointTester::USBEndpointTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, + bool abort_transfer_test) : + USBDevice(phy, vendor_id, product_id, product_release), _abort_transfer_test(abort_transfer_test), _endpoint_configs( + &_intf_config_max) +{ + _cnt_cb_set_conf = 0; + _cnt_cb_set_intf = 0; + _cnt_cb_bulk_out = 0; + _cnt_cb_bulk_in = 0; + _cnt_cb_int_out = 0; + _cnt_cb_int_in = 0; + _cnt_cb_iso_out = 0; + _cnt_cb_iso_in = 0; + _num_packets_bulk_out_abort = 0; + _num_packets_bulk_in_abort = 0; + _num_packets_int_out_abort = 0; + _num_packets_int_in_abort = 0; + + EndpointResolver resolver(endpoint_table()); + resolver.endpoint_ctrl(64); + ep_config_t *epc = NULL; + for (size_t i = 0; i < NUM_ENDPOINTS; i++) { + epc = &((*_endpoint_configs)[i]); + _endpoints[i] = resolver.next_free_endpoint(epc->dir_in, epc->type, epc->max_packet); + _endpoint_buffs[i] = (uint8_t *) calloc(epc->max_packet, sizeof(uint8_t)); + MBED_ASSERT(_endpoint_buffs[i] != NULL); + } + MBED_ASSERT(resolver.valid()); + configuration_desc(0); + ctrl_buf = new uint8_t[CTRL_BUF_SIZE]; + init(); + USBDevice::connect(); + flags.wait_any(EVENT_READY, osWaitForever, false); +} + +USBEndpointTester::~USBEndpointTester() +{ + for (size_t i = 0; i < NUM_ENDPOINTS; i++) { + if (_endpoint_buffs[i] != NULL) { + free(_endpoint_buffs[i]); + } + } + deinit(); + delete[] ctrl_buf; +} + +const char *USBEndpointTester::get_desc_string(const uint8_t *desc) +{ + static char ret_string[128] = { }; + const uint8_t desc_size = desc[0] - 2; + const uint8_t *desc_str = &desc[2]; + uint32_t j = 0; + for (uint32_t i = 0; i < desc_size; i += 2, j++) { + ret_string[j] = desc_str[i]; + } + ret_string[j] = '\0'; + return ret_string; +} + +const char *USBEndpointTester::get_serial_desc_string() +{ + return get_desc_string(string_iserial_desc()); +} + +void USBEndpointTester::callback_state_change(DeviceState new_state) +{ + if (new_state == Configured) { + flags.set(EVENT_READY); + } else { + flags.clear(EVENT_READY); + } +} + +void USBEndpointTester::callback_request(const setup_packet_t *setup) +{ + /* Called in ISR context */ + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + /* Process vendor-specific requests */ + if (setup->bmRequestType.Type == VENDOR_TYPE) { + switch (setup->bRequest) { + case VENDOR_TEST_CTRL_IN: + result = Send; + data = ctrl_buf; + size = setup->wValue < CTRL_BUF_SIZE ? setup->wValue : CTRL_BUF_SIZE; + break; + case VENDOR_TEST_CTRL_OUT: + result = Receive; + data = ctrl_buf; + size = setup->wValue < 8 ? setup->wValue : 8; + break; + case VENDOR_TEST_CTRL_IN_SIZES: + result = Send; + data = ctrl_buf; + size = setup->wLength; + break; + case VENDOR_TEST_CTRL_OUT_SIZES: + result = Receive; + data = ctrl_buf; + size = setup->wValue; + break; + case VENDOR_TEST_RW_RESTART: + result = (_request_rw_restart(setup)) ? Success : Failure; + break; + case VENDOR_TEST_ABORT_BUFF_CHECK: + result = Send; + ctrl_buf[0] = _request_abort_buff_check(setup); + data = ctrl_buf; + size = 1; + break; + default: + result = PassThrough; + break; + } + } + complete_request(result, data, size); +} + +bool USBEndpointTester::_request_rw_restart(const setup_packet_t *setup) +{ + assert_locked(); + ep_config_t *epc = NULL; + for (size_t i = 0; i < NUM_ENDPOINTS; i++) { + epc = &((*_endpoint_configs)[i]); + endpoint_abort(_endpoints[i]); + if (epc->dir_in == false) { + // Wait for data on every OUT endpoint + read_start(_endpoints[i], _endpoint_buffs[i], epc->max_packet); + } + } + return true; +} + +bool USBEndpointTester::_request_abort_buff_check(const setup_packet_t *setup) +{ + assert_locked(); + if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) { + return false; + } + size_t ep_index = NUM_ENDPOINTS; + for (size_t i = 0; i < NUM_ENDPOINTS; i++) { + if (_endpoints[i] == setup->wIndex) { + ep_index = i; + break; + } + } + if (ep_index == NUM_ENDPOINTS) { + return false; + } + if (_endpoint_buffs[ep_index] == NULL) { + return false; + } + for (size_t i = 0; i < (*_endpoint_configs)[ep_index].max_packet; i++) { + if (_endpoint_buffs[ep_index][i] != EP_ABORT_BUFF_VALUE) { + return false; + } + } + return true; +} + +void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + if (aborted) { + complete_request_xfer_done(false); + return; + } + + bool result = false; + if (setup->bmRequestType.Type == VENDOR_TYPE) { + switch (setup->bRequest) { + case VENDOR_TEST_CTRL_IN: + result = true; + break; + case VENDOR_TEST_CTRL_OUT: + result = true; + break; + case VENDOR_TEST_CTRL_OUT_SIZES: + result = true; + break; + case VENDOR_TEST_CTRL_IN_SIZES: + result = true; + break; + case VENDOR_TEST_ABORT_BUFF_CHECK: + result = true; + break; + default: + result = false; + break; + } + } + complete_request_xfer_done(result); +} + +void USBEndpointTester::callback_set_configuration(uint8_t configuration) +{ + _cnt_cb_set_conf++; + if (configuration != DEFAULT_CONFIGURATION) { + complete_set_configuration(false); + return; + } + + // Configure endpoints > 0 + bool status = _setup_interface(0, 0); + complete_set_configuration(status); +} + +bool USBEndpointTester::_setup_interface(uint16_t interface, uint8_t alternate) +{ + if (interface != 0) { + return false; + } + + switch (alternate) { + case 0: + _endpoint_configs = &_intf_config0; + break; + case 1: + _endpoint_configs = &_intf_config1; + break; + case 2: + _endpoint_configs = &_intf_config2; + break; + case 3: + _endpoint_configs = &_intf_config3; + break; + case 4: + _endpoint_configs = &_intf_config4; + break; + default: + return false; + } + + _setup_non_zero_endpoints(); + + if (_abort_transfer_test && alternate >= 1) { + _num_packets_bulk_out_abort = 0; + _num_packets_bulk_in_abort = 0; + _num_packets_int_out_abort = 0; + _num_packets_int_in_abort = 0; + start_ep_in_abort_test(); + } + return true; +} + +void USBEndpointTester::_setup_non_zero_endpoints() +{ + ep_config_t *epc = NULL; + for (size_t i = 0; i < NUM_ENDPOINTS; i++) { + epc = &((*_endpoint_configs)[i]); + endpoint_add(_endpoints[i], epc->max_packet, epc->type, epc->callback); + if (epc->callback == NULL) { + continue; + } + if (epc->dir_in == false) { + // Wait for data on every OUT endpoint + read_start(_endpoints[i], _endpoint_buffs[i], epc->max_packet); + } + } +} + +void USBEndpointTester::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + _cnt_cb_set_intf++; + if (interface != 0 || alternate > 4) { + complete_set_interface(false); + return; + } + for (size_t i = 0; i < NUM_ENDPOINTS; i++) { + endpoint_abort(_endpoints[i]); + endpoint_remove(_endpoints[i]); + } + bool status = _setup_interface(interface, alternate); + complete_set_interface(status); +} + +#define CONFIG1_DESC_SIZE (CONFIGURATION_DESCRIPTOR_LENGTH \ + + 5 * (INTERFACE_DESCRIPTOR_LENGTH + NUM_ENDPOINTS * ENDPOINT_DESCRIPTOR_LENGTH) ) + +const uint8_t *USBEndpointTester::configuration_desc(uint8_t index) +{ + static const uint8_t config_1_descriptor[] = { + // configuration descriptor + CONFIGURATION_DESCRIPTOR_LENGTH, // bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(CONFIG1_DESC_SIZE), // wTotalLength (LSB) + MSB(CONFIG1_DESC_SIZE), // wTotalLength (MSB) + 1, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + NUM_ENDPOINTS, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_OUT], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config0[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config0[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_IN], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config0[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config0[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_OUT], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config0[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config0[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_IN], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config0[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config0[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_OUT], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config0[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config0[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_IN], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config0[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config0[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 1, // bAlternateSetting + NUM_ENDPOINTS, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_OUT], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config1[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config1[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_IN], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config1[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config1[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_OUT], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config1[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config1[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_IN], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config1[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config1[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_OUT], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config1[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config1[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_IN], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config1[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config1[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 2, // bAlternateSetting + NUM_ENDPOINTS, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_OUT], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config2[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config2[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_IN], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config2[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config2[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_OUT], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config2[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config2[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_IN], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config2[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config2[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_OUT], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config2[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config2[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_IN], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config2[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config2[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 3, // bAlternateSetting + NUM_ENDPOINTS, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_OUT], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config3[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config3[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_IN], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config3[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config3[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_OUT], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config3[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config3[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_IN], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config3[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config3[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_OUT], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config3[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config3[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_IN], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config3[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config3[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 4, // bAlternateSetting + NUM_ENDPOINTS, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_OUT], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config4[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config4[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_BULK_IN], // bEndpointAddress + E_BULK, // bmAttributes + (uint8_t)(LSB(_intf_config4[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config4[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB) + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_OUT], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config4[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config4[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_INT_IN], // bEndpointAddress + E_INTERRUPT, // bmAttributes + (uint8_t)(LSB(_intf_config4[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config4[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_OUT], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config4[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config4[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _endpoints[EP_ISO_IN], // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_intf_config4[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB) + (uint8_t)(MSB(_intf_config4[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB) + 1, // bInterval + }; + if (index == 0) { + return config_1_descriptor; + } else { + return NULL; + } +} + +void USBEndpointTester::_cb_bulk_out() +{ + _cnt_cb_bulk_out++; + uint32_t rx_size = read_finish(_endpoints[EP_BULK_OUT]); + if (_abort_transfer_test == false) { + // Send data back to host using the IN endpoint. + memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet); + memcpy(_endpoint_buffs[EP_BULK_IN], _endpoint_buffs[EP_BULK_OUT], rx_size); + write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], rx_size); + } else { + // Abort the transfer if enough data was received. + _num_packets_bulk_out_abort++; + if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) { + // Set every byte of the buffer to a known value. + memset(_endpoint_buffs[EP_BULK_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_BULK_OUT].max_packet); + } + read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet); + if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) { + endpoint_abort(_endpoints[EP_BULK_OUT]); + } + } +} + +void USBEndpointTester::_cb_bulk_in() +{ + _cnt_cb_bulk_in++; + write_finish(_endpoints[EP_BULK_IN]); + if (_abort_transfer_test == false) { + // Receive more data from the host using the OUT endpoint. + read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet); + } else { + _num_packets_bulk_in_abort++; + if (_num_packets_bulk_in_abort >= NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) { + return; + } + // Abort the transfer if enough data was sent. + memset(_endpoint_buffs[EP_BULK_IN], _num_packets_bulk_in_abort, (*_endpoint_configs)[EP_BULK_IN].max_packet); + write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet); + if (_num_packets_bulk_in_abort == NUM_PACKETS_UNTIL_ABORT) { + endpoint_abort(_endpoints[EP_BULK_IN]); + // Verify that buffer given in write_start is not used after the + // call to endpoint_abort(), by changing the buffer contents. + // The test will fail if the host receives new buffer content. + memset(_endpoint_buffs[EP_BULK_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_BULK_IN].max_packet); + } + } +} + +void USBEndpointTester::_cb_int_out() +{ + _cnt_cb_int_out++; + uint32_t rx_size = read_finish(_endpoints[EP_INT_OUT]); + if (_abort_transfer_test == false) { + // Send data back to host using the IN endpoint. + memset(_endpoint_buffs[EP_INT_IN], 0, (*_endpoint_configs)[EP_INT_IN].max_packet); + memcpy(_endpoint_buffs[EP_INT_IN], _endpoint_buffs[EP_INT_OUT], rx_size); + write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], rx_size); + } else { + // Abort the transfer if enough data was received. + _num_packets_int_out_abort++; + if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) { + // Set every byte of the buffer to a known value. + memset(_endpoint_buffs[EP_INT_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_INT_OUT].max_packet); + } + read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet); + if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) { + endpoint_abort(_endpoints[EP_INT_OUT]); + } + } +} + +void USBEndpointTester::_cb_int_in() +{ + _cnt_cb_int_in++; + write_finish(_endpoints[EP_INT_IN]); + if (_abort_transfer_test == false) { + // Receive more data from the host using the OUT endpoint. + read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet); + } else { + _num_packets_int_in_abort++; + if (_num_packets_int_in_abort >= NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) { + return; + } + // Abort the transfer if enough data was sent. + memset(_endpoint_buffs[EP_INT_IN], _num_packets_int_in_abort, (*_endpoint_configs)[EP_INT_IN].max_packet); + write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet); + if (_num_packets_int_in_abort == NUM_PACKETS_UNTIL_ABORT) { + endpoint_abort(_endpoints[EP_INT_IN]); + // Verify that buffer given in write_start is not used after the + // call to endpoint_abort(), by changing the buffer contents. + // The test will fail if the host receives new buffer content. + memset(_endpoint_buffs[EP_INT_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_INT_IN].max_packet); + } + } +} + +void USBEndpointTester::_cb_iso_out() +{ + _cnt_cb_iso_out++; + uint32_t rx_size = read_finish(_endpoints[EP_ISO_OUT]); + // Send data back to host using the IN endpoint. + memset(_endpoint_buffs[EP_ISO_IN], 0, (*_endpoint_configs)[EP_ISO_IN].max_packet); + memcpy(_endpoint_buffs[EP_ISO_IN], _endpoint_buffs[EP_ISO_OUT], rx_size); + write_start(_endpoints[EP_ISO_IN], _endpoint_buffs[EP_ISO_IN], rx_size); +} + +void USBEndpointTester::_cb_iso_in() +{ + _cnt_cb_iso_in++; + write_finish(_endpoints[EP_ISO_IN]); + // Receive more data from the host using the OUT endpoint. + read_start(_endpoints[EP_ISO_OUT], _endpoint_buffs[EP_ISO_OUT], (*_endpoint_configs)[EP_ISO_OUT].max_packet); +} + +void USBEndpointTester::start_ep_in_abort_test() +{ + memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet); + memset(_endpoint_buffs[EP_INT_IN], 0, (*_endpoint_configs)[EP_INT_IN].max_packet); + + write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet); + write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet); +} + +#endif //USB_DEVICE_TESTS diff --git a/drivers/tests/TESTS/usb_device/basic/USBEndpointTester.h b/drivers/tests/TESTS/usb_device/basic/USBEndpointTester.h new file mode 100644 index 0000000..cf649dd --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/USBEndpointTester.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USB_ENDPOINT_TESTER_H +#define USB_ENDPOINT_TESTER_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" +#include "events/EventQueue.h" +#include "EventFlags.h" +#include "platform/NonCopyable.h" + +#include "USBDevice.h" + +#define NUM_ENDPOINTS 6 // Not including CTRL OUT/IN + +class USBEndpointTester: public USBDevice, private mbed::NonCopyable { + +public: + USBEndpointTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, + bool abort_transfer_test); + virtual ~USBEndpointTester(); + const char *get_serial_desc_string(); + void start_ep_in_abort_test(); + + uint32_t get_cnt_cb_set_conf() const + { + return _cnt_cb_set_conf; + } + uint32_t get_cnt_cb_set_intf() const + { + return _cnt_cb_set_intf; + } + uint32_t get_cnt_cb_bulk_out() const + { + return _cnt_cb_bulk_out; + } + uint32_t get_cnt_cb_bulk_in() const + { + return _cnt_cb_bulk_in; + } + uint32_t get_cnt_cb_int_out() const + { + return _cnt_cb_int_out; + } + uint32_t get_cnt_cb_int_in() const + { + return _cnt_cb_int_in; + } + uint32_t get_cnt_cb_iso_out() const + { + return _cnt_cb_iso_out; + } + uint32_t get_cnt_cb_iso_in() const + { + return _cnt_cb_iso_in; + } + + struct ep_config_t { + bool dir_in; + usb_ep_type_t type; + uint32_t max_packet; + ep_cb_t callback; + }; + +protected: + rtos::EventFlags flags; + uint8_t *ctrl_buf; + + bool _abort_transfer_test; + usb_ep_t _endpoints[NUM_ENDPOINTS]; + uint8_t *_endpoint_buffs[NUM_ENDPOINTS]; + ep_config_t (*_endpoint_configs)[NUM_ENDPOINTS]; + + static ep_config_t _intf_config_max[NUM_ENDPOINTS]; + static ep_config_t _intf_config0[NUM_ENDPOINTS]; + static ep_config_t _intf_config1[NUM_ENDPOINTS]; + static ep_config_t _intf_config2[NUM_ENDPOINTS]; + static ep_config_t _intf_config3[NUM_ENDPOINTS]; + static ep_config_t _intf_config4[NUM_ENDPOINTS]; + + volatile uint32_t _cnt_cb_set_conf; + volatile uint32_t _cnt_cb_set_intf; + volatile uint32_t _cnt_cb_bulk_out; + volatile uint32_t _cnt_cb_bulk_in; + volatile uint32_t _cnt_cb_int_out; + volatile uint32_t _cnt_cb_int_in; + volatile uint32_t _cnt_cb_iso_out; + volatile uint32_t _cnt_cb_iso_in; + + volatile uint32_t _num_packets_bulk_out_abort; + volatile uint32_t _num_packets_bulk_in_abort; + volatile uint32_t _num_packets_int_out_abort; + volatile uint32_t _num_packets_int_in_abort; + + virtual const uint8_t *configuration_desc(uint8_t index); + virtual void callback_state_change(DeviceState new_state); + virtual void callback_request(const setup_packet_t *setup); + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + + void _setup_non_zero_endpoints(); + bool _setup_interface(uint16_t interface, uint8_t alternate); + + virtual void _cb_bulk_out(); + virtual void _cb_bulk_in(); + virtual void _cb_int_out(); + virtual void _cb_int_in(); + virtual void _cb_iso_out(); + virtual void _cb_iso_in(); + +private: + const char *get_desc_string(const uint8_t *desc); + bool _request_rw_restart(const setup_packet_t *setup); + bool _request_abort_buff_check(const setup_packet_t *setup); +}; + +#endif diff --git a/drivers/tests/TESTS/usb_device/basic/USBTester.cpp b/drivers/tests/TESTS/usb_device/basic/USBTester.cpp new file mode 100644 index 0000000..743f217 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/USBTester.cpp @@ -0,0 +1,688 @@ +/* + * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if USB_DEVICE_TESTS + +#include "stdint.h" +#include "USBTester.h" +#include "events/mbed_shared_queues.h" +#include "EndpointResolver.h" + +#define DEFAULT_CONFIGURATION (1) +#define LAST_CONFIGURATION (2) + +#define VENDOR_TEST_CTRL_IN 1 +#define VENDOR_TEST_CTRL_OUT 2 +#define VENDOR_TEST_CTRL_IN_SIZES 3 +#define VENDOR_TEST_CTRL_OUT_SIZES 4 + +#define MAX_EP_SIZE 64 +#define MIN_EP_SIZE 8 + +#define CTRL_BUF_SIZE (2048) + +#define EVENT_READY (1 << 0) + +USBTester::USBTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBDevice(phy, vendor_id, product_id, product_release), interface_0_alt_set(NONE), + interface_1_alt_set(NONE), configuration_set(NONE), reset_count(0), + suspend_count(0), resume_count(0) +{ + + EndpointResolver resolver(endpoint_table()); + + resolver.endpoint_ctrl(64); + bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, 64); + bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, 64); + int_in = resolver.endpoint_in(USB_EP_TYPE_INT, 64); + int_out = resolver.endpoint_out(USB_EP_TYPE_INT, 64); + MBED_ASSERT(resolver.valid()); + configuration_desc(0); + ctrl_buf = new uint8_t[CTRL_BUF_SIZE]; + init(); + USBDevice::connect(); + flags.wait_any(EVENT_READY, osWaitForever, false); + +} + +USBTester::~USBTester() +{ + deinit(); + delete[] ctrl_buf; +} + + +const char *USBTester::get_desc_string(const uint8_t *desc) +{ + static char ret_string[128] = { }; + const uint8_t desc_size = desc[0] - 2; + const uint8_t *desc_str = &desc[2]; + uint32_t j = 0; + for (uint32_t i = 0; i < desc_size; i += 2, j++) { + ret_string[j] = desc_str[i]; + } + ret_string[j] = '\0'; + return ret_string; +} + +void USBTester::suspend(bool suspended) +{ + if (suspended) { + ++suspend_count; + } else { + ++resume_count; + } +} + +const char *USBTester::get_serial_desc_string() +{ + return get_desc_string(string_iserial_desc()); +} + +const char *USBTester::get_iinterface_desc_string() +{ + return get_desc_string(string_iserial_desc()); +} + +const char *USBTester::get_iproduct_desc_string() +{ + return get_desc_string(string_iserial_desc()); +} + +void USBTester::callback_state_change(DeviceState new_state) +{ + if (new_state == Configured) { + flags.set(EVENT_READY); + } else { + flags.clear(EVENT_READY); + configuration_set = NONE; + interface_0_alt_set = NONE; + interface_1_alt_set = NONE; + } +} + +void USBTester::callback_reset() +{ + ++reset_count; +} + +void USBTester::callback_request(const setup_packet_t *setup) +{ + /* Called in ISR context */ + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + /* Process vendor-specific requests */ + if (setup->bmRequestType.Type == VENDOR_TYPE) { + switch (setup->bRequest) { + case VENDOR_TEST_CTRL_IN: + result = Send; + data = ctrl_buf; + size = setup->wValue < CTRL_BUF_SIZE ? setup->wValue : CTRL_BUF_SIZE; + break; + case VENDOR_TEST_CTRL_OUT: + result = Receive; + data = ctrl_buf; + size = setup->wValue < 8 ? setup->wValue : 8; + break; + case VENDOR_TEST_CTRL_IN_SIZES: + result = Send; + data = ctrl_buf; + size = setup->wLength; + break; + case VENDOR_TEST_CTRL_OUT_SIZES: + result = Receive; + data = ctrl_buf; + size = setup->wValue; + break; + default: + result = PassThrough; + break; + } + } + + complete_request(result, data, size); +} + +void USBTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + if (aborted) { + complete_request_xfer_done(false); + return; + } + + bool result = false; + if (setup->bmRequestType.Type == VENDOR_TYPE) { + switch (setup->bRequest) { + case VENDOR_TEST_CTRL_IN: + result = true; + break; + case VENDOR_TEST_CTRL_OUT: + result = true; + break; + case VENDOR_TEST_CTRL_OUT_SIZES: + result = true; + break; + case VENDOR_TEST_CTRL_IN_SIZES: + result = true; + break; + default: + result = false; + break; + } + } + complete_request_xfer_done(result); +} + +// Called in ISR context +// Set configuration. Return false if the +// configuration is not supported. +void USBTester::callback_set_configuration(uint8_t configuration) +{ + bool ret = false; + + if (configuration >= DEFAULT_CONFIGURATION && configuration <= LAST_CONFIGURATION) { + endpoint_remove_all(); + ret = set_configuration(configuration); + } + + complete_set_configuration(ret); +} + +bool USBTester::setup_iterface(uint8_t ep_in, uint8_t ep_out, uint32_t ep_size, usb_ep_type_t ep_type, + uint8_t *buf, uint32_t buf_size, void (USBTester::*callback)()) +{ + bool success = false; + + success = endpoint_add(ep_in, ep_size, ep_type); + success &= endpoint_add(ep_out, ep_size, ep_type, callback); + success &= read_start(ep_out, buf, buf_size); + return success; +} + +void USBTester::remove_iterface(uint16_t interface) +{ + if (configuration_set == 1) { + if (interface == 0) { + endpoint_remove(bulk_in); + endpoint_remove(bulk_out); + interface_0_alt_set = NONE; + } + if (interface == 1) { + endpoint_remove(int_in); + endpoint_remove(int_out); + interface_1_alt_set = NONE; + } + } + if (configuration_set == 2) { + if (interface == 0) { + endpoint_remove(int_in); + endpoint_remove(int_out); + interface_0_alt_set = NONE; + } + if (interface == 1) { + endpoint_remove(bulk_in); + endpoint_remove(bulk_out); + interface_1_alt_set = NONE; + } + } +} + +bool USBTester::set_configuration(uint16_t configuration) +{ + bool success = false; + // set 0 alt setting for each interface + if (configuration == 1) { + // interface 0 alternate 0 + success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, + bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); + // interface 1 alternate 0 + success &= setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, + int_buf, sizeof(int_buf), &USBTester::epint_out_callback); + } else if (configuration == 2) { + // interface 0 alternate 0 + success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, + int_buf, sizeof(int_buf), &USBTester::epint_out_callback); + // interface 1 alternate 0 + success &= setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, + bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); + } + if (success) { + configuration_set = configuration; + interface_0_alt_set = interface_1_alt_set = 0; + } + return success; +} + +void USBTester::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + complete_set_interface(set_interface(interface, alternate)); +} + +bool USBTester::set_interface(uint16_t interface, uint16_t alternate) +{ + bool success = false; + + if (interface == 0) { + if (configuration_set == 1) { + if (alternate == 0) { + remove_iterface(interface); + success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, + bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); + } + if (alternate == 1) { + remove_iterface(interface); + success = setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, + bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); + } + } + if (configuration_set == 2) { + if (alternate == 0) { + remove_iterface(interface); + success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, + int_buf, sizeof(int_buf), &USBTester::epint_out_callback); + } + if (alternate == 1) { + remove_iterface(interface); + success = setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, + int_buf, sizeof(int_buf), &USBTester::epint_out_callback); + } + } + if (success) { + interface_0_alt_set = alternate; + } + } + if (interface == 1) { + if (configuration_set == 1) { + if (alternate == 0) { + remove_iterface(interface); + success = setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, + int_buf, sizeof(int_buf), &USBTester::epint_out_callback); + } + if (alternate == 1) { + remove_iterface(interface); + success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, + int_buf, sizeof(int_buf), &USBTester::epint_out_callback); + } + } + if (configuration_set == 2) { + if (alternate == 0) { + remove_iterface(interface); + success = setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, + bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); + } + if (alternate == 1) { + remove_iterface(interface); + success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, + bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); + } + } + if (success) { + interface_1_alt_set = alternate; + } + } + return success; +} + +const uint8_t *USBTester::device_desc() +{ + uint8_t ep0_size = endpoint_max_packet_size(0x00); + uint8_t device_descriptor_temp[] = { + 18, // bLength + 1, // bDescriptorType + 0x10, 0x01, // bcdUSB + 0, // bDeviceClass + 0, // bDeviceSubClass + 0, // bDeviceProtocol + ep0_size, // bMaxPacketSize0 + (uint8_t)(LSB(vendor_id)), (uint8_t)(MSB(vendor_id)), // idVendor + (uint8_t)(LSB(product_id)), (uint8_t)(MSB(product_id)),// idProduct + 0x00, 0x01, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 2 // bNumConfigurations + }; + MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); + memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); + return device_descriptor; +} + +const uint8_t *USBTester::string_iinterface_desc() +{ + static const uint8_t string_iinterface_descriptor[] = { + 0x08, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, + }; + return string_iinterface_descriptor; +} + +const uint8_t *USBTester::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 0x22, + STRING_DESCRIPTOR, + 'M', 0, 'B', 0, 'E', 0, 'D', 0, ' ', 0, + 'T', 0, 'E', 0, 'S', 0, 'T', 0, ' ', 0, + 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0, + }; + return string_iproduct_descriptor; +} + + +#define CONFIG_1_DESC_SIZE (9+9+7+7 + 9+7+7 + 9+7+7 + 9+7+7) +#define CONFIG_2_DESC_SIZE (9+9+7+7 + 9+7+7 + 9+7+7 + 9+7+7) + +const uint8_t *USBTester::configuration_desc(uint8_t index) +{ + static const uint8_t config_1_descriptor[] = { + // configuration descriptor + CONFIGURATION_DESCRIPTOR_LENGTH,// bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(CONFIG_1_DESC_SIZE), // wTotalLength + MSB(CONFIG_1_DESC_SIZE), + 2, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // Interface 0 setting 0 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 0 setting 1 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 1 setting 0 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 1, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 1 setting 1 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 1, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1 // bInterval + }; + + static const uint8_t config_2_descriptor[] = { + // configuration descriptor + CONFIGURATION_DESCRIPTOR_LENGTH,// bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(CONFIG_2_DESC_SIZE), // wTotalLength + MSB(CONFIG_2_DESC_SIZE), + 2, // bNumInterfaces + 2, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // Interface 0 setting 0 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 0 setting 1 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 1 setting 0 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 1, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 1 setting 1 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + INTERFACE_DESCRIPTOR_LENGTH,// bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 1, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1 // bInterval + }; + + if (index == 0) { + return config_1_descriptor; + } else if (index == 1) { + return config_2_descriptor; + } else { + return NULL; + } +} + +void USBTester::epint_out_callback() +{ + read_finish(int_out); + read_start(int_out, int_buf, sizeof(int_buf)); +} +void USBTester::epbulk_out_callback() +{ + read_finish(bulk_out); + read_start(bulk_out, bulk_buf, sizeof(bulk_buf)); +} + +#endif //USB_DEVICE_TESTS diff --git a/drivers/tests/TESTS/usb_device/basic/USBTester.h b/drivers/tests/TESTS/usb_device/basic/USBTester.h new file mode 100644 index 0000000..d131ab3 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/USBTester.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USB_TESTER_H +#define USB_TESTER_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" +#include "events/EventQueue.h" +#include "EventFlags.h" +#include "platform/NonCopyable.h" + +#include "USBDevice.h" + +class USBTester: public USBDevice, private mbed::NonCopyable { +public: + + /* + * Constructor + * + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); + + virtual ~USBTester(); + + /* + * + * @returns descriptor string in ASCII + */ + const char *get_serial_desc_string(); + const char *get_iproduct_desc_string(); + const char *get_iinterface_desc_string(); + uint32_t get_reset_count() const + { + return reset_count; + } + uint32_t get_suspend_count() const + { + return suspend_count; + } + uint32_t get_resume_count() const + { + return resume_count; + } + void clear_reset_count() + { + reset_count = 0; + } + void clear_suspend_count() + { + suspend_count = 0; + } + void clear_resume_count() + { + resume_count = 0; + } + +private: + + enum { NONE = -1 }; + const char *get_desc_string(const uint8_t *desc); + virtual void suspend(bool suspended); + bool set_configuration(uint16_t configuration); + bool set_interface(uint16_t interface, uint16_t alternate); + bool setup_iterface(uint8_t ep_in, uint8_t ep_out, uint32_t ep_size, usb_ep_type_t ep_type, + uint8_t *buf, uint32_t buf_size, void (USBTester::*callback)()); + void remove_iterface(uint16_t interface); + int16_t interface_0_alt_set; + int16_t interface_1_alt_set; + int16_t configuration_set; + +protected: + + /* + * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the device descriptor + */ + virtual const uint8_t *device_desc(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual const uint8_t *string_iproduct_desc(); + + /* + * Get string interface descriptor + * + * @returns pointer to the string interface descriptor + */ + virtual const uint8_t *string_iinterface_desc(); + + /* + * Get configuration descriptor + * + * @param index descriptor index + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index); + +protected: + uint8_t bulk_in; + uint8_t bulk_out; + uint8_t bulk_buf[64]; + uint8_t int_in; + uint8_t int_out; + uint8_t int_buf[64]; + rtos::EventFlags flags; + volatile uint32_t reset_count; + volatile uint32_t suspend_count; + volatile uint32_t resume_count; + + virtual void callback_state_change(DeviceState new_state); + virtual void callback_request(const setup_packet_t *setup); + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + virtual void epbulk_out_callback(); + virtual void epint_out_callback(); + virtual void callback_reset(); + uint8_t *ctrl_buf; + +}; + +#endif diff --git a/drivers/tests/TESTS/usb_device/basic/main.cpp b/drivers/tests/TESTS/usb_device/basic/main.cpp new file mode 100644 index 0000000..2667af2 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/main.cpp @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !USB_DEVICE_TESTS +#error [NOT_SUPPORTED] usb device tests not enabled +#else + +#include +#include +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest/utest.h" + +#include "USBTester.h" +#include "USBEndpointTester.h" +#include "usb_phy_api.h" + +// TODO +// suspend resume test: implement host side USB suspend/resume +// sync frame test: add test on isochronous endpoint + +// Uncomment/remove this when host suspend_resume_test part will be implemented +//#define SUSPEND_RESUME_TEST_SUPPORTED + +// If disconnect() + connect() occur too fast the reset event will be dropped. +// At a minimum there should be a 200us delay between disconnect and connect. +// To be on the safe side I would recommend a 1ms delay, so the host controller +// has an entire USB frame to detect the disconnect. +#define MIN_DISCONNECT_TIME_US 1000 + +#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#error [NOT_SUPPORTED] USB Device not supported for this target +#else + + +using namespace utest::v1; + +// Print callback counters for endpoint tests +#define EP_DBG 0 + +static USBPhy *get_phy() +{ + return get_usb_phy(); +} + + +/** Control basic tests + + Test device configuration/deconfiguration + Given an initialized USB (HOST <---> DUT connection established) + When device configuration is checked just after initialization + Then get_configuration returns 1 (default configuration is set) + When device is deconfigured + Then get_configuration returns 0 (no configuration is set) + When each from supported configurations is set + Then the configuration is set correctly + + Test device interface setting + Given an initialized USB (HOST <---> DUT connection established) + When each altsetting from every supported configuration is set + Then the interface altsetting is set correctly + + Test device/interface/endpoint status + Given an initialized USB (HOST <---> DUT connection established) + When device status is checked + Then status is within allowed values (see status bits description below) + When control endpoint status is checked + Then control endpoint status is 0 + When status of each interface from every supported configuration is checked + Then interface status is 0 + When status of each endpoint in every allowed device interface/configuration combination is checked + Then endpoint status is 0 (not halted) + + Test set/clear feature on device/interface/endpoint + Given an initialized USB (HOST <---> DUT connection established) + When for each endpoint in every allowed interface/configuration combination the feature is set and then cleared + Then selected feature is set/cleared accordingly + + Test device/configuration/interface/endpoint descriptors + Given an initialized USB (HOST <---> DUT connection established) + When device descriptor is read + Then the descriptor content is valid + When configuration descriptor is read + Then the descriptor content is valid + When interface descriptor is read + Then the error is thrown since it is not directly accessible + When endpoint descriptor is read + Then the error is thrown since it is not directly accessible + + Test descriptor setting + Given an initialized USB (HOST <---> DUT connection established) + When device descriptor is to be set + Then error is thrown since descriptor setting command is not supported by Mbed +*/ +void control_basic_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + char str[128] = {}; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + sprintf(str, "%s %d %d", serial.get_serial_desc_string(), vendor_id, product_id); + greentea_send_kv("control_basic_test", str); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + + +/** Test control endpoint stall on invalid request + + Given an initialized USB (HOST <---> DUT connection established) + When unsupported request to control endpoint is to be sent + Then the endpoint is stalled and error is thrown + +*/ +void control_stall_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + greentea_send_kv("control_stall_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + + +/** Test various data sizes in control transfer + + Given an initialized USB (HOST <---> DUT connection established) + When control data in each tested size is sent + Then read data should match sent data + +*/ +void control_sizes_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + greentea_send_kv("control_sizes_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + + +/** Test various patterns of control transfers + + Given an initialized USB (HOST <---> DUT connection established) + When stress control transfer with a data in stage is performed + Then transfer ends with success + When stress control transfer with a data out stage followed by a control transfer with a data in stage is performed + Then transfer ends with success + When stress control transfer with a data out stage is performed + Then transfer ends with success + +*/ +void control_stress_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + greentea_send_kv("control_stress_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + +/** Test data correctness for every OUT/IN endpoint pair + * + * Given a USB device with multiple OUT/IN endpoint pairs + * When the host sends random payloads up to wMaxPacketSize in size + * to an OUT endpoint of the device, + * and then the device sends data back to host using an IN endpoint + * Then data sent and received by host is equal for every endpoint pair + */ +void ep_test_data_correctness() +{ + 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_correctness", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); +#if EP_DBG + ThisThread::sleep_for(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 endpoint halt for every OUT/IN endpoint pair + * + * Given a USB device with multiple OUT/IN endpoint pairs + * When the host issues an endpoint halt control request at a random point + * of OUT or IN transfer + * Then the endpoint is stalled and all further transfers fail + */ +void ep_test_halt() +{ + 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_halt", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); +#if EP_DBG + ThisThread::sleep_for(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 simultaneous data transfers for multiple OUT/IN endpoint pairs + * + * Given a USB device with multiple OUT/IN endpoint pairs + * When multiple OUT and IN endpoints are used to transfer random data in parallel + * Then all transfers succeed + * and for every endpoint pair, data received by host equals data sent by host + */ +void ep_test_parallel_transfers() +{ + 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_parallel_transfers", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); +#if EP_DBG + ThisThread::sleep_for(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 simultaneous data transfers in parallel with control transfers + * + * Given a USB device with multiple OUT/IN endpoint pairs + * When multiple OUT and IN endpoints are used to transfer random data + * and control requests are processed in parallel + * Then all transfers succeed + * and for every endpoint pair, data received by host equals data sent by host + */ +void ep_test_parallel_transfers_ctrl() +{ + 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_parallel_transfers_ctrl", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); +#if EP_DBG + ThisThread::sleep_for(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 aborting data transfer for every OUT/IN endpoint pair + * + * Given a USB device with multiple OUT/IN endpoint pairs + * When a device aborts an in progress data transfer + * Then no more data is transmitted + * and endpoint buffer is correctly released on the device end + */ +void ep_test_abort() +{ + 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, true); + greentea_send_kv("ep_test_abort", serial.get_serial_desc_string()); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); +#if EP_DBG + ThisThread::sleep_for(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 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 + ThisThread::sleep_for(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) + When USB device is reset repeatedly + Then the USB is operational with no errors + +*/ +void device_reset_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + + greentea_send_kv("reset_support", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + if (strcmp(_value, "false") != 0) { + + USBTester serial(get_phy(), vendor_id, product_id, product_release); + serial.clear_reset_count(); + greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); + while (serial.get_reset_count() == 0); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + + while (!serial.configured()); + + serial.clear_reset_count(); + greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); + while (serial.get_reset_count() == 0); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + + while (!serial.configured()); + + serial.clear_reset_count(); + greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); + while (serial.get_reset_count() == 0); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + + while (!serial.configured()); + + greentea_send_kv("device_reset_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + + +/** Test USB implementation against repeated reconnection + + Given an initialized USB (HOST <---> DUT connection established) + When USB device is disconnected and then connected repeatedly + Then the USB is operational with no errors + +*/ +void device_soft_reconnection_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + const uint32_t reconnect_try_count = 3; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + + greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + + for (int i = 0; i < reconnect_try_count; i++) { + serial.disconnect(); + wait_us(MIN_DISCONNECT_TIME_US); + serial.connect(); + greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } + + serial.disconnect(); + wait_us(MIN_DISCONNECT_TIME_US); + serial.connect(); + serial.disconnect(); + wait_us(MIN_DISCONNECT_TIME_US); + serial.connect(); + serial.disconnect(); + wait_us(MIN_DISCONNECT_TIME_US); + serial.connect(); + greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + + +#if SUSPEND_RESUME_TEST_SUPPORTED +/** Test USB implementation against repeated suspend and resume + + Given an initialized USB (HOST <---> DUT connection established) + When USB device is suspended and then resumed repeatedly + Then the USB is operational with no errors + +*/ +void device_suspend_resume_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + greentea_send_kv("device_suspend_resume_test", serial.get_serial_desc_string()); + printf("[1] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count()); + serial.clear_suspend_count(); + serial.clear_resume_count(); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + printf("[2] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count()); + TEST_ASSERT_EQUAL_STRING("pass", _key); + ThisThread::sleep_for(5000); + printf("[3] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count()); + } +} +#endif + + +/** Test USB implementation against repeated initialization and deinitialization + + Given an initialized USB (HOST <---> DUT connection established) + When USB device is deinitialized and then initialized repeatedly + Then the USB is operational with no errors + +*/ +void repeated_construction_destruction_test() +{ + uint16_t vendor_id = 0x0d28; + uint16_t product_id = 0x0205; + uint16_t product_release = 0x0001; + char _key[11] = {}; + char _value[128] = {}; + + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + TEST_ASSERT_EQUAL(true, serial.configured()); + } + + wait_us(MIN_DISCONNECT_TIME_US); + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + TEST_ASSERT_EQUAL(true, serial.configured()); + } + + wait_us(MIN_DISCONNECT_TIME_US); + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + TEST_ASSERT_EQUAL(true, serial.configured()); + } + + wait_us(MIN_DISCONNECT_TIME_US); + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + TEST_ASSERT_EQUAL(true, serial.configured()); + greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } + + wait_us(MIN_DISCONNECT_TIME_US); + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + TEST_ASSERT_EQUAL(true, serial.configured()); + greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } + + wait_us(MIN_DISCONNECT_TIME_US); + { + USBTester serial(get_phy(), vendor_id, product_id, product_release); + TEST_ASSERT_EQUAL(true, serial.configured()); + greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string()); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("pass", _key); + } +} + +Case cases[] = { + Case("usb control basic test", control_basic_test), + Case("usb control stall test", control_stall_test), + Case("usb control sizes test", control_sizes_test), + Case("usb control stress test", control_stress_test), + Case("usb device reset test", device_reset_test), + Case("usb soft reconnection test", device_soft_reconnection_test), +#if SUSPEND_RESUME_TEST_SUPPORTED + Case("usb device suspend/resume test", device_suspend_resume_test), +#endif + Case("usb repeated construction destruction test", repeated_construction_destruction_test), + Case("endpoint test data correctness", ep_test_data_correctness), + 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 data toggle reset", ep_test_data_toggle) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(180, "pyusb_basic"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#endif // !defined(USB_DEVICE_TESTS) diff --git a/drivers/tests/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg b/drivers/tests/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg new file mode 100644 index 0000000..71e084a --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg @@ -0,0 +1,6 @@ +# Zadig device configuration +# Mbed OS USB test device -- basic tests +[device] +Description = "MBED TEST DEVICE" +VID = 0x0D28 +PID = 0x0205 diff --git a/drivers/tests/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg b/drivers/tests/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg new file mode 100644 index 0000000..80caf34 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg @@ -0,0 +1,6 @@ +# Zadig device configuration +# Mbed OS USB test device -- endpoint tests +[device] +Description = "USB DEVICE" +VID = 0x0D28 +PID = 0x0206 diff --git a/drivers/tests/TESTS/usb_device/hid/main.cpp b/drivers/tests/TESTS/usb_device/hid/main.cpp new file mode 100644 index 0000000..551bac2 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/hid/main.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !USB_DEVICE_TESTS +#error [NOT_SUPPORTED] usb device tests not enabled +#else + +#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#error [NOT_SUPPORTED] USB Device not supported for this target +#else + +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "mbed.h" +#include +#include "usb_phy_api.h" +#include "USBHID.h" +#include "USBMouse.h" +#include "USBKeyboard.h" +#include "hal/us_ticker_api.h" + +// Reuse the VID & PID from basic USB test. +#define USB_HID_VID 0x0d28 +#define USB_HID_PID_GENERIC 0x0206 +#define USB_HID_PID_KEYBOARD 0x0206 +#define USB_HID_PID_MOUSE 0x0206 +#define USB_HID_PID_GENERIC2 0x0007 + +#define MSG_VALUE_LEN 24 +#define MSG_KEY_LEN 24 +#define MSG_KEY_DEVICE_READY "dev_ready" +#define MSG_KEY_HOST_READY "host_ready" +#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn" +#define MSG_KEY_TEST_GET_DESCRIPTOR_HID "test_get_desc_hid" +#define MSG_KEY_TEST_GET_DESCRIPTOR_CFG "test_get_desc_cfg" +#define MSG_KEY_TEST_REQUESTS "test_requests" +#define MSG_KEY_TEST_RAW_IO "test_raw_io" + +#define MSG_KEY_TEST_CASE_FAILED "fail" +#define MSG_KEY_TEST_CASE_PASSED "pass" +#define MSG_VALUE_DUMMY "0" +#define MSG_VALUE_NOT_SUPPORTED "not_supported" + +#define RAW_IO_REPS 16 + +#define USB_DEV_SN_LEN (32) // 32 hex digit UUID +#define NONASCII_CHAR ('?') +#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2) + +const char *default_serial_num = "0123456789"; +char usb_dev_sn[USB_DEV_SN_LEN + 1]; + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +/** + * Convert a USB string descriptor to C style ASCII + * + * The string placed in str is always null-terminated which may cause the + * loss of data if n is to small. If the length of descriptor string is less + * than n, additional null bytes are written to str. + * + * @param str output buffer for the ASCII string + * @param usb_desc USB string descriptor + * @param n size of str buffer + * @returns number of non-null bytes returned in str or -1 on failure + */ +int usb_string_desc2ascii(char *str, const uint8_t *usb_desc, size_t n) +{ + if (str == NULL || usb_desc == NULL || n < 1) { + return -1; + } + // bDescriptorType @ offset 1 + if (usb_desc[1] != STRING_DESCRIPTOR) { + return -1; + } + // bLength @ offset 0 + const size_t bLength = usb_desc[0]; + if (bLength % 2 != 0) { + return -1; + } + size_t s, d; + for (s = 0, d = 2; s < n - 1 && d < bLength; s++, d += 2) { + // handle non-ASCII characters + if (usb_desc[d] > 0x7f || usb_desc[d + 1] != 0) { + str[s] = NONASCII_CHAR; + } else { + str[s] = usb_desc[d]; + } + } + int str_len = s; + for (; s < n; s++) { + str[s] = '\0'; + } + return str_len; +} + +/** + * Convert a C style ASCII to a USB string descriptor + * + * @param usb_desc output buffer for the USB string descriptor + * @param str ASCII string + * @param n size of usb_desc buffer, even number + * @returns number of bytes returned in usb_desc or -1 on failure + */ +int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n) +{ + if (str == NULL || usb_desc == NULL || n < 4) { + return -1; + } + if (n % 2 != 0) { + return -1; + } + size_t s, d; + // set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string + memset(usb_desc, 0, n); + for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) { + usb_desc[d] = str[s]; + } + // set bLength @ offset 0 + usb_desc[0] = d; + // set bDescriptorType @ offset 1 + usb_desc[1] = STRING_DESCRIPTOR; + return d; +} + +class TestUSBHID: public USBHID { +private: + uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; +public: + TestUSBHID(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num, uint8_t output_report_length = 64, uint8_t input_report_length = 64) : + USBHID(get_usb_phy(), output_report_length, input_report_length, vendor_id, product_id, 0x01) + { + init(); + int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); + if (rc < 0) { + ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); + } + } + + virtual ~TestUSBHID() + { + deinit(); + } + + virtual const uint8_t *string_iserial_desc() + { + return (const uint8_t *) _serial_num_descriptor; + } + + // Make this accessible for tests (public). + using USBHID::report_desc_length; +}; + +class TestUSBMouse: public USBMouse { +private: + uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; +public: + TestUSBMouse(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) : + USBMouse(get_usb_phy(), REL_MOUSE, vendor_id, product_id, 0x01) + { + init(); + int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); + if (rc < 0) { + ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); + } + } + + virtual ~TestUSBMouse() + { + deinit(); + } + + virtual const uint8_t *string_iserial_desc() + { + return (const uint8_t *) _serial_num_descriptor; + } + + // Make this accessible for tests (public). + using USBHID::report_desc_length; +}; + +class TestUSBKeyboard: public USBKeyboard { +private: + uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; +public: + TestUSBKeyboard(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) : + USBKeyboard(get_usb_phy(), vendor_id, product_id, 0x01) + { + init(); + int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); + if (rc < 0) { + ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); + } + } + + virtual ~TestUSBKeyboard() + { + deinit(); + } + + virtual const uint8_t *string_iserial_desc() + { + return (const uint8_t *) _serial_num_descriptor; + } + + // Make this accessible for tests (public). + using USBHID::report_desc_length; +}; + +/** Test Get_Descriptor request with the HID class descriptors + * + * Given a USB HID class device connected to a host, + * when the host issues the Get_Descriptor(HID) request, + * then the device returns the HID descriptor. + * + * When the host issues the Get_Descriptor(Report) request, + * then the device returns the Report descriptor + * and the size of the descriptor is equal to USBHID::report_desc_length(). + * + * Details in USB Device Class Definition for HID, v1.11, paragraph 7.1. + */ +template +void test_get_hid_class_desc() +{ + T usb_hid(USB_HID_VID, PID, usb_dev_sn); + usb_hid.connect(); + greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_HID, MSG_VALUE_DUMMY); + + char key[MSG_KEY_LEN + 1] = { }; + char value[MSG_VALUE_LEN + 1] = { }; + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key); + uint16_t host_report_desc_len; + int num_args = sscanf(value, "%04hx", &host_report_desc_len); + TEST_ASSERT_MESSAGE(num_args != 0 && num_args != EOF, "Invalid data received from host."); + TEST_ASSERT_EQUAL_UINT16(usb_hid.report_desc_length(), host_report_desc_len); +} + +/** Test Get_Descriptor request with the Configuration descriptor + * + * Given a USB HID class device connected to a host, + * when the host issues the Get_Descriptor(Configuration) request, + * then the device returns the Configuration descriptor and a HID + * descriptor for each HID interface. + * + * Details in USB Device Class Definition for HID, v1.11, paragraph 7.1. + */ +template +void test_get_configuration_desc() +{ + T usb_hid(USB_HID_VID, PID, usb_dev_sn); + usb_hid.connect(); + greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_CFG, MSG_VALUE_DUMMY); + + char key[MSG_KEY_LEN + 1] = { }; + char value[MSG_VALUE_LEN + 1] = { }; + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key); +} + +/** Test HID class requests + * + * Given a USB HID class device connected to a host, + * when the host issues a request specific to the HID class device type, + * then the device returns valid data. + * + * Details in USB Device Class Definition for HID, v1.11, + * paragraph 7.2 and Appendix G. + */ +template +void test_class_requests() +{ + T usb_hid(USB_HID_VID, PID, usb_dev_sn); + usb_hid.connect(); + greentea_send_kv(MSG_KEY_TEST_REQUESTS, MSG_VALUE_DUMMY); + + char key[MSG_KEY_LEN + 1] = { }; + char value[MSG_VALUE_LEN + 1] = { }; + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key); +} + +/** Test send & read + * + * Given a USB HID class device connected to a host, + * when the device sends input reports with a random data to the host + * and the host sends them back to the device, + * then received output report data is equal to the input report data. + */ +template // Range [1, MAX_HID_REPORT_SIZE]. +void test_generic_raw_io() +{ + TestUSBHID usb_hid(USB_HID_VID, USB_HID_PID_GENERIC2, usb_dev_sn, REPORT_SIZE, REPORT_SIZE); + usb_hid.connect(); + greentea_send_kv(MSG_KEY_TEST_RAW_IO, REPORT_SIZE); + + // Wait for the host HID driver to complete setup. + char key[MSG_KEY_LEN + 1] = { }; + char value[MSG_VALUE_LEN + 1] = { }; + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + TEST_ASSERT_EQUAL_STRING(MSG_KEY_HOST_READY, key); + if (strcmp(value, MSG_VALUE_NOT_SUPPORTED) == 0) { + TEST_IGNORE_MESSAGE("Test case not supported by host plarform."); + return; + } + + // Report ID omitted here. There are no Report ID tags in the Report descriptor. + HID_REPORT input_report = {}; + HID_REPORT output_report = {}; + for (size_t r = 0; r < RAW_IO_REPS; r++) { + for (size_t i = 0; i < REPORT_SIZE; i++) { + input_report.data[i] = (uint8_t)(rand() % 0x100); + } + input_report.length = REPORT_SIZE; + output_report.length = 0; + TEST_ASSERT(usb_hid.send(&input_report)); + TEST_ASSERT(usb_hid.read(&output_report)); + TEST_ASSERT_EQUAL_UINT32(input_report.length, output_report.length); + TEST_ASSERT_EQUAL_UINT8_ARRAY(input_report.data, output_report.data, REPORT_SIZE); + } +} + +utest::v1::status_t testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(45, "usb_device_hid"); + srand((unsigned) ticker_read_us(get_us_ticker_data())); + + utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases); + if (status != utest::v1::STATUS_CONTINUE) { + return status; + } + + char key[MSG_KEY_LEN + 1] = { }; + char usb_dev_uuid[USB_DEV_SN_LEN + 1] = { }; + + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); + greentea_parse_kv(key, usb_dev_uuid, MSG_KEY_LEN, USB_DEV_SN_LEN + 1); + + if (strcmp(key, MSG_KEY_SERIAL_NUMBER) != 0) { + utest_printf("Invalid message key.\n"); + return utest::v1::STATUS_ABORT; + } + + strncpy(usb_dev_sn, usb_dev_uuid, USB_DEV_SN_LEN + 1); + return status; +} + +Case cases[] = { + Case("Configuration descriptor, generic", test_get_configuration_desc), + Case("Configuration descriptor, keyboard", test_get_configuration_desc), + Case("Configuration descriptor, mouse", test_get_configuration_desc), + + Case("HID class descriptors, generic", test_get_hid_class_desc), + Case("HID class descriptors, keyboard", test_get_hid_class_desc), + Case("HID class descriptors, mouse", test_get_hid_class_desc), + + // HID class requests not supported by Mbed + // Case("HID class requests, generic", test_class_requests), + // Case("HID class requests, keyboard", test_class_requests), + // Case("HID class requests, mouse", test_class_requests), + + Case("Raw input/output, 1-byte reports", test_generic_raw_io<1>), + Case("Raw input/output, 20-byte reports", test_generic_raw_io<20>), + Case("Raw input/output, 64-byte reports", test_generic_raw_io<64>), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#endif // !defined(USB_DEVICE_TESTS) diff --git a/drivers/tests/TESTS/usb_device/hid/requirements.txt b/drivers/tests/TESTS/usb_device/hid/requirements.txt new file mode 100644 index 0000000..f9453c9 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/hid/requirements.txt @@ -0,0 +1 @@ +hidapi>=0.7.99,<0.8.0 diff --git a/drivers/tests/TESTS/usb_device/msd/TestUSBMSD.h b/drivers/tests/TESTS/usb_device/msd/TestUSBMSD.h new file mode 100644 index 0000000..0c40042 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/msd/TestUSBMSD.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef Test_USBMSD_H +#define Test_USBMSD_H + +#include "USBMSD.h" + + +#define USB_DEV_SN_LEN (32) // 32 hex digit UUID +#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2) + +/** + * Convert a C style ASCII to a USB string descriptor + * + * @param usb_desc output buffer for the USB string descriptor + * @param str ASCII string + * @param n size of usb_desc buffer, even number + * @returns number of bytes returned in usb_desc or -1 on failure + */ +int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n) +{ + if (str == NULL || usb_desc == NULL || n < 4) { + return -1; + } + if (n % 2 != 0) { + return -1; + } + size_t s, d; + // set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string + memset(usb_desc, 0, n); + for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) { + usb_desc[d] = str[s]; + } + // set bLength @ offset 0 + usb_desc[0] = d; + // set bDescriptorType @ offset 1 + usb_desc[1] = STRING_DESCRIPTOR; + return d; +} + +class TestUSBMSD: public USBMSD { +public: + TestUSBMSD(BlockDevice *bd, bool connect_blocking = true, uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, + uint16_t product_release = 0x0001) + : USBMSD(bd, connect_blocking, vendor_id, product_id, product_release) + { + + } + + virtual ~TestUSBMSD() + { + + } + + uint32_t get_read_counter() + { + return read_counter; + } + + uint32_t get_program_counter() + { + return program_counter; + } + + void reset_counters() + { + read_counter = program_counter = erase_counter = 0; + } + + static void setup_serial_number() + { + char _key[128] = { 0 }; + char _value[128] = { 0 }; + + greentea_send_kv("get_serial_number", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("serial_number", _key); + usb_dev_sn[USB_DEV_SN_LEN] = '\0'; + memcpy(usb_dev_sn, _value, USB_DEV_SN_LEN); + ascii2usb_string_desc(_serial_num_descriptor, usb_dev_sn, USB_DEV_SN_DESC_SIZE); + } + + virtual const uint8_t *string_iserial_desc() + { + return (const uint8_t *)_serial_num_descriptor; + } + + static volatile uint32_t read_counter; + static volatile uint32_t program_counter; + static volatile uint32_t erase_counter; + +protected: + virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count) + { + read_counter++; + return USBMSD::disk_read(data, block, count); + } + + virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count) + { + erase_counter++; + program_counter++; + + return USBMSD::disk_write(data, block, count); + } +private: + static uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; + static char usb_dev_sn[USB_DEV_SN_LEN + 1]; +}; + +uint8_t TestUSBMSD::_serial_num_descriptor[USB_DEV_SN_DESC_SIZE] = { 0 }; +char TestUSBMSD::usb_dev_sn[USB_DEV_SN_LEN + 1] = { 0 }; + + +volatile uint32_t TestUSBMSD::read_counter = 0; +volatile uint32_t TestUSBMSD::program_counter = 0; +volatile uint32_t TestUSBMSD::erase_counter = 0; + +#endif // Test_USBMSD_H diff --git a/drivers/tests/TESTS/usb_device/msd/main.cpp b/drivers/tests/TESTS/usb_device/msd/main.cpp new file mode 100644 index 0000000..a6e03f0 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/msd/main.cpp @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2019-2020, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !USB_DEVICE_TESTS +#error [NOT_SUPPORTED] usb device tests not enabled +#else + +#if !defined(MBED_CONF_RTOS_PRESENT) +#error [NOT_SUPPORTED] USB stack and test cases require RTOS to run. +#else + +#include +#include +#include /* srand, rand */ +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest/utest.h" + +#include "mbed.h" +#include "USBMSD.h" +#include "TestUSBMSD.h" + +#include "HeapBlockDevice.h" +#include "FATFileSystem.h" + +// TARGET_NANO100 SRAM 16KB can't afford mass-storage-disk test, so skip usb_msd_test. +#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE || TARGET_NANO100 +#error [NOT_SUPPORTED] USB Device not supported for this target +#else + + +#define OS_WINDOWS 1 +#define OS_LINUX 2 +#define OS_MAC 3 + +// Host side unmount was disabled for windows machines. +// PowerShell execution policies/restrictions cause that +// on some windows machines unmount is failing +// To re-enable it comment out below line. +#define DISABLE_HOST_SIDE_UMOUNT + +#ifdef MIN +#undef MIN +#endif +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) + +#define DEFAULT_BLOCK_SIZE 512 +#define HEAP_BLOCK_DEVICE_SIZE (128 * DEFAULT_BLOCK_SIZE) +#define MIN_HEAP_SIZE (HEAP_BLOCK_DEVICE_SIZE + 6144) + + +/* TODO: + * + * Test if slave(DUT) can force host to refresh mounted fs. (not supported in USBMSD yet) + * + */ + +#define TEST_DIR "usb_msd_test_data" +#define TEST_FILE "usb_msd_test_file" +#define TEST_STRING "usb_msd_test_string" + +using namespace utest::v1; + +uint32_t prev_read_counter = 0; +uint32_t prev_program_counter = 0; +extern uint32_t mbed_heap_size; + +static char _key[256] = { 0 }; +static char _value[128] = { 0 }; + +static volatile bool msd_process_done = false; + + +FATFileSystem heap_fs("heap_fs"); + +Semaphore media_remove_event(0, 1); + + +/** Creates heap block device + * + */ +BlockDevice *get_heap_block_device() +{ + // create 64kB heap block device + if (mbed_heap_size >= MIN_HEAP_SIZE) { + static HeapBlockDevice bd(128 * DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE); + bd.init(); + return &bd; + } else { + return NULL; + } +} + +uint64_t get_fs_mount_size(FileSystem *fs) +{ + struct statvfs stat; + fs->statvfs(fs->getName(), &stat); + uint64_t size = stat.f_bsize * stat.f_blocks; + return size; +} + +/** + * Create test data + * + * @param fs_root filesystem path + */ +static bool test_files_create(const char *fs_root, const char *test_file = TEST_FILE, const char *test_string = TEST_STRING) +{ + char path[128]; + sprintf(path, "/%s/%s", fs_root, TEST_DIR); + int ret = mkdir(path, 0777); + if (ret != 0 && errno != EEXIST) { + utest_printf("mkdir failed!!! errno: %d\n", errno); + return false; + } + sprintf(path, "/%s/%s/%s", fs_root, TEST_DIR, test_file); + FILE *f = fopen(path, "w"); + if (f == NULL) { + utest_printf("fopen failed!!! errno: %d\n", errno); + return false; + } + fprintf(f, test_string); + fflush(f); + fclose(f); + return true; +} + +/** + * Remove test data + * + * @param fs_root filesystem path + */ +static void test_files_remove(const char *fs_root) +{ + DIR *dir; + struct dirent *dp; + char path[512]; + + sprintf(path, "/%s/%s", fs_root, TEST_DIR); + dir = opendir(path); + if (dir == NULL) { + return; + } + while ((dp = readdir(dir)) != NULL) { + sprintf(path, "/%s/%s/%s", fs_root, TEST_DIR, dp->d_name); + remove(path); + } + sprintf(path, "/%s/%s", fs_root, TEST_DIR); + remove(path); +} + +/** + * Check if test data exist + * + * @param fs_root filesystem path + * @return true if data exist + */ +static bool test_files_exist(const char *fs_root, const char *test_file = TEST_FILE, const char *test_string = TEST_STRING) +{ + char path[128]; + char str[512] = { 0 }; + sprintf(path, "/%s/%s/%s", fs_root, TEST_DIR, test_file); + FILE *f = fopen(path, "r"); + if (f != NULL) { + fscanf(f, "%s", str); + if (strcmp(test_string, str) == 0) { + return true; + } + } + return false; +} + +/** + * Mounts a filesystem to a block device + * + * @param bd block device + * @param fs filesystem + * @return true if success, false otherwise + */ +static bool prepare_storage(BlockDevice *bd, FileSystem *fs) +{ + const char *fs_root = fs->getName(); + int err = fs->mount(bd); + if (err) { + utest_printf("%s filesystem mount failed\ntry to reformat device... ", fs->getName()); + err = fs->reformat(bd); + if (err) { + utest_printf("failed !!!\n"); + return false; + } else { + utest_printf("succeed\n"); + } + } + // remove old test data + test_files_remove(fs_root); + + return true; +} + +void run_processing(Semaphore *sem) +{ + sem->release(); +} + +void msd_process(USBMSD *msd) +{ + Semaphore proc; + msd->attach(callback(run_processing, &proc)); + while (!msd_process_done) { + proc.try_acquire_for(100); + msd->process(); + if (msd->media_removed()) { + media_remove_event.release(); + } + } + msd->attach(NULL); +} + + +// wait until msd negotiation is done (no r/w disk operation for at least 1s) +// max wait time is 15s +#define WAIT_MSD_COMMUNICATION_DONE() \ + for (int x = 0; x < 15; x++) { \ + prev_read_counter = usb.get_read_counter();\ + prev_program_counter = usb.get_program_counter();\ + ThisThread::sleep_for(1000);\ + if ((usb.get_read_counter() == prev_read_counter) && \ + (usb.get_program_counter() == prev_program_counter)) {\ + break;\ + }\ + } + + +#define TEST_ASSERT_EQUAL_STRING_LOOP(expected, actual, loop_index) \ + if (strcmp(expected, actual) != 0) { \ + char str[128]; \ + sprintf(str, "expected %s was %s (loop index: %lu)", expected, actual, loop_index); \ + TEST_ASSERT_MESSAGE(false, str); \ + } + +#define TEST_ASSERT_EQUAL_LOOP(expected, actual, loop_index) \ + if (expected != actual) { \ + char str[128]; \ + sprintf(str, "expected %d was %d (loop index: %lu)", expected, actual, loop_index); \ + TEST_ASSERT_MESSAGE(false, str); \ + } + + +/** Initialize storages + * + * Given the DUT USB mass storage device + * When DUT has enought heap memory + * Then initialize heap block device for tests + * When DUT has any falsh block device + * Then initialize it for tests + */ +void storage_init() +{ + TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation"); + FATFileSystem::format(get_heap_block_device()); + bool result = prepare_storage(get_heap_block_device(), &heap_fs); + TEST_ASSERT_MESSAGE(result, "heap storage initialisation failed"); +} + +/** Test mass storage device mount and unmount + * + * Given the DUT USB mass storage device connected to the host + * When DUT call @USBMSD::connect + * Then host detects mass storage device is mounted as removable disk drive and it reports valid filesystem size + * When DUT call @USBMSD::disconnect + * Then host detects mass storage device is unmounted (ejected) + * + * Given the DUT USB mass storage device connected to the host and mounted + * When host unmounts (ejects) mass storage device + * Then DUT detects media remove event + */ +template +void mount_unmount_test(BlockDevice *bd, FileSystem *fs) +{ + Thread msd_thread(osPriorityHigh); + TestUSBMSD usb(bd, false); + msd_process_done = false; + msd_thread.start(callback(msd_process, &usb)); + + for (uint32_t i = 1; i <= N; i++) { + // mount + usb.connect(); + WAIT_MSD_COMMUNICATION_DONE(); + // check if device is mounted on host side + greentea_send_kv("check_if_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + + TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); + + greentea_send_kv("get_mounted_fs_size", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + uint64_t ret_size = atoll(_key); + TEST_ASSERT_EQUAL_UINT64(get_fs_mount_size(fs), ret_size); + + // unmount + usb.disconnect(); + // check if device is detached on host side + greentea_send_kv("check_if_not_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); + } + + for (uint32_t i = 1; i <= N; i++) { + // mount + usb.connect(); + WAIT_MSD_COMMUNICATION_DONE(); + // check if device is mounted on host side + greentea_send_kv("check_if_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); + + greentea_send_kv("get_mounted_fs_size", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + uint64_t ret_size = atoll(_key); + TEST_ASSERT_EQUAL_UINT64(get_fs_mount_size(fs), ret_size); + +#ifdef DISABLE_HOST_SIDE_UMOUNT + greentea_send_kv("get_os_type", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + int32_t os_type = atoi(_value); + if (os_type != OS_WINDOWS) { +#endif + // unmount msd device on host side + greentea_send_kv("unmount", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); + + // wait for unmount event (set 10s timeout) + media_remove_event.try_acquire_for(10000); + if (!usb.media_removed()) { + TEST_ASSERT_EQUAL_LOOP(true, usb.media_removed(), i); + } + + // unmount since media_removed doesn't disconnects device side + usb.disconnect(); +#ifdef DISABLE_HOST_SIDE_UMOUNT + } else { + // unmount + usb.disconnect(); + } +#endif + // check if device is detached on host side + greentea_send_kv("check_if_not_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i); + } + + // mount + usb.connect(); + WAIT_MSD_COMMUNICATION_DONE(); + // check if device is mounted on host side + greentea_send_kv("check_if_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + // unmount + usb.disconnect(); + // check if device is detached on host side + greentea_send_kv("check_if_not_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + msd_process_done = true; // terminate msd_thread + msd_thread.join(); +} + +/** Test mass storage device mount and unmount together with underlying file system operations + * + * Given the DUT USB mass storage device connected to the host + * When DUT call @USBMSD::connect + * Then host detects that mass storage device is mounted as removable disk drive and test files are present + * When DUT call @USBMSD::disconnect + * Then host detects mass storage device is unmounted (ejected) + * + * Given the DUT USB mass storage device connected to the host and already mounted + * When host unmounts (ejects) mass storage device + * Then DUT detects media remove event + */ +void mount_unmount_and_data_test(BlockDevice *bd, FileSystem *fs) +{ + const char *fs_root = fs->getName(); + Thread msd_thread(osPriorityHigh); + TestUSBMSD usb(bd, false); + msd_process_done = false; + msd_thread.start(callback(msd_process, &usb)); + + // mount + usb.connect(); + WAIT_MSD_COMMUNICATION_DONE(); + // check if device is mounted on host side + greentea_send_kv("check_if_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + greentea_send_kv("check_file_exist", TEST_DIR " " TEST_FILE " " TEST_STRING); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("non-exist", _key); + + usb.disconnect(); + // check if device is detached on host side + greentea_send_kv("check_if_not_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + test_files_create(fs_root); + TEST_ASSERT(test_files_exist(fs_root)); + + usb.connect(); + WAIT_MSD_COMMUNICATION_DONE(); + // check if device is mounted on host side + greentea_send_kv("check_if_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + greentea_send_kv("check_file_exist", TEST_DIR " " TEST_FILE " " TEST_STRING); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("exist", _key); + + greentea_send_kv("delete_files", TEST_DIR " " TEST_FILE); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + do { + ThisThread::sleep_for(1); + } while (test_files_exist(fs_root)); + TEST_ASSERT_EQUAL(false, test_files_exist(fs_root)); + + usb.disconnect(); + // check if device is detached on host side + greentea_send_kv("check_if_not_mounted", 0); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); + + msd_process_done = true; // terminate msd_thread + msd_thread.join(); + test_files_remove(fs_root); +} + +void heap_block_device_mount_unmount_test() +{ + TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation"); + mount_unmount_test<3>(get_heap_block_device(), &heap_fs); +} + +void heap_block_device_mount_unmount_and_data_test() +{ + TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation"); + mount_unmount_and_data_test(get_heap_block_device(), &heap_fs); +} + + +Case cases[] = { + Case("storage initialization", storage_init), + + Case("mount/unmount test - Heap block device", heap_block_device_mount_unmount_test), + Case("mount/unmount and data test - Heap block device", heap_block_device_mount_unmount_and_data_test), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(300, "pyusb_msd"); + utest::v1::status_t status = greentea_test_setup_handler(number_of_cases); + TestUSBMSD::setup_serial_number(); + return status; +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#endif // !defined(MBED_CONF_RTOS_PRESENT) +#endif // !defined(USB_DEVICE_TESTS) diff --git a/drivers/tests/TESTS/usb_device/serial/main.cpp b/drivers/tests/TESTS/usb_device/serial/main.cpp new file mode 100644 index 0000000..7f16ef5 --- /dev/null +++ b/drivers/tests/TESTS/usb_device/serial/main.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2018-2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !USB_DEVICE_TESTS +#error [NOT_SUPPORTED] usb device tests not enabled +#else + +#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#error [NOT_SUPPORTED] USB Device not supported for this target +#else + +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "mbed.h" +#include +#include "usb_phy_api.h" +#include "USBCDC.h" +#include "USBSerial.h" +#include "hal/us_ticker_api.h" + +#define USB_CDC_VID 0x1f00 +#define USB_CDC_PID 0x2013 +#define USB_SERIAL_VID 0x1f00 +#define USB_SERIAL_PID 0x2012 + +#define MSG_KEY_LEN 24 +#define MSG_VALUE_DUMMY "0" +#define MSG_KEY_DEVICE_READY "ready" +#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn" +#define MSG_KEY_PORT_OPEN_WAIT "port_open_wait" +#define MSG_KEY_PORT_OPEN_CLOSE "port_open_close" +#define MSG_KEY_SEND_BYTES_SINGLE "send_single" +#define MSG_KEY_SEND_BYTES_MULTIPLE "send_multiple" +#define MSG_KEY_LOOPBACK "loopback" +#define MSG_KEY_CHANGE_LINE_CODING "change_lc" + +#define TX_BUFF_SIZE 32 +#define RX_BUFF_SIZE 32 + +// The size of every data chunk the host sends (for each value from a +// known sequence) during 'CDC RX multiple' test cases is +// HOST_RX_BUFF_SIZE_RATIO times the size of RX_BUFF_SIZE input buffer. +// 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 1ms + +#define CDC_LOOPBACK_REPS 1200 +#define SERIAL_LOOPBACK_REPS 100 +#define USB_RECONNECT_DELAY_MS 1ms + +#define LINE_CODING_SEP (',') +#define LINE_CODING_DELIM (';') + +#define USB_DEV_SN_LEN (32) // 32 hex digit UUID +#define NONASCII_CHAR ('?') +#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2) + +const char *default_serial_num = "0123456789"; +char usb_dev_sn[USB_DEV_SN_LEN + 1]; + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +typedef struct LineCoding { + // bits per second + int baud; + + // 5, 6, 7, 8 or 16 + int bits; + + // 0 -- None, + // 1 -- Odd, + // 2 -- Even, + // 3 -- Mark, + // 4 -- Space + int parity; + + // 0 -- 1 Stop bit, + // 1 -- 1.5 Stop bits, + // 2 -- 2 Stop bits + int stop; + + int get_num_diffs(LineCoding const &other) const + { + int diffs = 0; + if (baud != other.baud) { + diffs++; + } + if (bits != other.bits) { + diffs++; + } + if (parity != other.parity) { + diffs++; + } + if (stop != other.stop) { + diffs++; + } + return diffs; + } +} line_coding_t; + +line_coding_t default_lc = { 9600, 8, 0, 0 }; + +// There is no POSIX support for 1.5 stop bits. +// Do not set stop bits to 1.5 to keep tests compatible with all supported +// host systems. +line_coding_t test_codings[] = { + { 9600, 5, 0, 2 }, + { 4800, 7, 2, 0 }, + { 19200, 8, 0, 2 }, + { 115200, 8, 0, 0 }, + { 38400, 8, 1, 0 }, + { 1200, 8, 0, 0 }, + { 19200, 8, 0, 0 }, + { 2400, 7, 2, 0 }, + { 9600, 8, 0, 0 }, + { 57600, 8, 0, 0 }, +}; +static CircularBuffer lc_data; + +#define EF_SEND (1ul << 0) +EventFlags event_flags; + +/** + * Convert a USB string descriptor to C style ASCII + * + * The string placed in str is always null-terminated which may cause the + * loss of data if n is to small. If the length of descriptor string is less + * than n, additional null bytes are written to str. + * + * @param str output buffer for the ASCII string + * @param usb_desc USB string descriptor + * @param n size of str buffer + * @returns number of non-null bytes returned in str or -1 on failure + */ +int usb_string_desc2ascii(char *str, const uint8_t *usb_desc, size_t n) +{ + if (str == NULL || usb_desc == NULL || n < 1) { + return -1; + } + // bDescriptorType @ offset 1 + if (usb_desc[1] != STRING_DESCRIPTOR) { + return -1; + } + // bLength @ offset 0 + const size_t bLength = usb_desc[0]; + if (bLength % 2 != 0) { + return -1; + } + size_t s, d; + for (s = 0, d = 2; s < n - 1 && d < bLength; s++, d += 2) { + // handle non-ASCII characters + if (usb_desc[d] > 0x7f || usb_desc[d + 1] != 0) { + str[s] = NONASCII_CHAR; + } else { + str[s] = usb_desc[d]; + } + } + int str_len = s; + for (; s < n; s++) { + str[s] = '\0'; + } + return str_len; +} + +/** + * Convert a C style ASCII to a USB string descriptor + * + * @param usb_desc output buffer for the USB string descriptor + * @param str ASCII string + * @param n size of usb_desc buffer, even number + * @returns number of bytes returned in usb_desc or -1 on failure + */ +int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n) +{ + if (str == NULL || usb_desc == NULL || n < 4) { + return -1; + } + if (n % 2 != 0) { + return -1; + } + size_t s, d; + // set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string + memset(usb_desc, 0, n); + for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) { + usb_desc[d] = str[s]; + } + // set bLength @ offset 0 + usb_desc[0] = d; + // set bDescriptorType @ offset 1 + usb_desc[1] = STRING_DESCRIPTOR; + return d; +} + +class TestUSBCDC: public USBCDC { +private: + uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; +public: + TestUSBCDC(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, + const char *serial_number = default_serial_num) : + USBCDC(get_usb_phy(), vendor_id, product_id, product_release) + { + init(); + int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); + if (rc < 0) { + ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); + } + } + + virtual ~TestUSBCDC() + { + deinit(); + } + + virtual const uint8_t *string_iserial_desc() + { + return (const uint8_t *) _serial_num_descriptor; + } +}; + +class TestUSBSerial: public USBSerial { +private: + uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE]; +public: + TestUSBSerial(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, + const char *serial_number = default_serial_num) : + USBSerial(get_usb_phy(), vendor_id, product_id, product_release) + { + int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE); + if (rc < 0) { + ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE); + } + } + + virtual ~TestUSBSerial() + { + deinit(); + } + + virtual const uint8_t *string_iserial_desc() + { + return (const uint8_t *) _serial_num_descriptor; + } +}; + +/** Test CDC USB reconnect + * + * Given the host has successfully opened the port of a USB CDC device + * When the USB device disconnects and connects again + * Then the host is able to successfully open the port again + */ +void test_cdc_usb_reconnect() +{ + TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn); + TEST_ASSERT_FALSE(usb_cdc.configured()); + TEST_ASSERT_FALSE(usb_cdc.ready()); + + // Connect the USB device. + usb_cdc.connect(); + // Wait for the USB enumeration to complete. + while (!usb_cdc.configured()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_cdc.configured()); + TEST_ASSERT_FALSE(usb_cdc.ready()); + + 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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_cdc.wait_ready(); + TEST_ASSERT_TRUE(usb_cdc.configured()); + TEST_ASSERT_TRUE(usb_cdc.ready()); + + // Disconnect the USB device. + usb_cdc.disconnect(); + TEST_ASSERT_FALSE(usb_cdc.configured()); + TEST_ASSERT_FALSE(usb_cdc.ready()); + + ThisThread::sleep_for(USB_RECONNECT_DELAY_MS); + // Connect the USB device again. + usb_cdc.connect(); + // Wait for the USB enumeration to complete. + while (!usb_cdc.configured()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_cdc.configured()); + TEST_ASSERT_FALSE(usb_cdc.ready()); + + 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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_cdc.wait_ready(); + TEST_ASSERT_TRUE(usb_cdc.configured()); + TEST_ASSERT_TRUE(usb_cdc.ready()); + + // Disconnect the USB device again. + usb_cdc.disconnect(); + TEST_ASSERT_FALSE(usb_cdc.configured()); + TEST_ASSERT_FALSE(usb_cdc.ready()); +} + +/** Test CDC receive single bytes + * + * Given the USB CDC device connected to a host + * When the host transmits a known sequence one byte at a time + * Then every byte received by the device matches the sequence + */ +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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_cdc.wait_ready(); + uint8_t buff = 0x01; + for (int expected = 0xff; expected >= 0; expected--) { + TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); + TEST_ASSERT_EQUAL_UINT8(expected, buff); + } + for (int expected = 0; expected <= 0xff; expected++) { + TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); + TEST_ASSERT_EQUAL_UINT8(expected, buff); + } + // Wait for the host to close its port. + while (usb_cdc.ready()) { + ThisThread::sleep_for(1ms); + } + usb_cdc.disconnect(); +} + +void tx_thread_fun(USBCDC *usb_cdc) +{ + uint8_t buff_val = 0; + uint8_t buff[TX_BUFF_SIZE] = { 0 }; + while (event_flags.get() & EF_SEND) { + if (!usb_cdc->send(buff, TX_BUFF_SIZE)) { + ThisThread::sleep_for(1ms); + continue; + } + buff_val++; + memset(buff, buff_val, TX_BUFF_SIZE); + } +} + +void tx_ticker_fun(USBCDC *usb_cdc, uint8_t start_val, uint8_t size = TX_BUFF_SIZE) +{ + static uint8_t buff_val = start_val; + uint32_t actual_tx = 0; + uint8_t buff[TX_BUFF_SIZE] = { 0 }; + memset(buff, buff_val, size); + usb_cdc->send_nb(buff, size, &actual_tx); + TEST_ASSERT_EQUAL_UINT8(size, actual_tx); + buff_val++; +} + +/** Test CDC receive single bytes concurrently + * + * Given the USB CDC device connected to a host + * When the host transmits a known sequence one byte at a time + * and at the same time the device transmits data to host + * Then every byte received by the device matches the sequence + */ +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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_cdc.wait_ready(); +#if defined(MBED_CONF_RTOS_PRESENT) + Thread tx_thread; + event_flags.set(EF_SEND); + tx_thread.start(mbed::callback(tx_thread_fun, &usb_cdc)); +#else + Ticker t; + t.attach([&] { tx_ticker_fun(&usb_cdc, 0, 1); }, 2ms); +#endif + + uint8_t buff = 0x01; + for (int expected = 0xff; expected >= 0; expected--) { + TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); + TEST_ASSERT_EQUAL_UINT8(expected, buff); + } + for (int expected = 0; expected <= 0xff; expected++) { + TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL)); + TEST_ASSERT_EQUAL_UINT8(expected, buff); + } + +#if defined(MBED_CONF_RTOS_PRESENT) + event_flags.clear(EF_SEND); + tx_thread.join(); +#else + t.detach(); +#endif + // Wait for the host to close its port. + while (usb_cdc.ready()) { + ThisThread::sleep_for(1ms); + } + usb_cdc.disconnect(); +} + +/** Test CDC receive multiple bytes + * + * Given the USB CDC device connected to a host + * When the host transmits chunks of data following a known sequence + * Then every chunk received by the device matches the sequence + */ +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(); + ThisThread::sleep_for(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 }; + for (int expected = 0xff; expected >= 0; expected--) { + for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { + memset(expected_buff, expected, RX_BUFF_SIZE); + TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); + } + } + for (int expected = 0; expected <= 0xff; expected++) { + for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { + memset(expected_buff, expected, RX_BUFF_SIZE); + TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); + } + } + // Wait for the host to close its port. + while (usb_cdc.ready()) { + ThisThread::sleep_for(1ms); + } + usb_cdc.disconnect(); +} + +/** Test CDC receive multiple bytes concurrently + * + * Given the USB CDC device connected to a host + * When the host transmits chunks of data following a known sequence + * and at the same time the device transmits data to host + * Then every chunk received by the device matches the sequence + */ +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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_cdc.wait_ready(); +#if defined(MBED_CONF_RTOS_PRESENT) + Thread tx_thread; + event_flags.set(EF_SEND); + tx_thread.start(mbed::callback(tx_thread_fun, &usb_cdc)); +#else + Ticker t; + t.attach([&] { tx_ticker_fun(&usb_cdc, 0); }, 3ms); +#endif + uint8_t buff[RX_BUFF_SIZE] = { 0 }; + uint8_t expected_buff[RX_BUFF_SIZE] = { 0 }; + for (int expected = 0xff; expected >= 0; expected--) { + for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { + memset(expected_buff, expected, RX_BUFF_SIZE); + TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); + } + } + for (int expected = 0; expected <= 0xff; expected++) { + for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) { + memset(expected_buff, expected, RX_BUFF_SIZE); + TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE); + } + } +#if defined(MBED_CONF_RTOS_PRESENT) + event_flags.clear(EF_SEND); + tx_thread.join(); +#else + t.detach(); +#endif + // Wait for the host to close its port. + while (usb_cdc.ready()) { + ThisThread::sleep_for(1ms); + } + usb_cdc.disconnect(); +} + +/** Test CDC loopback + * + * Given the USB CDC device connected to a host + * When the device transmits random bytes to host + * and the host transmits them back to the device + * Then every byte received by the device is equal to byte preciously sent + */ +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(); + ThisThread::sleep_for(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++) { + tx_buff = (uint8_t)(rand() % 0x100); + rx_buff = (uint8_t)(tx_buff + 1); + TEST_ASSERT(usb_cdc.send(&tx_buff, 1)); + TEST_ASSERT(usb_cdc.receive(&rx_buff, 1, NULL)); + TEST_ASSERT_EQUAL_UINT8(tx_buff, rx_buff); + } + // Wait for the host to close its port. + while (usb_cdc.ready()) { + ThisThread::sleep_for(1ms); + } + usb_cdc.disconnect(); +} + +/** Test Serial USB reconnect + * + * Given the host has successfully opened the port of a USB Serial device + * When the USB device disconnects and connects again + * Then the host is able to successfully open the port again + */ +void test_serial_usb_reconnect() +{ + TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); + TEST_ASSERT_FALSE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + // Connect the USB device. + usb_serial.connect(); + // Wait for the USB enumeration to complete. + while (!usb_serial.configured()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + 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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + while (!usb_serial.connected()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_TRUE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + // Disconnect the USB device. + usb_serial.disconnect(); + TEST_ASSERT_FALSE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + ThisThread::sleep_for(USB_RECONNECT_DELAY_MS); + // Connect the USB device again. + usb_serial.connect(); + // Wait for the USB enumeration to complete. + while (!usb_serial.configured()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + 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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + while (!usb_serial.connected()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_TRUE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + // Disconnect the USB device again. + usb_serial.disconnect(); + TEST_ASSERT_FALSE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); +} + +/** Test Serial terminal reopen + * + * Given the host has successfully opened the port of a USB Serial device + * When the host closes its port + * Then the host is able to successfully open the port again + */ +void test_serial_term_reopen() +{ + TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn); + 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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + while (!usb_serial.connected()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_TRUE(usb_serial.ready()); + TEST_ASSERT_TRUE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + // Wait for the host to close the terminal. + while (usb_serial.ready()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.ready()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + 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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + while (!usb_serial.connected()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_TRUE(usb_serial.ready()); + TEST_ASSERT_TRUE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + // Wait for the host to close the terminal again. + while (usb_serial.ready()) { + ThisThread::sleep_for(1ms); + } + TEST_ASSERT_TRUE(usb_serial.configured()); + TEST_ASSERT_FALSE(usb_serial.ready()); + TEST_ASSERT_FALSE(usb_serial.connected()); + TEST_ASSERT_EQUAL_INT(0, usb_serial.readable()); + + usb_serial.disconnect(); +} + +/** Test Serial getc + * + * Given the USB Serial device connected to a host + * When the host transmits a known sequence one byte at a time + * Then every byte received by the device matches the sequence + */ +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); +#if LINUX_HOST_DTR_FIX + usb_serial.wait_ready(); + ThisThread::sleep_for(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()); + } + for (int expected = 0; expected <= 0xff; expected++) { + TEST_ASSERT_EQUAL_INT(expected, usb_serial.getc()); + } + // Wait for the host to close its port. + while (usb_serial.ready()) { + ThisThread::sleep_for(1ms); + } + usb_serial.disconnect(); +} + +/** Test Serial printf & scanf + * + * Given the USB Serial device connected to a host + * When the device transmits a formatted string with a random value + * using the printf method + * and the host sends it back to the device + * Then the device can successfully read the value using scanf method + * and the value received is equal value sent + */ +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(); + ThisThread::sleep_for(LINUX_HOST_DTR_FIX_DELAY_MS); +#endif + usb_serial.wait_ready(); + static const char fmt[] = "Formatted\nstring %i."; + int tx_val, rx_val, rc; + for (int i = 0; i < SERIAL_LOOPBACK_REPS; i++) { + tx_val = rand(); + rx_val = tx_val + 1; + rc = usb_serial.printf(fmt, tx_val); + TEST_ASSERT(rc > 0); + rc = usb_serial.scanf(fmt, &rx_val); + TEST_ASSERT(rc == 1); + TEST_ASSERT_EQUAL_INT(tx_val, rx_val); + } + // Wait for the host to close its port. + while (usb_serial.ready()) { + ThisThread::sleep_for(1ms); + } + usb_serial.disconnect(); +} + +void line_coding_changed_cb(int baud, int bits, int parity, int stop) +{ + line_coding_t lc = { + .baud = baud, + .bits = bits, + .parity = parity, + .stop = stop + }; + lc_data.push(lc); + event_flags.set(EF_SEND); +} + +/** Test Serial / CDC line coding change + * + * Given the device transmits a set of line coding params to host + * When the host updates serial port settings + * Then line_coding_changed() callback is called + * and the line coding is set as expected + */ +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(); + ThisThread::sleep_for(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]; + line_coding_t *lc_prev = &default_lc; + line_coding_t *lc_expected = NULL; + int num_expected_callbacks, rc; + for (size_t i = 0; i < num_line_codings; i++) { + line_coding_t lc_actual = {0}; + lc_expected = &(test_codings[i]); + num_expected_callbacks = lc_prev->get_num_diffs(*lc_expected); + rc = usb_serial.printf("%06i,%02i,%01i,%01i%c", lc_expected->baud, lc_expected->bits, lc_expected->parity, + lc_expected->stop, LINE_CODING_DELIM); + TEST_ASSERT(rc > 0); + // The pyserial Python module does not update all line coding params + // at once. It updates params one by one instead, and since every + // update is followed by port reconfiguration we get multiple + // calls to line_coding_changed callback on the device. + while (num_expected_callbacks > 0) { + num_expected_callbacks--; + event_flags.wait_all(EF_SEND); + lc_data.pop(lc_actual); + if (lc_expected->get_num_diffs(lc_actual) == 0) { + break; + } + } + TEST_ASSERT_EQUAL_INT(lc_expected->baud, lc_actual.baud); + TEST_ASSERT_EQUAL_INT(lc_expected->bits, lc_actual.bits); + TEST_ASSERT_EQUAL_INT(lc_expected->parity, lc_actual.parity); + TEST_ASSERT_EQUAL_INT(lc_expected->stop, lc_actual.stop); + lc_prev = lc_expected; + } + // Wait for the host to close its port. + while (usb_serial.ready()) { + ThisThread::sleep_for(1ms); + } + usb_serial.disconnect(); +} + +utest::v1::status_t testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(45, "usb_device_serial"); + srand((unsigned) ticker_read_us(get_us_ticker_data())); + + utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases); + if (status != utest::v1::STATUS_CONTINUE) { + return status; + } + + char key[MSG_KEY_LEN + 1] = { }; + char usb_dev_uuid[USB_DEV_SN_LEN + 1] = { }; + + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); + greentea_parse_kv(key, usb_dev_uuid, MSG_KEY_LEN, USB_DEV_SN_LEN + 1); + + if (strcmp(key, MSG_KEY_SERIAL_NUMBER) != 0) { + utest_printf("Invalid message key.\n"); + return utest::v1::STATUS_ABORT; + } + + strncpy(usb_dev_sn, usb_dev_uuid, USB_DEV_SN_LEN + 1); + return status; +} + +Case cases[] = { + Case("CDC USB reconnect", test_cdc_usb_reconnect), + Case("CDC RX single bytes", test_cdc_rx_single_bytes), + Case("CDC RX single bytes concurrent", test_cdc_rx_single_bytes_concurrent), + Case("CDC RX multiple bytes", test_cdc_rx_multiple_bytes), + Case("CDC RX multiple bytes concurrent", test_cdc_rx_multiple_bytes_concurrent), + Case("CDC loopback", test_cdc_loopback), + Case("Serial USB reconnect", test_serial_usb_reconnect), + Case("Serial terminal reopen", test_serial_term_reopen), + Case("Serial getc", test_serial_getc), + Case("Serial printf/scanf", test_serial_printf_scanf), + Case("Serial line coding change", test_serial_line_coding_change), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#endif // !defined(USB_DEVICE_TESTS)