From 05f30c1052e09fc506e778d7a73edc7657c38e1d Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Thu, 6 Oct 2022 23:16:54 +0200 Subject: [PATCH] usb: Refactor USB Host tests * USB tests migrated to pytest * Error messages improved * Configurable for different mock devices --- .gitlab/ci/rules.yml | 36 ++- components/usb/.build-test-rules.yml | 5 + components/usb/test/CMakeLists.txt | 12 - .../usb/test/common/test_usb_mock_classes.c | 132 -------- .../usb/test/common/test_usb_mock_classes.h | 290 ------------------ .../usb/test_apps/common/CMakeLists.txt | 3 + .../common/test_usb_common.c | 5 +- .../common/test_usb_common.h | 2 +- .../usb/test_apps/common/test_usb_mock_hid.c | 42 +++ .../usb/test_apps/common/test_usb_mock_hid.h | 113 +++++++ .../usb/test_apps/common/test_usb_mock_msc.c | 158 ++++++++++ .../usb/test_apps/common/test_usb_mock_msc.h | 196 ++++++++++++ components/usb/test_apps/hcd/CMakeLists.txt | 6 + components/usb/test_apps/hcd/README.md | 10 + .../usb/test_apps/hcd/main/CMakeLists.txt | 6 + .../usb/test_apps/hcd/main/test_app_main.c | 48 +++ .../hcd/main}/test_hcd_bulk.c | 25 +- .../hcd/main}/test_hcd_common.c | 50 +-- .../hcd/main}/test_hcd_common.h | 2 + .../hcd/main}/test_hcd_ctrl.c | 39 +-- .../hcd/main}/test_hcd_intr.c | 14 +- .../hcd/main}/test_hcd_isoc.c | 16 +- .../hcd/main}/test_hcd_port.c | 28 +- .../hcd/main}/test_usb_helpers.c | 31 +- .../usb/test_apps/hcd/pytest_usb_hcd.py | 14 + .../usb/test_apps/hcd/sdkconfig.defaults | 8 + .../usb/test_apps/usb_host/CMakeLists.txt | 6 + components/usb/test_apps/usb_host/README.md | 11 + .../test_apps/usb_host/main/CMakeLists.txt | 6 + .../usb_host/main}/ctrl_client.h | 0 .../usb_host/main}/ctrl_client_async_seq.c | 6 +- .../usb_host/main}/msc_client.h | 0 .../usb_host/main}/msc_client_async_dconn.c | 7 +- .../usb_host/main}/msc_client_async_enum.c | 17 +- .../usb_host/main}/msc_client_async_seq.c | 13 +- .../test_apps/usb_host/main/test_app_main.c | 59 ++++ .../usb_host/main}/test_usb_host_async.c | 64 +--- .../usb_host/main}/test_usb_host_plugging.c | 52 +--- .../usb/test_apps/usb_host/pytest_usb_host.py | 14 + .../usb/test_apps/usb_host/sdkconfig.defaults | 8 + pytest.ini | 1 + 41 files changed, 880 insertions(+), 675 deletions(-) create mode 100644 components/usb/.build-test-rules.yml delete mode 100644 components/usb/test/CMakeLists.txt delete mode 100644 components/usb/test/common/test_usb_mock_classes.c delete mode 100644 components/usb/test/common/test_usb_mock_classes.h create mode 100644 components/usb/test_apps/common/CMakeLists.txt rename components/usb/{test => test_apps}/common/test_usb_common.c (85%) rename components/usb/{test => test_apps}/common/test_usb_common.h (87%) create mode 100644 components/usb/test_apps/common/test_usb_mock_hid.c create mode 100644 components/usb/test_apps/common/test_usb_mock_hid.h create mode 100644 components/usb/test_apps/common/test_usb_mock_msc.c create mode 100644 components/usb/test_apps/common/test_usb_mock_msc.h create mode 100644 components/usb/test_apps/hcd/CMakeLists.txt create mode 100644 components/usb/test_apps/hcd/README.md create mode 100644 components/usb/test_apps/hcd/main/CMakeLists.txt create mode 100644 components/usb/test_apps/hcd/main/test_app_main.c rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_bulk.c (83%) rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_common.c (88%) rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_common.h (99%) rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_ctrl.c (91%) rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_intr.c (87%) rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_isoc.c (93%) rename components/usb/{test/hcd => test_apps/hcd/main}/test_hcd_port.c (93%) rename components/usb/{test/usb_host => test_apps/hcd/main}/test_usb_helpers.c (96%) create mode 100644 components/usb/test_apps/hcd/pytest_usb_hcd.py create mode 100644 components/usb/test_apps/hcd/sdkconfig.defaults create mode 100644 components/usb/test_apps/usb_host/CMakeLists.txt create mode 100644 components/usb/test_apps/usb_host/README.md create mode 100644 components/usb/test_apps/usb_host/main/CMakeLists.txt rename components/usb/{test/usb_host => test_apps/usb_host/main}/ctrl_client.h (100%) rename components/usb/{test/usb_host => test_apps/usb_host/main}/ctrl_client_async_seq.c (95%) rename components/usb/{test/usb_host => test_apps/usb_host/main}/msc_client.h (100%) rename components/usb/{test/usb_host => test_apps/usb_host/main}/msc_client_async_dconn.c (98%) rename components/usb/{test/usb_host => test_apps/usb_host/main}/msc_client_async_enum.c (86%) rename components/usb/{test/usb_host => test_apps/usb_host/main}/msc_client_async_seq.c (94%) create mode 100644 components/usb/test_apps/usb_host/main/test_app_main.c rename components/usb/{test/usb_host => test_apps/usb_host/main}/test_usb_host_async.c (83%) rename components/usb/{test/usb_host => test_apps/usb_host/main}/test_usb_host_plugging.c (74%) create mode 100644 components/usb/test_apps/usb_host/pytest_usb_host.py create mode 100644 components/usb/test_apps/usb_host/sdkconfig.defaults diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index 582ea63a4e..2627a72cf1 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -224,7 +224,7 @@ - "components/driver/**/*" - "components/sdmmc/**/*" -# for jobs: example_test_pytest_esp32s3_usb_device and test_app_test_pytest_esp32s2_usb_host: +# for jobs: USB host and device examples .patterns-example_test-usb: &patterns-example_test-usb - "components/hal/usb*.c" - "components/hal/esp32s*/include/hal/usb*.h" @@ -233,6 +233,12 @@ - "examples/peripherals/usb/host/**/**/**/*" - "examples/peripherals/usb/device/**/**/*" +# for jobs: USB component (Host) pytest test_app +.patterns-component_ut-usb: &patterns-component_ut-usb + - "components/hal/usb*.c" + - "components/hal/esp32s*/include/hal/usb*.h" + - "components/usb/**/*" + # for jobs: component_ut_pytest_esp32x_adc: .patterns-component_ut-adc: &patterns-component_ut-adc - "components/esp_adc/**/*" @@ -527,6 +533,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -550,6 +558,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -572,6 +582,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -594,6 +606,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -616,6 +630,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -638,6 +654,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -660,6 +678,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-target_test-wifi @@ -1096,6 +1116,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-adc + - <<: *if-dev-push + changes: *patterns-component_ut-usb - <<: *if-dev-push changes: *patterns-custom_test - <<: *if-dev-push @@ -1381,6 +1403,8 @@ - <<: *if-label-component_ut - <<: *if-label-component_ut_esp32 - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-usb .rules:test:component_ut-esp32-wifi: rules: @@ -1466,6 +1490,8 @@ - <<: *if-label-component_ut - <<: *if-label-component_ut_esp32c2 - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-usb .rules:test:component_ut-esp32c2-wifi: rules: @@ -1551,6 +1577,8 @@ - <<: *if-label-component_ut - <<: *if-label-component_ut_esp32c3 - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-usb .rules:test:component_ut-esp32c3-wifi: rules: @@ -1636,6 +1664,8 @@ - <<: *if-label-component_ut - <<: *if-label-component_ut_esp32h2 - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-usb .rules:test:component_ut-esp32h2-wifi: rules: @@ -1721,6 +1751,8 @@ - <<: *if-label-component_ut - <<: *if-label-component_ut_esp32s2 - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-usb .rules:test:component_ut-esp32s2-wifi: rules: @@ -1806,6 +1838,8 @@ - <<: *if-label-component_ut - <<: *if-label-component_ut_esp32s3 - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-usb .rules:test:component_ut-esp32s3-wifi: rules: diff --git a/components/usb/.build-test-rules.yml b/components/usb/.build-test-rules.yml new file mode 100644 index 0000000000..d11df13540 --- /dev/null +++ b/components/usb/.build-test-rules.yml @@ -0,0 +1,5 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/usb/test_apps: + enable: + - if: SOC_USB_OTG_SUPPORTED == 1 diff --git a/components/usb/test/CMakeLists.txt b/components/usb/test/CMakeLists.txt deleted file mode 100644 index 7857c7ee66..0000000000 --- a/components/usb/test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -idf_build_get_property(target IDF_TARGET) - -#USB Host is currently only supported on ESP32-S2, ESP32S3 chips -if(NOT "${target}" MATCHES "^esp32s[2-3]") - return() -endif() - -idf_component_register( - SRC_DIRS "common" "hcd" "usb_host" - PRIV_INCLUDE_DIRS "../private_include" "common" "hcd" "usb_host" - PRIV_REQUIRES cmock usb test_utils - ) diff --git a/components/usb/test/common/test_usb_mock_classes.c b/components/usb/test/common/test_usb_mock_classes.c deleted file mode 100644 index 1a049fd447..0000000000 --- a/components/usb/test/common/test_usb_mock_classes.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include "usb/usb_types_ch9.h" -#include "test_usb_mock_classes.h" - -// ---------------------------------------------------- MSC SCSI ------------------------------------------------------- - -const char *MSC_CLIENT_TAG = "MSC Client"; - -const uint8_t mock_msc_scsi_dev_desc[] = { - 0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x5F, 0x12, 0x8A, 0xC0, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01, -}; - -const uint8_t mock_msc_scsi_config_desc[] = { - 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0xF0, 0x09, 0x04, 0x00, 0x00, 0x02, 0x08, 0x06, 0x50, 0x00, 0x07, - 0x05, 0x01, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x01, -}; - - -const uint8_t mock_msc_scsi_str_desc_manu[] = { - 0x0c, 0x03, 0x41, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, -}; - -const uint8_t mock_msc_scsi_str_desc_prod[] = { - 0x2c, 0x03, 0x41, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, - 0x00, 0x20, 0x00, 0x46, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x20, 0x00, 0x44, 0x00, 0x72, 0x00, - 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, -}; - -const uint8_t mock_msc_scsi_str_desc_ser_num[] = { - 0x22, 0x03, 0x31, 0x00, 0x33, 0x00, 0x43, 0x00, 0x32, 0x00, 0x38, 0x00, 0x31, 0x00, 0x36, 0x00, 0x35, 0x00, 0x38, - 0x00, 0x32, 0x00, 0x31, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x45, 0x00, -}; - -const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc = { - .bLength = sizeof(usb_ep_desc_t), - .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, - .bEndpointAddress = 0x01, //EP 1 OUT - .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK, - .wMaxPacketSize = 64, //MPS of 64 bytes - .bInterval = 1, -}; - -const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc = { - .bLength = sizeof(usb_ep_desc_t), - .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, - .bEndpointAddress = 0x82, //EP 2 IN - .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK, - .wMaxPacketSize = 64, //MPS of 64 bytes - .bInterval = 1, -}; - -void mock_msc_scsi_init_cbw(mock_msc_bulk_cbw_t *cbw, bool is_read, int offset, int num_sectors, uint32_t tag) -{ - cbw->dCBWSignature = 0x43425355; //Fixed value - cbw->dCBWTag = tag; //Random value that is echoed back - cbw->dCBWDataTransferLength = num_sectors * MOCK_MSC_SCSI_SECTOR_SIZE; - cbw->bmCBWFlags = (is_read) ? (1 << 7) : 0; //If this is a read, set the direction flag - cbw->bCBWLUN = MOCK_MSC_SCSI_LUN; - cbw->bCBWCBLength = 10; //The length of the SCSI command - //Initialize SCSI CMD as READ10 or WRITE 10 - cbw->CBWCB.opcode = (is_read) ? 0x28 : 0x2A; //SCSI CMD READ10 or WRITE10 - cbw->CBWCB.flags = 0; - cbw->CBWCB.lba_3 = (offset >> 24); - cbw->CBWCB.lba_2 = (offset >> 16); - cbw->CBWCB.lba_1 = (offset >> 8); - cbw->CBWCB.lba_0 = (offset >> 0); - cbw->CBWCB.group = 0; - cbw->CBWCB.len_1 = (num_sectors >> 8); - cbw->CBWCB.len_0 = (num_sectors >> 0); - cbw->CBWCB.control = 0; -} - -bool mock_msc_scsi_check_csw(mock_msc_bulk_csw_t *csw, uint32_t tag_expect) -{ - bool no_issues = true; - if (csw->dCSWSignature != 0x53425355) { - no_issues = false; - printf("Warning: csw signature corrupt (0x%"PRIX32")\n", csw->dCSWSignature); - } - if (csw->dCSWTag != tag_expect) { - no_issues = false; - printf("Warning: csw tag unexpected! Expected %"PRIu32" got %"PRIu32"\n", tag_expect, csw->dCSWTag); - } - if (csw->dCSWDataResidue) { - no_issues = false; - printf("Warning: csw indicates data residue of %"PRIu32" bytes!\n", csw->dCSWDataResidue); - } - if (csw->bCSWStatus) { - no_issues = false; - printf("Warning: csw indicates non-good status %d!\n", csw->bCSWStatus); - } - return no_issues; -} - - -// ---------------------------------------------------- HID Mouse ------------------------------------------------------ - -const usb_ep_desc_t mock_hid_mouse_in_ep_desc = { - .bLength = sizeof(usb_ep_desc_t), - .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, - .bEndpointAddress = 0x81, //EP 1 IN - .bmAttributes = USB_BM_ATTRIBUTES_XFER_INT, - .wMaxPacketSize = 4, //MPS of 4 bytes - .bInterval = 10, //Interval of 10ms -}; - -void mock_hid_process_report(mock_hid_mouse_report_t *report, int iter) -{ - static int x_pos = 0; - static int y_pos = 0; - //Update X position - if (report->x_movement & 0x80) { //Positive movement - x_pos += report->x_movement & 0x7F; - } else { //Negative movement - x_pos -= report->x_movement & 0x7F; - } - //Update Y position - if (report->y_movement & 0x80) { //Positive movement - y_pos += report->y_movement & 0x7F; - } else { //Negative movement - y_pos -= report->y_movement & 0x7F; - } - printf("\rX:%d\tY:%d\tIter: %d\n", x_pos, y_pos, iter); -} diff --git a/components/usb/test/common/test_usb_mock_classes.h b/components/usb/test/common/test_usb_mock_classes.h deleted file mode 100644 index 8149f67f49..0000000000 --- a/components/usb/test/common/test_usb_mock_classes.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* -This header contains bare-bone mock implementations of some device classes in order to test various layers of the USB -Host stack. -*/ - -#pragma once - -#include -#include -#include "esp_assert.h" -#include "usb/usb_types_ch9.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// ---------------------------------------------------- MSC SCSI ------------------------------------------------------- - -extern const char *MSC_CLIENT_TAG; - -/* -Note: The mock MSC SCSI tests requires that USB flash drive be connected. The flash drive should... - -- Be implement the Mass Storage class supporting BULK only transfers using SCSI commands -- It's configuration 1 should have the following endpoints - -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 2.00 - bDeviceClass 0 - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x125f - idProduct 0xc08a - bcdDevice 1.00 - iManufacturer 1 - iProduct 2 - iSerial 3 - bNumConfigurations 1 -Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 0x0020 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 480mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 8 Mass Storage - bInterfaceSubClass 6 SCSI - bInterfaceProtocol 80 Bulk-Only - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x01 EP 1 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - -If you're using a flash driver with different endpoints, modify the endpoint descriptors below. -*/ - -//Constant descriptors -extern const uint8_t mock_msc_scsi_dev_desc[]; -extern const uint8_t mock_msc_scsi_config_desc[]; -extern const uint8_t mock_msc_scsi_str_desc_manu[]; -extern const uint8_t mock_msc_scsi_str_desc_prod[]; -extern const uint8_t mock_msc_scsi_str_desc_ser_num[]; -extern const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc; -extern const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc; - -#define MOCK_MSC_SCSI_DEV_ID_VENDOR 0x125F -#define MOCK_MSC_SCSI_DEV_ID_PRODUCT 0xc08A -#define MOCK_MSC_SCSI_DEV_DFLT_EP_MPS 64 -#define MOCK_MSC_SCSI_SECTOR_SIZE 512 -#define MOCK_MSC_SCSI_LUN 0 -#define MOCK_MSC_SCSI_INTF_NUMBER 0 -#define MOCK_MSC_SCSI_INTF_ALT_SETTING 0 -#define MOCK_MSC_SCSI_BULK_OUT_EP_ADDR 0x01 -#define MOCK_MSC_SCSI_BULK_IN_EP_ADDR 0x82 -#define MOCK_MSC_SCSI_BULK_EP_MPS 64 - -#define MOCK_MSC_SCSI_REQ_INIT_RESET(setup_pkt_ptr, intf_num) ({ \ - (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ - (setup_pkt_ptr)->bRequest = 0xFF; \ - (setup_pkt_ptr)->wValue = 0; \ - (setup_pkt_ptr)->wIndex = (intf_num); \ - (setup_pkt_ptr)->wLength = 0; \ -}) - -typedef struct __attribute__((packed)) { - uint8_t opcode; //0x28 = read(10), 0x2A=write(10) - uint8_t flags; - uint8_t lba_3; - uint8_t lba_2; - uint8_t lba_1; - uint8_t lba_0; - uint8_t group; - uint8_t len_1; - uint8_t len_0; - uint8_t control; -} mock_scsi_cmd10_t; - -typedef struct __attribute__((packed)) { - uint32_t dCBWSignature; - uint32_t dCBWTag; - uint32_t dCBWDataTransferLength; - uint8_t bmCBWFlags; - uint8_t bCBWLUN; - uint8_t bCBWCBLength; - mock_scsi_cmd10_t CBWCB; - uint8_t padding[6]; -} mock_msc_bulk_cbw_t; - -// USB Bulk Transfer Command Status Wrapper data -typedef struct __attribute__((packed)) { - uint32_t dCSWSignature; - uint32_t dCSWTag; - uint32_t dCSWDataResidue; - uint8_t bCSWStatus; -} mock_msc_bulk_csw_t; - -/** - * @brief Initialize a MSC Command Block Wrapper (CBW) as an SCSI command - * - * @param cbw CBW structure - * @param is_read Is a read command - * @param offset Block offset - * @param num_sectors Number of sectors to read - * @param tag Tag (this is simply echoed back - */ -void mock_msc_scsi_init_cbw(mock_msc_bulk_cbw_t *cbw, bool is_read, int offset, int num_sectors, uint32_t tag); - -/** - * @brief Check that returned Command Status Wrapper (CSW) is valid - * - * @param csw CSW structure - * @param tag_expect Expected tag - * @return true CSW is valid - * @return false CSW is not valid - */ -bool mock_msc_scsi_check_csw(mock_msc_bulk_csw_t *csw, uint32_t tag_expect); - -// ---------------------------------------------------- HID Mouse ------------------------------------------------------ - - -/* -Note: The mock HID mouse tests require that USB low speed mouse be connected. The mouse should... - -- Be implement the HID with standard report format used by mice -- It's configuration 1 should have the following endpoint - -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 2.00 - bDeviceClass 0 - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 8 - idVendor 0x413c Dell Computer Corp. - idProduct 0x301a - bcdDevice 1.00 - iManufacturer 1 - iProduct 2 - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 0x0022 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xa0 - (Bus Powered) - Remote Wakeup - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 3 Human Interface Device - bInterfaceSubClass 1 Boot Interface Subclass - bInterfaceProtocol 2 Mouse - iInterface 0 - HID Device Descriptor: - bLength 9 - bDescriptorType 33 - bcdHID 1.11 - bCountryCode 0 Not supported - bNumDescriptors 1 - bDescriptorType 34 Report - wDescriptorLength 46 - Report Descriptors: - ** UNAVAILABLE ** - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0004 1x 4 bytes - bInterval 10 - -If you're using another mice with different endpoints, modify the endpoint descriptor below -*/ - -extern const usb_ep_desc_t mock_hid_mouse_in_ep_desc; - -#define MOCK_HID_MOUSE_DEV_ID_VENDOR 0x413C -#define MOCK_HID_MOUSE_DEV_ID_PRODUCT 0x301A -#define MOCK_HID_MOUSE_DEV_DFLT_EP_MPS 8 -#define MOCK_HID_MOUSE_INTF_NUMBER 0 -#define MOCK_HID_MOUSE_INTF_ALT_SETTING 0 -#define MOCK_HID_MOUSE_INTR_IN_EP_ADDR 0x81 -#define MOCK_HID_MOUSE_INTR_IN_MPS 0x04 - -typedef union { - struct { - uint32_t left_button: 1; - uint32_t right_button: 1; - uint32_t middle_button: 1; - uint32_t reserved5: 5; - uint8_t x_movement; - uint8_t y_movement; - } __attribute__((packed)); - uint8_t val[3]; -} mock_hid_mouse_report_t; -ESP_STATIC_ASSERT(sizeof(mock_hid_mouse_report_t) == 3, "Size of HID mouse report incorrect"); - -void mock_hid_process_report(mock_hid_mouse_report_t *report, int iter); - -// ---------------------------------------------------- Mock ISOC ------------------------------------------------------ - -/* -Note: ISOC test rely on communicating with a non existent endpoint using ISOC OUT transfers. Since no ACK is given for -ISOC, transferring to a non-existent endpoint should work. The non-existent endpoint descriptor is described below: -*/ - -#define MOCK_ISOC_EP_NUM 2 -#define MOCK_ISOC_EP_MPS 512 - - -static const usb_ep_desc_t mock_isoc_out_ep_desc = { - .bLength = sizeof(usb_ep_desc_t), - .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, - .bEndpointAddress = MOCK_ISOC_EP_NUM, - .bmAttributes = USB_BM_ATTRIBUTES_XFER_ISOC, - .wMaxPacketSize = MOCK_ISOC_EP_MPS, //MPS of 512 bytes - .bInterval = 1, //Isoc interval is (2 ^ (bInterval - 1)) which means an interval of 1ms -}; - - -#ifdef __cplusplus -} -#endif diff --git a/components/usb/test_apps/common/CMakeLists.txt b/components/usb/test_apps/common/CMakeLists.txt new file mode 100644 index 0000000000..f03c668f9e --- /dev/null +++ b/components/usb/test_apps/common/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_usb_common.c" "test_usb_mock_msc.c" "test_usb_mock_hid.c" + INCLUDE_DIRS "." + REQUIRES usb unity) diff --git a/components/usb/test/common/test_usb_common.c b/components/usb/test_apps/common/test_usb_common.c similarity index 85% rename from components/usb/test/common/test_usb_common.c rename to components/usb/test_apps/common/test_usb_common.c index bc9e32d3c9..2fa98b3dab 100644 --- a/components/usb/test/common/test_usb_common.c +++ b/components/usb/test_apps/common/test_usb_common.c @@ -11,6 +11,7 @@ #include "hal/usb_phy_types.h" #include "esp_private/usb_phy.h" #include "test_usb_common.h" +#include "unity.h" static usb_phy_handle_t phy_hdl = NULL; @@ -25,13 +26,13 @@ void test_usb_init_phy(void) .ext_io_conf = NULL, .otg_io_conf = NULL, }; - ESP_ERROR_CHECK(usb_new_phy(&phy_config, &phy_hdl)); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, usb_new_phy(&phy_config, &phy_hdl), "Failed to init USB PHY"); } void test_usb_deinit_phy(void) { //Deinitialize the internal USB PHY - ESP_ERROR_CHECK(usb_del_phy(phy_hdl)); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, usb_del_phy(phy_hdl), "Failed to delete PHY"); phy_hdl = NULL; } diff --git a/components/usb/test/common/test_usb_common.h b/components/usb/test_apps/common/test_usb_common.h similarity index 87% rename from components/usb/test/common/test_usb_common.h rename to components/usb/test_apps/common/test_usb_common.h index ac88f3432e..cf055d4f4d 100644 --- a/components/usb/test/common/test_usb_common.h +++ b/components/usb/test_apps/common/test_usb_common.h @@ -13,7 +13,7 @@ void test_usb_init_phy(void); /** - * @brief Deinitalize the internal USB PHY and USB Controller after USB Host testing + * @brief Deinitialize the internal USB PHY and USB Controller after USB Host testing */ void test_usb_deinit_phy(void); diff --git a/components/usb/test_apps/common/test_usb_mock_hid.c b/components/usb/test_apps/common/test_usb_mock_hid.c new file mode 100644 index 0000000000..ea7dc32af9 --- /dev/null +++ b/components/usb/test_apps/common/test_usb_mock_hid.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "usb/usb_types_ch9.h" +#include "test_usb_mock_hid.h" + +// ---------------------------------------------------- HID Mouse ------------------------------------------------------ + +const usb_ep_desc_t mock_hid_mouse_in_ep_desc = { + .bLength = sizeof(usb_ep_desc_t), + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, + .bEndpointAddress = MOCK_HID_MOUSE_INTR_IN_EP_ADDR, //EP 1 IN + .bmAttributes = USB_BM_ATTRIBUTES_XFER_INT, + .wMaxPacketSize = MOCK_HID_MOUSE_INTR_IN_MPS, + .bInterval = 10, //Interval of 10ms +}; + +void mock_hid_process_report(mock_hid_mouse_report_t *report, int iter) +{ + static int x_pos = 0; + static int y_pos = 0; + //Update X position + if (report->x_movement & 0x80) { //Positive movement + x_pos += report->x_movement & 0x7F; + } else { //Negative movement + x_pos -= report->x_movement & 0x7F; + } + //Update Y position + if (report->y_movement & 0x80) { //Positive movement + y_pos += report->y_movement & 0x7F; + } else { //Negative movement + y_pos -= report->y_movement & 0x7F; + } + printf("\rX:%d\tY:%d\tIter: %d\n", x_pos, y_pos, iter); +} diff --git a/components/usb/test_apps/common/test_usb_mock_hid.h b/components/usb/test_apps/common/test_usb_mock_hid.h new file mode 100644 index 0000000000..8cbf22d33c --- /dev/null +++ b/components/usb/test_apps/common/test_usb_mock_hid.h @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* +This header contains bare-bone mock implementations of some device classes in order to test various layers of the USB +Host stack. +*/ + +#pragma once + +#include +#include +#include "esp_assert.h" +#include "usb/usb_types_ch9.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ---------------------------------------------------- HID Mouse ------------------------------------------------------ + + +/* +Note: The mock HID mouse tests require that USB low speed mouse be connected. The mouse should... + +- Be implement the HID with standard report format used by mice +- It's configuration 1 should have the following endpoint + + ------------------ Configuration Descriptor ------------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x02 (Configuration Descriptor) +wTotalLength : 0x003B (59 bytes) +bNumInterfaces : 0x02 (2 Interfaces) +bConfigurationValue : 0x01 (Configuration 1) +iConfiguration : 0x00 (No String Descriptor) +bmAttributes : 0xA0 + D7: Reserved, set 1 : 0x01 + D6: Self Powered : 0x00 (no) + D5: Remote Wakeup : 0x01 (yes) + D4..0: Reserved, set 0 : 0x00 +MaxPower : 0x32 (100 mA) +Data (HexDump) : 09 02 3B 00 02 01 00 A0 32 09 04 00 00 01 03 01 + 02 00 09 21 00 02 00 01 22 4D 00 07 05 81 03 08 + 00 0A 09 04 01 00 01 03 01 01 00 09 21 00 02 00 + 01 22 31 00 07 05 82 03 08 00 0A + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x00 +bAlternateSetting : 0x00 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x03 (HID - Human Interface Device) +bInterfaceSubClass : 0x01 (Boot Interface) +bInterfaceProtocol : 0x02 (Mouse) +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 00 00 01 03 01 02 00 + + ------------------- HID Descriptor -------------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x21 (HID Descriptor) +bcdHID : 0x0200 (HID Version 2.00) +bCountryCode : 0x00 (00 = not localized) +bNumDescriptors : 0x01 +Data (HexDump) : 09 21 00 02 00 01 22 4D 00 +Descriptor 1: +bDescriptorType : 0x22 (Class=Report) +wDescriptorLength : 0x004D (77 bytes) +Error reading descriptor : ERROR_INVALID_PARAMETER (due to a obscure limitation of the Win32 USB API, see UsbTreeView.txt) + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x03 (TransferType=Interrupt) +wMaxPacketSize : 0x0008 +bInterval : 0x0A (10 ms) +Data (HexDump) : 07 05 81 03 08 00 0A + +If you're using another mice with different endpoints, modify the endpoint descriptor below +*/ + +extern const usb_ep_desc_t mock_hid_mouse_in_ep_desc; + +#define MOCK_HID_MOUSE_DEV_ID_VENDOR 0x03F0 +#define MOCK_HID_MOUSE_DEV_ID_PRODUCT 0x1198 +#define MOCK_HID_MOUSE_DEV_DFLT_EP_MPS 8 +#define MOCK_HID_MOUSE_INTF_NUMBER 0 +#define MOCK_HID_MOUSE_INTF_ALT_SETTING 0 +#define MOCK_HID_MOUSE_INTR_IN_EP_ADDR 0x81 +#define MOCK_HID_MOUSE_INTR_IN_MPS 8 + +typedef union { + struct { + uint32_t left_button: 1; + uint32_t right_button: 1; + uint32_t middle_button: 1; + uint32_t reserved5: 5; + uint8_t x_movement; + uint8_t y_movement; + } __attribute__((packed)); + uint8_t val[3]; +} mock_hid_mouse_report_t; +ESP_STATIC_ASSERT(sizeof(mock_hid_mouse_report_t) == 3, "Size of HID mouse report incorrect"); + +void mock_hid_process_report(mock_hid_mouse_report_t *report, int iter); + +#ifdef __cplusplus +} +#endif diff --git a/components/usb/test_apps/common/test_usb_mock_msc.c b/components/usb/test_apps/common/test_usb_mock_msc.c new file mode 100644 index 0000000000..6307ddd926 --- /dev/null +++ b/components/usb/test_apps/common/test_usb_mock_msc.c @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "usb/usb_types_ch9.h" +#include "test_usb_mock_msc.h" + +// ---------------------------------------------------- MSC SCSI ------------------------------------------------------- + +const char *MSC_CLIENT_TAG = "MSC Client"; + +const usb_device_desc_t mock_msc_scsi_dev_desc = { + .bLength = USB_DEVICE_DESC_SIZE, + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_DEVICE, + .bcdUSB = MOCK_MSC_SCSI_USB_VERSION, + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = MOCK_MSC_SCSI_DEV_DFLT_EP_MPS, + .idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR, + .idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT, + .bcdDevice = MOCK_MSC_SCSI_DEV_VERSION, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +#define MOCK_MSC_SCSI_WTOTALLENGTH (USB_CONFIG_DESC_SIZE + USB_INTF_DESC_SIZE + 2*USB_EP_DESC_SIZE) +static const usb_config_desc_t mock_msc_config_desc = { + .bLength = USB_CONFIG_DESC_SIZE, + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_CONFIGURATION, + .wTotalLength = MOCK_MSC_SCSI_WTOTALLENGTH, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x70, //224mA +}; + +static const usb_intf_desc_t mock_msc_intf_desc = { + .bLength = USB_INTF_DESC_SIZE, + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_INTERFACE, + .bInterfaceNumber = MOCK_MSC_SCSI_INTF_NUMBER, + .bAlternateSetting = MOCK_MSC_SCSI_INTF_ALT_SETTING, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, //SCSI + .bInterfaceProtocol = 0x50, //Bulk only + .iInterface = 0, +}; + +uint8_t mock_msc_scsi_config_desc[255]; +uint16_t mock_msc_scsi_str_desc_manu[128]; +uint16_t mock_msc_scsi_str_desc_prod[128]; +uint16_t mock_msc_scsi_str_desc_ser_num[128]; + +const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc = { + .bLength = sizeof(usb_ep_desc_t), + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, + .bEndpointAddress = MOCK_MSC_SCSI_BULK_OUT_EP_ADDR, //EP 1 OUT + .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK, + .wMaxPacketSize = MOCK_MSC_SCSI_BULK_EP_MPS, //MPS of 64 bytes + .bInterval = 0, +}; + +const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc = { + .bLength = sizeof(usb_ep_desc_t), + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, + .bEndpointAddress = MOCK_MSC_SCSI_BULK_IN_EP_ADDR, + .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK, + .wMaxPacketSize = MOCK_MSC_SCSI_BULK_EP_MPS, //MPS of 64 bytes + .bInterval = 0, +}; + +void mock_msc_scsi_init_cbw(mock_msc_bulk_cbw_t *cbw, bool is_read, int offset, int num_sectors, uint32_t tag) +{ + cbw->dCBWSignature = 0x43425355; //Fixed value + cbw->dCBWTag = tag; //Random value that is echoed back + cbw->dCBWDataTransferLength = num_sectors * MOCK_MSC_SCSI_SECTOR_SIZE; + cbw->bmCBWFlags = (is_read) ? (1 << 7) : 0; //If this is a read, set the direction flag + cbw->bCBWLUN = MOCK_MSC_SCSI_LUN; + cbw->bCBWCBLength = 10; //The length of the SCSI command + //Initialize SCSI CMD as READ10 or WRITE 10 + cbw->CBWCB.opcode = (is_read) ? 0x28 : 0x2A; //SCSI CMD READ10 or WRITE10 + cbw->CBWCB.flags = 0; + cbw->CBWCB.lba_3 = (offset >> 24); + cbw->CBWCB.lba_2 = (offset >> 16); + cbw->CBWCB.lba_1 = (offset >> 8); + cbw->CBWCB.lba_0 = (offset >> 0); + cbw->CBWCB.group = 0; + cbw->CBWCB.len_1 = (num_sectors >> 8); + cbw->CBWCB.len_0 = (num_sectors >> 0); + cbw->CBWCB.control = 0; +} + +bool mock_msc_scsi_check_csw(mock_msc_bulk_csw_t *csw, uint32_t tag_expect) +{ + bool no_issues = true; + if (csw->dCSWSignature != 0x53425355) { + no_issues = false; + printf("Warning: csw signature corrupt (0x%"PRIX32")\n", csw->dCSWSignature); + } + if (csw->dCSWTag != tag_expect) { + no_issues = false; + printf("Warning: csw tag unexpected! Expected %"PRIu32" got %"PRIu32"\n", tag_expect, csw->dCSWTag); + } + if (csw->dCSWDataResidue) { + no_issues = false; + printf("Warning: csw indicates data residue of %"PRIu32" bytes!\n", csw->dCSWDataResidue); + } + if (csw->bCSWStatus) { + no_issues = false; + printf("Warning: csw indicates non-good status %d!\n", csw->bCSWStatus); + } + return no_issues; +} + +void mock_msc_scsi_init_reference_descriptors(void) +{ + // Configuration descriptor + uint8_t *dest_ptr = mock_msc_scsi_config_desc; + memcpy(dest_ptr, (void*)&mock_msc_config_desc, sizeof(mock_msc_config_desc)); + dest_ptr += USB_CONFIG_DESC_SIZE; + memcpy(dest_ptr, (void*)&mock_msc_intf_desc, sizeof(mock_msc_intf_desc)); + dest_ptr += USB_INTF_DESC_SIZE; + memcpy(dest_ptr, (void*)&mock_msc_scsi_bulk_in_ep_desc, sizeof(mock_msc_scsi_bulk_in_ep_desc)); + dest_ptr += USB_EP_DESC_SIZE; + memcpy(dest_ptr, (void*)&mock_msc_scsi_bulk_out_ep_desc, sizeof(mock_msc_scsi_bulk_out_ep_desc)); + + // String descriptors + const char *str = MOCK_MSC_SCSI_STRING_1; + uint8_t chr_count = strlen(str); + mock_msc_scsi_str_desc_manu[0] = (USB_B_DESCRIPTOR_TYPE_STRING << 8 ) | (2 * chr_count + 2); // first byte is length (including header), second byte is string type + for (uint8_t i = 0; i < chr_count; i++) { + mock_msc_scsi_str_desc_manu[1 + i] = str[i]; + } + + str = MOCK_MSC_SCSI_STRING_2; + chr_count = strlen(str); + mock_msc_scsi_str_desc_prod[0] = (USB_B_DESCRIPTOR_TYPE_STRING << 8 ) | (2 * chr_count + 2); // first byte is length (including header), second byte is string type + for (uint8_t i = 0; i < chr_count; i++) { + mock_msc_scsi_str_desc_prod[1 + i] = str[i]; + } + + str = MOCK_MSC_SCSI_STRING_3; + chr_count = strlen(str); + mock_msc_scsi_str_desc_ser_num[0] = (USB_B_DESCRIPTOR_TYPE_STRING << 8 ) | (2 * chr_count + 2); // first byte is length (including header), second byte is string type + for (uint8_t i = 0; i < chr_count; i++) { + mock_msc_scsi_str_desc_ser_num[1 + i] = str[i]; + } +} diff --git a/components/usb/test_apps/common/test_usb_mock_msc.h b/components/usb/test_apps/common/test_usb_mock_msc.h new file mode 100644 index 0000000000..4d5685d56e --- /dev/null +++ b/components/usb/test_apps/common/test_usb_mock_msc.h @@ -0,0 +1,196 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* +This header contains bare-bone mock implementations of some device classes in order to test various layers of the USB +Host stack. +*/ + +#pragma once + +#include +#include +#include "esp_assert.h" +#include "usb/usb_types_ch9.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ---------------------------------------------------- MSC SCSI ------------------------------------------------------- + +extern const char *MSC_CLIENT_TAG; + +/* +Note: The mock MSC SCSI tests requires that USB flash drive be connected. The flash drive should... + +- Be implement the Mass Storage class supporting BULK only transfers using SCSI commands +- It's configuration 1 should have the following endpoints + + ------------------ Configuration Descriptor ------------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x02 (Configuration Descriptor) +wTotalLength : 0x0020 (32 bytes) +bNumInterfaces : 0x01 (1 Interface) +bConfigurationValue : 0x01 (Configuration 1) +iConfiguration : 0x00 (No String Descriptor) +bmAttributes : 0x80 + D7: Reserved, set 1 : 0x01 + D6: Self Powered : 0x00 (no) + D5: Remote Wakeup : 0x00 (no) + D4..0: Reserved, set 0 : 0x00 +MaxPower : 0x70 (224 mA) +Data (HexDump) : 09 02 20 00 01 01 00 80 70 09 04 00 00 02 08 06 + 50 00 07 05 81 02 00 02 00 07 05 02 02 00 02 00 + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x00 +bAlternateSetting : 0x00 +bNumEndpoints : 0x02 (2 Endpoints) +bInterfaceClass : 0x08 (Mass Storage) +bInterfaceSubClass : 0x06 (SCSI transparent command set) +bInterfaceProtocol : 0x50 (Bulk-Only Transport) +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 00 00 02 08 06 50 00 + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x02 (TransferType=Bulk) +wMaxPacketSize : 0x0040 (max 64 bytes) +bInterval : 0x00 (never NAKs) +Data (HexDump) : 07 05 81 02 40 00 00 + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x02 (Direction=OUT EndpointID=2) +bmAttributes : 0x02 (TransferType=Bulk) +wMaxPacketSize : 0x0040 (max 64 bytes) +bInterval : 0x00 (never NAKs) +Data (HexDump) : 07 05 02 02 40 00 00 + +If you're using a flash driver with different endpoints, modify the endpoint descriptors below. +*/ + +//Constant descriptors +extern const usb_device_desc_t mock_msc_scsi_dev_desc; +extern uint8_t mock_msc_scsi_config_desc[255]; +extern uint16_t mock_msc_scsi_str_desc_manu[128]; +extern uint16_t mock_msc_scsi_str_desc_prod[128]; +extern uint16_t mock_msc_scsi_str_desc_ser_num[128]; +extern const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc; +extern const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc; + +#define MOCK_MSC_SCSI_DEV_ID_VENDOR 0x0781 // Western Digital, Sandisk +#define MOCK_MSC_SCSI_DEV_ID_PRODUCT 0x5595 +#define MOCK_MSC_SCSI_DEV_VERSION 0x0100 //1.00 +#define MOCK_MSC_SCSI_USB_VERSION 0x0210 //2.10 +#define MOCK_MSC_SCSI_DEV_DFLT_EP_MPS 64 +#define MOCK_MSC_SCSI_SECTOR_SIZE 512 +#define MOCK_MSC_SCSI_LUN 0 +#define MOCK_MSC_SCSI_INTF_NUMBER 0 +#define MOCK_MSC_SCSI_INTF_ALT_SETTING 0 +#define MOCK_MSC_SCSI_BULK_OUT_EP_ADDR 0x02 +#define MOCK_MSC_SCSI_BULK_IN_EP_ADDR 0x81 +#define MOCK_MSC_SCSI_BULK_EP_MPS 64 +#define MOCK_MSC_SCSI_STRING_1 (" USB") +#define MOCK_MSC_SCSI_STRING_2 (" SanDisk 3.2Gen1") +#define MOCK_MSC_SCSI_STRING_3 ("0101cdd1e856b427bbb796f870561a4b2b817af9da9872c8d75217cccdd5d5eccb3a0000000000000000000096abe1a3ff83610095558107aea948b4") // This string is NOT checked by the enum test + +#define MOCK_MSC_SCSI_REQ_INIT_RESET(setup_pkt_ptr, intf_num) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ + (setup_pkt_ptr)->bRequest = 0xFF; \ + (setup_pkt_ptr)->wValue = 0; \ + (setup_pkt_ptr)->wIndex = (intf_num); \ + (setup_pkt_ptr)->wLength = 0; \ +}) + +typedef struct __attribute__((packed)) { + uint8_t opcode; //0x28 = read(10), 0x2A=write(10) + uint8_t flags; + uint8_t lba_3; + uint8_t lba_2; + uint8_t lba_1; + uint8_t lba_0; + uint8_t group; + uint8_t len_1; + uint8_t len_0; + uint8_t control; +} mock_scsi_cmd10_t; + +typedef struct __attribute__((packed)) { + uint32_t dCBWSignature; + uint32_t dCBWTag; + uint32_t dCBWDataTransferLength; + uint8_t bmCBWFlags; + uint8_t bCBWLUN; + uint8_t bCBWCBLength; + mock_scsi_cmd10_t CBWCB; + uint8_t padding[6]; +} mock_msc_bulk_cbw_t; + +// USB Bulk Transfer Command Status Wrapper data +typedef struct __attribute__((packed)) { + uint32_t dCSWSignature; + uint32_t dCSWTag; + uint32_t dCSWDataResidue; + uint8_t bCSWStatus; +} mock_msc_bulk_csw_t; + +/** + * @brief Initialize a MSC Command Block Wrapper (CBW) as an SCSI command + * + * @param cbw CBW structure + * @param is_read Is a read command + * @param offset Block offset + * @param num_sectors Number of sectors to read + * @param tag Tag (this is simply echoed back + */ +void mock_msc_scsi_init_cbw(mock_msc_bulk_cbw_t *cbw, bool is_read, int offset, int num_sectors, uint32_t tag); + +/** + * @brief Check that returned Command Status Wrapper (CSW) is valid + * + * @param csw CSW structure + * @param tag_expect Expected tag + * @return true CSW is valid + * @return false CSW is not valid + */ +bool mock_msc_scsi_check_csw(mock_msc_bulk_csw_t *csw, uint32_t tag_expect); + +/** + * @brief Construct configuration and string descriptors + */ +void mock_msc_scsi_init_reference_descriptors(void); + +// ---------------------------------------------------- Mock ISOC ------------------------------------------------------ + +/* +Note: ISOC test rely on communicating with a non existent endpoint using ISOC OUT transfers. Since no ACK is given for +ISOC, transferring to a non-existent endpoint should work. The non-existent endpoint descriptor is described below: +*/ + +#define MOCK_ISOC_EP_NUM 2 +#define MOCK_ISOC_EP_MPS 512 + + +static const usb_ep_desc_t mock_isoc_out_ep_desc = { + .bLength = sizeof(usb_ep_desc_t), + .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT, + .bEndpointAddress = MOCK_ISOC_EP_NUM, + .bmAttributes = USB_BM_ATTRIBUTES_XFER_ISOC, + .wMaxPacketSize = MOCK_ISOC_EP_MPS, //MPS of 512 bytes + .bInterval = 1, //Isoc interval is (2 ^ (bInterval - 1)) which means an interval of 1ms +}; + + +#ifdef __cplusplus +} +#endif diff --git a/components/usb/test_apps/hcd/CMakeLists.txt b/components/usb/test_apps/hcd/CMakeLists.txt new file mode 100644 index 0000000000..e0c09e3113 --- /dev/null +++ b/components/usb/test_apps/hcd/CMakeLists.txt @@ -0,0 +1,6 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(EXTRA_COMPONENT_DIRS "../common") +project(test_app_usb_host) diff --git a/components/usb/test_apps/hcd/README.md b/components/usb/test_apps/hcd/README.md new file mode 100644 index 0000000000..8dad6d2f95 --- /dev/null +++ b/components/usb/test_apps/hcd/README.md @@ -0,0 +1,10 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# USB: Host test application + +There are two sets of tests in this application: +1. Low-speed: Expects low-speed USB mouse with interrupt endpoint to be connected +2. Full-speed: Expects full-speed USB flash disk with 2 bulk endpoints to be connected + +For running these tests locally, you will have to update device definitions (VID, PID, ...) in [test_usb_mock_classes.h](../common/test_usb_mock_classes.h). diff --git a/components/usb/test_apps/hcd/main/CMakeLists.txt b/components/usb/test_apps/hcd/main/CMakeLists.txt new file mode 100644 index 0000000000..707fca965d --- /dev/null +++ b/components/usb/test_apps/hcd/main/CMakeLists.txt @@ -0,0 +1,6 @@ +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "../../../private_include" "." + REQUIRES usb unity common + WHOLE_ARCHIVE) diff --git a/components/usb/test_apps/hcd/main/test_app_main.c b/components/usb/test_apps/hcd/main/test_app_main.c new file mode 100644 index 0000000000..ad2894ed3f --- /dev/null +++ b/components/usb/test_apps/hcd/main/test_app_main.c @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "test_hcd_common.h" + +void setUp(void) +{ + unity_utils_record_free_mem(); + port_hdl = test_hcd_setup(); +} + +void tearDown(void) +{ + //Short delay to allow task to be cleaned up + vTaskDelay(10); + test_hcd_teardown(port_hdl); + port_hdl = NULL; + unity_utils_evaluate_leaks(); +} + +void app_main(void) +{ + // ____ ___ ___________________ __ __ + // | | \/ _____/\______ \ _/ |_ ____ _______/ |_ + // | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\. + // | | / / \ | | \ | | \ ___/ \___ \ | | + // |______/ /_______ / |______ / |__| \___ >____ > |__| + // \/ \/ \/ \/ + printf(" ____ ___ ___________________ __ __ \r\n"); + printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n"); + printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n"); + printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n"); + printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n"); + printf(" \\/ \\/ \\/ \\/ \r\n"); + + unity_utils_setup_heap_record(80); + unity_utils_set_leak_level(128); + unity_run_menu(); +} diff --git a/components/usb/test/hcd/test_hcd_bulk.c b/components/usb/test_apps/hcd/main/test_hcd_bulk.c similarity index 83% rename from components/usb/test/hcd/test_hcd_bulk.c rename to components/usb/test_apps/hcd/main/test_hcd_bulk.c index e1bd189c27..c425c5b2d3 100644 --- a/components/usb/test/hcd/test_hcd_bulk.c +++ b/components/usb/test_apps/hcd/main/test_hcd_bulk.c @@ -9,8 +9,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "unity.h" -#include "test_utils.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "test_hcd_common.h" // --------------------------------------------------- Test Cases ------------------------------------------------------ @@ -25,8 +24,8 @@ static void mock_msc_reset_req(hcd_pipe_handle_t default_pipe) //Enqueue, wait, dequeue, and check URB TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb)); test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE); - TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_PTR(urb, hcd_urb_dequeue(default_pipe)); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); //Free URB test_hcd_free_urb(urb); } @@ -53,9 +52,8 @@ Procedure: #define TEST_NUM_SECTORS_TOTAL 10 #define TEST_NUM_SECTORS_PER_XFER 2 -TEST_CASE("Test HCD bulk pipe URBs", "[hcd][ignore]") +TEST_CASE("Test HCD bulk pipe URBs", "[bulk][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -80,20 +78,20 @@ TEST_CASE("Test HCD bulk pipe URBs", "[hcd][ignore]") mock_msc_scsi_init_cbw((mock_msc_bulk_cbw_t *)urb_cbw->transfer.data_buffer, true, block_num, TEST_NUM_SECTORS_PER_XFER, 0xAAAAAAAA); TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(bulk_out_pipe, urb_cbw)); test_hcd_expect_pipe_event(bulk_out_pipe, HCD_PIPE_EVENT_URB_DONE); - TEST_ASSERT_EQUAL(urb_cbw, hcd_urb_dequeue(bulk_out_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb_cbw->transfer.status); + TEST_ASSERT_EQUAL_PTR(urb_cbw, hcd_urb_dequeue(bulk_out_pipe)); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb_cbw->transfer.status, "Transfer NOT completed"); //Read data through BULK IN pipe TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(bulk_in_pipe, urb_data)); test_hcd_expect_pipe_event(bulk_in_pipe, HCD_PIPE_EVENT_URB_DONE); - TEST_ASSERT_EQUAL(urb_data, hcd_urb_dequeue(bulk_in_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb_data->transfer.status); + TEST_ASSERT_EQUAL_PTR(urb_data, hcd_urb_dequeue(bulk_in_pipe)); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb_data->transfer.status, "Transfer NOT completed"); //Read the CSW through BULK IN pipe TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(bulk_in_pipe, urb_csw)); test_hcd_expect_pipe_event(bulk_in_pipe, HCD_PIPE_EVENT_URB_DONE); - TEST_ASSERT_EQUAL(urb_csw, hcd_urb_dequeue(bulk_in_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb_data->transfer.status); + TEST_ASSERT_EQUAL_PTR(urb_csw, hcd_urb_dequeue(bulk_in_pipe)); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb_data->transfer.status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(sizeof(mock_msc_bulk_csw_t), urb_csw->transfer.actual_num_bytes); - TEST_ASSERT_EQUAL(true, mock_msc_scsi_check_csw((mock_msc_bulk_csw_t *)urb_csw->transfer.data_buffer, 0xAAAAAAAA)); + TEST_ASSERT_TRUE(mock_msc_scsi_check_csw((mock_msc_bulk_csw_t *)urb_csw->transfer.data_buffer, 0xAAAAAAAA)); //Print the read data printf("Block %d to %d:\n", block_num, block_num + TEST_NUM_SECTORS_PER_XFER); for (int i = 0; i < urb_data->transfer.actual_num_bytes; i++) { @@ -110,5 +108,4 @@ TEST_CASE("Test HCD bulk pipe URBs", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Cleanup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } diff --git a/components/usb/test/hcd/test_hcd_common.c b/components/usb/test_apps/hcd/main/test_hcd_common.c similarity index 88% rename from components/usb/test/hcd/test_hcd_common.c rename to components/usb/test_apps/hcd/main/test_hcd_common.c index 3741289dd8..7ac22ee4c3 100644 --- a/components/usb/test/hcd/test_hcd_common.c +++ b/components/usb/test_apps/hcd/main/test_hcd_common.c @@ -8,7 +8,7 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" -#include "test_utils.h" +#include "freertos/task.h" #include "soc/usb_wrap_struct.h" #include "esp_intr_alloc.h" #include "esp_err.h" @@ -19,6 +19,7 @@ #include "usb/usb_types_ch9.h" #include "test_hcd_common.h" #include "test_usb_common.h" +#include "unity.h" #define PORT_NUM 1 #define EVENT_QUEUE_LEN 5 @@ -35,6 +36,8 @@ typedef struct { hcd_pipe_event_t pipe_event; } pipe_event_msg_t; +hcd_port_handle_t port_hdl = NULL; + // ---------------------------------------------------- Private -------------------------------------------------------- /** @@ -52,7 +55,7 @@ static bool port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_even //We store the port's queue handle in the port's context variable void *port_ctx = hcd_port_get_context(port_hdl); QueueHandle_t port_evt_queue = (QueueHandle_t)port_ctx; - TEST_ASSERT(in_isr); //Current HCD implementation should never call a port callback in a task context + TEST_ASSERT_TRUE(in_isr); //Current HCD implementation should never call a port callback in a task context port_event_msg_t msg = { .port_hdl = port_hdl, .port_event = port_event, @@ -95,13 +98,14 @@ void test_hcd_expect_port_event(hcd_port_handle_t port_hdl, hcd_port_event_t exp { //Get the port event queue from the port's context variable QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl); - TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue); + TEST_ASSERT_NOT_NULL(port_evt_queue); //Wait for port callback to send an event message port_event_msg_t msg; - xQueueReceive(port_evt_queue, &msg, portMAX_DELAY); + BaseType_t ret = xQueueReceive(port_evt_queue, &msg, pdMS_TO_TICKS(5000)); + TEST_ASSERT_EQUAL_MESSAGE(pdPASS, ret, "Port event not generated on time"); //Check the contents of that event message TEST_ASSERT_EQUAL(port_hdl, msg.port_hdl); - TEST_ASSERT_EQUAL(expected_event, msg.port_event); + TEST_ASSERT_EQUAL_MESSAGE(expected_event, msg.port_event, "Unexpected event"); printf("\t-> Port event\n"); } @@ -109,20 +113,21 @@ void test_hcd_expect_pipe_event(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t exp { //Get the pipe's event queue from the pipe's context variable QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl); - TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue); + TEST_ASSERT_NOT_NULL(pipe_evt_queue); //Wait for pipe callback to send an event message pipe_event_msg_t msg; - xQueueReceive(pipe_evt_queue, &msg, portMAX_DELAY); + BaseType_t ret = xQueueReceive(pipe_evt_queue, &msg, pdMS_TO_TICKS(5000)); + TEST_ASSERT_EQUAL_MESSAGE(pdPASS, ret, "Pipe event not generated on time"); //Check the contents of that event message TEST_ASSERT_EQUAL(pipe_hdl, msg.pipe_hdl); - TEST_ASSERT_EQUAL(expected_event, msg.pipe_event); + TEST_ASSERT_EQUAL_MESSAGE(expected_event, msg.pipe_event, "Unexpected event"); } int test_hcd_get_num_port_events(hcd_port_handle_t port_hdl) { //Get the port event queue from the port's context variable QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl); - TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue); + TEST_ASSERT_NOT_NULL(port_evt_queue); return EVENT_QUEUE_LEN - uxQueueSpacesAvailable(port_evt_queue); } @@ -130,7 +135,7 @@ int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl) { //Get the pipe's event queue from the pipe's context variable QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl); - TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue); + TEST_ASSERT_NOT_NULL(pipe_evt_queue); return EVENT_QUEUE_LEN - uxQueueSpacesAvailable(pipe_evt_queue); } @@ -141,7 +146,7 @@ hcd_port_handle_t test_hcd_setup(void) test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing //Create a queue for port callback to queue up port events QueueHandle_t port_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(port_event_msg_t)); - TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue); + TEST_ASSERT_NOT_NULL(port_evt_queue); //Install HCD hcd_config_t hcd_config = { .intr_flags = ESP_INTR_FLAG_LEVEL1, @@ -156,7 +161,7 @@ hcd_port_handle_t test_hcd_setup(void) }; hcd_port_handle_t port_hdl; TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl)); - TEST_ASSERT_NOT_EQUAL(NULL, port_hdl); + TEST_ASSERT_NOT_NULL(port_hdl); TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl)); test_usb_set_phy_state(false, 0); //Force disconnected state on PHY return port_hdl; @@ -164,9 +169,12 @@ hcd_port_handle_t test_hcd_setup(void) void test_hcd_teardown(hcd_port_handle_t port_hdl) { + if (!port_hdl) { + return; // In case of setup stage failure, don't run tear-down stage + } //Get the queue handle from the port's context variable QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl); - TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue); + TEST_ASSERT_NOT_NULL(port_evt_queue); //Deinitialize a port TEST_ASSERT_EQUAL(ESP_OK, hcd_port_deinit(port_hdl)); //Uninstall the HCD @@ -226,7 +234,7 @@ hcd_pipe_handle_t test_hcd_pipe_alloc(hcd_port_handle_t port_hdl, const usb_ep_d { //Create a queue for pipe callback to queue up pipe events QueueHandle_t pipe_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(pipe_event_msg_t)); - TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue); + TEST_ASSERT_NOT_NULL(pipe_evt_queue); printf("Creating pipe\n"); hcd_pipe_config_t pipe_config = { .callback = pipe_callback, @@ -238,7 +246,7 @@ hcd_pipe_handle_t test_hcd_pipe_alloc(hcd_port_handle_t port_hdl, const usb_ep_d }; hcd_pipe_handle_t pipe_hdl; TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_alloc(port_hdl, &pipe_config, &pipe_hdl)); - TEST_ASSERT_NOT_EQUAL(NULL, pipe_hdl); + TEST_ASSERT_NOT_NULL(pipe_hdl); return pipe_hdl; } @@ -246,7 +254,7 @@ void test_hcd_pipe_free(hcd_pipe_handle_t pipe_hdl) { //Get the pipe's event queue from its context variable QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl); - TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue); + TEST_ASSERT_NOT_NULL(pipe_evt_queue); //Free the pipe and queue TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_free(pipe_hdl)); vQueueDelete(pipe_evt_queue); @@ -257,8 +265,8 @@ urb_t *test_hcd_alloc_urb(int num_isoc_packets, size_t data_buffer_size) //Allocate a URB and data buffer urb_t *urb = heap_caps_calloc(1, sizeof(urb_t) + (num_isoc_packets * sizeof(usb_isoc_packet_desc_t)), MALLOC_CAP_DEFAULT); uint8_t *data_buffer = heap_caps_malloc(data_buffer_size, MALLOC_CAP_DMA); - TEST_ASSERT_NOT_EQUAL(NULL, urb); - TEST_ASSERT_NOT_EQUAL(NULL, data_buffer); + TEST_ASSERT_NOT_NULL(urb); + TEST_ASSERT_NOT_NULL(data_buffer); //Initialize URB and underlying transfer structure. Need to cast to dummy due to const fields usb_transfer_dummy_t *transfer_dummy = (usb_transfer_dummy_t *)&urb->transfer; transfer_dummy->data_buffer = data_buffer; @@ -286,7 +294,7 @@ uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe) TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb)); test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE); TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); //Update the MPS of the default pipe usb_device_desc_t *device_desc = (usb_device_desc_t *)(urb->transfer.data_buffer + sizeof(usb_setup_packet_t)); @@ -298,7 +306,7 @@ uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe) TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb)); test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE); TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); //Update address of default pipe TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_dev_addr(default_pipe, ENUM_ADDR)); @@ -309,7 +317,7 @@ uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe) TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb)); test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE); TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe)); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); //Free URB test_hcd_free_urb(urb); diff --git a/components/usb/test/hcd/test_hcd_common.h b/components/usb/test_apps/hcd/main/test_hcd_common.h similarity index 99% rename from components/usb/test/hcd/test_hcd_common.h rename to components/usb/test_apps/hcd/main/test_hcd_common.h index 282e733528..6d772db672 100644 --- a/components/usb/test/hcd/test_hcd_common.h +++ b/components/usb/test_apps/hcd/main/test_hcd_common.h @@ -10,6 +10,8 @@ #include "usb_private.h" #include "usb/usb_types_ch9.h" +extern hcd_port_handle_t port_hdl; + #define URB_CONTEXT_VAL ((void *)0xDEADBEEF) // ------------------------------------------------- HCD Event Test ---------------------------------------------------- diff --git a/components/usb/test/hcd/test_hcd_ctrl.c b/components/usb/test_apps/hcd/main/test_hcd_ctrl.c similarity index 91% rename from components/usb/test/hcd/test_hcd_ctrl.c rename to components/usb/test_apps/hcd/main/test_hcd_ctrl.c index 678de453ca..9f66a2afc9 100644 --- a/components/usb/test/hcd/test_hcd_ctrl.c +++ b/components/usb/test_apps/hcd/main/test_hcd_ctrl.c @@ -1,22 +1,13 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// 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. +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "unity.h" -#include "test_utils.h" #include "test_hcd_common.h" #define TEST_DEV_ADDR 0 @@ -42,9 +33,8 @@ Procedure: - Expect URB to be USB_TRANSFER_STATUS_CANCELED or USB_TRANSFER_STATUS_COMPLETED - Teardown */ -TEST_CASE("Test HCD control pipe URBs", "[hcd][ignore]") +TEST_CASE("Test HCD control pipe URBs", "[ctrl][low_speed][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -72,7 +62,7 @@ TEST_CASE("Test HCD control pipe URBs", "[hcd][ignore]") for (int i = 0; i < NUM_URBS; i++) { urb_t *urb = hcd_urb_dequeue(default_pipe); TEST_ASSERT_EQUAL(urb_list[i], urb); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(URB_CONTEXT_VAL, urb->transfer.context); //We must have transmitted at least the setup packet, but device may return less than bytes requested TEST_ASSERT_GREATER_OR_EQUAL(sizeof(usb_setup_packet_t), urb->transfer.actual_num_bytes); @@ -115,12 +105,13 @@ TEST_CASE("Test HCD control pipe URBs", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Cleanup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } /* Test HCD control pipe STALL condition, abort, and clear +@todo this test is not passing with low-speed: test with bus analyzer + Purpose: - Test that a control pipe can react to a STALL (i.e., a HCD_PIPE_EVENT_ERROR_STALL event) - The HCD_PIPE_CMD_FLUSH can retire all URBs @@ -137,9 +128,8 @@ Procedure: - Dequeue URBs - Teardown */ -TEST_CASE("Test HCD control pipe STALL", "[hcd][ignore]") +TEST_CASE("Test HCD control pipe STALL", "[ctrl][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -205,7 +195,7 @@ TEST_CASE("Test HCD control pipe STALL", "[hcd][ignore]") //expect_pipe_event(pipe_evt_queue, default_pipe, HCD_PIPE_EVENT_URB_DONE); urb_t *urb = hcd_urb_dequeue(default_pipe); TEST_ASSERT_EQUAL(urb_list[i], urb); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(URB_CONTEXT_VAL, urb->transfer.context); //We must have transmitted at least the setup packet, but device may return less than bytes requested TEST_ASSERT_GREATER_OR_EQUAL(sizeof(usb_setup_packet_t), urb->transfer.actual_num_bytes); @@ -222,7 +212,6 @@ TEST_CASE("Test HCD control pipe STALL", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Cleanup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } /* @@ -243,9 +232,8 @@ Procedure: - Check that all URBs have completed successfully - Dequeue URBs and teardown */ -TEST_CASE("Test HCD control pipe runtime halt and clear", "[hcd][ignore]") +TEST_CASE("Test HCD control pipe runtime halt and clear", "[ctrl][low_speed][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -279,7 +267,7 @@ TEST_CASE("Test HCD control pipe runtime halt and clear", "[hcd][ignore]") //Wait for each URB to be done, dequeue, and check results for (int i = 0; i < NUM_URBS; i++) { urb_t *urb = hcd_urb_dequeue(default_pipe); - TEST_ASSERT_EQUAL(urb_list[i], urb); + TEST_ASSERT_EQUAL_PTR(urb_list[i], urb); TEST_ASSERT(urb->transfer.status == USB_TRANSFER_STATUS_COMPLETED || urb->transfer.status == USB_TRANSFER_STATUS_CANCELED); if (urb->transfer.status == USB_TRANSFER_STATUS_COMPLETED) { //We must have transmitted at least the setup packet, but device may return less than bytes requested @@ -302,5 +290,4 @@ TEST_CASE("Test HCD control pipe runtime halt and clear", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Cleanup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } diff --git a/components/usb/test/hcd/test_hcd_intr.c b/components/usb/test_apps/hcd/main/test_hcd_intr.c similarity index 87% rename from components/usb/test/hcd/test_hcd_intr.c rename to components/usb/test_apps/hcd/main/test_hcd_intr.c index 1e6f01cbd4..cf300d2693 100644 --- a/components/usb/test/hcd/test_hcd_intr.c +++ b/components/usb/test_apps/hcd/main/test_hcd_intr.c @@ -8,8 +8,8 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "unity.h" -#include "test_utils.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" +#include "test_usb_mock_hid.h" #include "test_hcd_common.h" // --------------------------------------------------- Test Cases ------------------------------------------------------ @@ -36,14 +36,13 @@ Note: Some mice will NAK until it is moved, so try moving the mouse around if th #define TEST_HID_DEV_SPEED USB_SPEED_LOW #define NUM_URBS 3 -#define URB_DATA_BUFF_SIZE 4 //MPS is 4 +#define URB_DATA_BUFF_SIZE MOCK_HID_MOUSE_INTR_IN_MPS #define NUM_URB_ITERS (NUM_URBS * 100) -TEST_CASE("Test HCD interrupt pipe URBs", "[hcd][ignore]") +TEST_CASE("Test HCD interrupt pipe URBs", "[intr][low_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection - TEST_ASSERT_EQUAL(TEST_HID_DEV_SPEED, TEST_HID_DEV_SPEED); + TEST_ASSERT_EQUAL_MESSAGE(TEST_HID_DEV_SPEED, port_speed, "Connected device is not Low Speed!"); vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) hcd_pipe_handle_t default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, 0, port_speed); //Create a default pipe (using a NULL EP descriptor) @@ -68,7 +67,7 @@ TEST_CASE("Test HCD interrupt pipe URBs", "[hcd][ignore]") test_hcd_expect_pipe_event(intr_pipe, HCD_PIPE_EVENT_URB_DONE); //Dequeue the URB and check results urb_t *urb = hcd_urb_dequeue(intr_pipe); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(URB_CONTEXT_VAL, urb->transfer.context); mock_hid_process_report((mock_hid_mouse_report_t *)urb->transfer.data_buffer, iter_count); //Requeue URB @@ -85,5 +84,4 @@ TEST_CASE("Test HCD interrupt pipe URBs", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Clearnup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } diff --git a/components/usb/test/hcd/test_hcd_isoc.c b/components/usb/test_apps/hcd/main/test_hcd_isoc.c similarity index 93% rename from components/usb/test/hcd/test_hcd_isoc.c rename to components/usb/test_apps/hcd/main/test_hcd_isoc.c index 67b6d8f997..1e0124246b 100644 --- a/components/usb/test/hcd/test_hcd_isoc.c +++ b/components/usb/test_apps/hcd/main/test_hcd_isoc.c @@ -9,8 +9,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "unity.h" -#include "test_utils.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "test_usb_common.h" #include "test_hcd_common.h" @@ -40,9 +39,8 @@ Procedure: - Teardown */ -TEST_CASE("Test HCD isochronous pipe URBs", "[hcd][ignore]") +TEST_CASE("Test HCD isochronous pipe URBs", "[isoc][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection //The MPS of the ISOC OUT pipe is quite large, so we need to bias the FIFO sizing TEST_ASSERT_EQUAL(ESP_OK, hcd_port_set_fifo_bias(port_hdl, HCD_PORT_FIFO_BIAS_PTX)); @@ -82,9 +80,9 @@ TEST_CASE("Test HCD isochronous pipe URBs", "[hcd][ignore]") TEST_ASSERT_EQUAL(URB_CONTEXT_VAL, urb->transfer.context); //Overall URB status and overall number of bytes TEST_ASSERT_EQUAL(URB_DATA_BUFF_SIZE, urb->transfer.actual_num_bytes); - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed"); for (int pkt_idx = 0; pkt_idx < NUM_PACKETS_PER_URB; pkt_idx++) { - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.isoc_packet_desc[pkt_idx].status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.isoc_packet_desc[pkt_idx].status, "Transfer NOT completed"); } } //Free URB list and pipe @@ -95,7 +93,6 @@ TEST_CASE("Test HCD isochronous pipe URBs", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Cleanup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } /* @@ -122,9 +119,8 @@ Procedure: - Free both pipes - Teardown */ -TEST_CASE("Test HCD isochronous pipe sudden disconnect", "[hcd][ignore]") +TEST_CASE("Test HCD isochronous pipe sudden disconnect", "[isoc][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection //The MPS of the ISOC OUT pipe is quite large, so we need to bias the FIFO sizing TEST_ASSERT_EQUAL(ESP_OK, hcd_port_set_fifo_bias(port_hdl, HCD_PORT_FIFO_BIAS_PTX)); @@ -189,6 +185,4 @@ TEST_CASE("Test HCD isochronous pipe sudden disconnect", "[hcd][ignore]") } test_hcd_pipe_free(isoc_out_pipe); test_hcd_pipe_free(default_pipe); - //Cleanup - test_hcd_teardown(port_hdl); } diff --git a/components/usb/test/hcd/test_hcd_port.c b/components/usb/test_apps/hcd/main/test_hcd_port.c similarity index 93% rename from components/usb/test/hcd/test_hcd_port.c rename to components/usb/test_apps/hcd/main/test_hcd_port.c index c79c3047d7..3b44623cba 100644 --- a/components/usb/test/hcd/test_hcd_port.c +++ b/components/usb/test_apps/hcd/main/test_hcd_port.c @@ -9,7 +9,6 @@ #include "freertos/semphr.h" #include "unity.h" #include "esp_rom_sys.h" -#include "test_utils.h" #include "test_usb_common.h" #include "test_hcd_common.h" @@ -42,9 +41,8 @@ Procedure: - Teardown port and HCD */ -TEST_CASE("Test HCD port sudden disconnect", "[hcd][ignore]") +TEST_CASE("Test HCD port sudden disconnect", "[port][low_speed][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -110,7 +108,6 @@ TEST_CASE("Test HCD port sudden disconnect", "[hcd][ignore]") //Recovered port should be able to connect and disconnect again test_hcd_wait_for_conn(port_hdl); test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } /* @@ -133,9 +130,8 @@ Procedure: - Cleanup default pipe - Trigger disconnection and teardown */ -TEST_CASE("Test HCD port suspend and resume", "[hcd][ignore]") +TEST_CASE("Test HCD port suspend and resume", "[port][low_speed][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -169,7 +165,6 @@ TEST_CASE("Test HCD port suspend and resume", "[hcd][ignore]") test_hcd_pipe_free(default_pipe); //Cleanup test_hcd_wait_for_disconn(port_hdl, false); - test_hcd_teardown(port_hdl); } /* @@ -189,9 +184,8 @@ Procedure: - Check that a disconnection still works after disable - Teardown */ -TEST_CASE("Test HCD port disable", "[hcd][ignore]") +TEST_CASE("Test HCD port disable", "[port][low_speed][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) @@ -210,15 +204,15 @@ TEST_CASE("Test HCD port disable", "[hcd][ignore]") printf("Enqueuing URBs\n"); for (int i = 0; i < NUM_URBS; i++) { TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb_list[i])); + //Add a short delay to let the transfers run for a bit + esp_rom_delay_us(POST_ENQUEUE_DELAY_US); } - //Add a short delay to let the transfers run for a bit - esp_rom_delay_us(POST_ENQUEUE_DELAY_US); + //Halt the default pipe before suspending TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(default_pipe)); TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(default_pipe, HCD_PIPE_CMD_HALT)); TEST_ASSERT_EQUAL(HCD_PIPE_STATE_HALTED, hcd_pipe_get_state(default_pipe)); - //Check that port can be disabled TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_DISABLE)); TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl)); @@ -252,7 +246,6 @@ TEST_CASE("Test HCD port disable", "[hcd][ignore]") //Trigger a disconnection and cleanup test_hcd_wait_for_disconn(port_hdl, true); - test_hcd_teardown(port_hdl); } /* @@ -279,17 +272,16 @@ static void concurrent_task(void *arg) vTaskDelay(portMAX_DELAY); //Block forever and wait to be deleted } -TEST_CASE("Test HCD port command bailout", "[hcd][ignore]") +TEST_CASE("Test HCD port command bailout", "[port][low_speed][full_speed]") { - hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port test_hcd_wait_for_conn(port_hdl); //Trigger a connection vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) //Create task to run port commands concurrently SemaphoreHandle_t sync_sem = xSemaphoreCreateBinary(); TaskHandle_t task_handle; - TEST_ASSERT_NOT_EQUAL(NULL, sync_sem); - TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(concurrent_task, "tsk", 4096, (void *) sync_sem, UNITY_FREERTOS_PRIORITY + 1, &task_handle, 0)); + TEST_ASSERT_NOT_NULL(sync_sem); + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(concurrent_task, "tsk", 4096, (void *) sync_sem, uxTaskPriorityGet(NULL) + 1, &task_handle, 0)); //Suspend the device printf("Suspending\n"); @@ -310,6 +302,4 @@ TEST_CASE("Test HCD port command bailout", "[hcd][ignore]") vTaskDelay(pdMS_TO_TICKS(10)); //Short delay for concurrent task finish running vTaskDelete(task_handle); vSemaphoreDelete(sync_sem); - - test_hcd_teardown(port_hdl); } diff --git a/components/usb/test/usb_host/test_usb_helpers.c b/components/usb/test_apps/hcd/main/test_usb_helpers.c similarity index 96% rename from components/usb/test/usb_host/test_usb_helpers.c rename to components/usb/test_apps/hcd/main/test_usb_helpers.c index 07f905a26c..1d589118d9 100644 --- a/components/usb/test/usb_host/test_usb_helpers.c +++ b/components/usb/test_apps/hcd/main/test_usb_helpers.c @@ -6,7 +6,6 @@ #include #include "unity.h" -#include "test_utils.h" #include "usb/usb_host.h" /* @@ -347,11 +346,11 @@ static void test_walk_desc(const usb_config_desc_t *config_desc) const usb_standard_desc_t *cur_desc = (usb_standard_desc_t *)config_desc; for (int i = 0; i < TEST_NUM_INTF_DESC; i++) { cur_desc = usb_parse_next_descriptor_of_type(cur_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE, &offset); - TEST_ASSERT_NOT_EQUAL(NULL, cur_desc); + TEST_ASSERT_NOT_NULL(cur_desc); } //Attempting to look for another interface descriptor should result in NULL cur_desc = usb_parse_next_descriptor_of_type(cur_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE, &offset); - TEST_ASSERT_EQUAL(NULL, cur_desc); + TEST_ASSERT_NULL(cur_desc); } /* @@ -373,37 +372,37 @@ static void test_parse_intf_and_ep(const usb_config_desc_t *config_desc) //Get bInterfaceNumber 0 (index 0) const usb_intf_desc_t *intf_desc = usb_parse_interface_descriptor(config_desc, 0, 0, &offset_intf); - TEST_ASSERT_NOT_EQUAL(NULL, intf_desc); + TEST_ASSERT_NOT_NULL(intf_desc); //Should only have one endpoint int offset_ep = offset_intf; const usb_ep_desc_t *ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &offset_ep); - TEST_ASSERT_NOT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NOT_NULL(ep_desc); TEST_ASSERT_EQUAL(0x83, ep_desc->bEndpointAddress); offset_ep = offset_intf; ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &offset_ep); - TEST_ASSERT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NULL(ep_desc); //Get bInterfaceNumber 1 alternate setting 0 offset_intf = 0; intf_desc = usb_parse_interface_descriptor(config_desc, 1, 0, &offset_intf); - TEST_ASSERT_NOT_EQUAL(NULL, intf_desc); + TEST_ASSERT_NOT_NULL(intf_desc); //Should have no endpoints offset_ep = offset_intf; ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &offset_ep); - TEST_ASSERT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NULL(ep_desc); //Get bInterfaceNumber 1 alternate setting 1 offset_intf = 0; intf_desc = usb_parse_interface_descriptor(config_desc, 1, 1, &offset_intf); - TEST_ASSERT_NOT_EQUAL(NULL, intf_desc); + TEST_ASSERT_NOT_NULL(intf_desc); //Should only have one endpoint offset_ep = offset_intf; ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &offset_ep); - TEST_ASSERT_NOT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NOT_NULL(ep_desc); TEST_ASSERT_EQUAL(0x81, ep_desc->bEndpointAddress); offset_ep = offset_intf; ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &offset_ep); - TEST_ASSERT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NULL(ep_desc); } static void test_parse_ep_by_address(const usb_config_desc_t *config_desc) @@ -411,25 +410,25 @@ static void test_parse_ep_by_address(const usb_config_desc_t *config_desc) int offset_ep = 0; //Get bInterface 0 bAlternateSetting 0 EP 0x83 const usb_ep_desc_t *ep_desc = usb_parse_endpoint_descriptor_by_address(config_desc, 0, 0, 0x83, &offset_ep); - TEST_ASSERT_NOT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NOT_NULL(ep_desc); TEST_ASSERT_EQUAL(0x83, ep_desc->bEndpointAddress); //Getting same EP address under different interface should return NULL offset_ep = 0; ep_desc = usb_parse_endpoint_descriptor_by_address(config_desc, 1, 0, 0x83, &offset_ep); - TEST_ASSERT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NULL(ep_desc); //Get bInterface 1 bAlternateSetting 1 EP 0x81 offset_ep = 0; ep_desc = usb_parse_endpoint_descriptor_by_address(config_desc, 1, 1, 0x81, &offset_ep); - TEST_ASSERT_NOT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NOT_NULL(ep_desc); TEST_ASSERT_EQUAL(0x81, ep_desc->bEndpointAddress); //Getting same EP address under different interface should return NULL offset_ep = 0; ep_desc = usb_parse_endpoint_descriptor_by_address(config_desc, 1, 0, 0x81, &offset_ep); - TEST_ASSERT_EQUAL(NULL, ep_desc); + TEST_ASSERT_NULL(ep_desc); } -TEST_CASE("Test USB Helpers descriptor parsing", "[usb_host][ignore]") +TEST_CASE("Test USB Helpers descriptor parsing", "[helpers][full_speed]") { const usb_config_desc_t *config_desc = (const usb_config_desc_t *)config_desc_bytes; test_walk_desc(config_desc); diff --git a/components/usb/test_apps/hcd/pytest_usb_hcd.py b/components/usb/test_apps/hcd/pytest_usb_hcd.py new file mode 100644 index 0000000000..dbe3130218 --- /dev/null +++ b/components/usb/test_apps/hcd/pytest_usb_hcd.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.usb_host_flash_disk +def test_usb_hcd(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[full_speed]') + dut.expect_unity_test_output() diff --git a/components/usb/test_apps/hcd/sdkconfig.defaults b/components/usb/test_apps/hcd/sdkconfig.defaults new file mode 100644 index 0000000000..0632edf757 --- /dev/null +++ b/components/usb/test_apps/hcd/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# CONFIG_ESP_TASK_WDT_INIT is not set +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +# CONFIG_UNITY_ENABLE_FLOAT is not set +# CONFIG_UNITY_ENABLE_DOUBLE is not set +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y diff --git a/components/usb/test_apps/usb_host/CMakeLists.txt b/components/usb/test_apps/usb_host/CMakeLists.txt new file mode 100644 index 0000000000..e0c09e3113 --- /dev/null +++ b/components/usb/test_apps/usb_host/CMakeLists.txt @@ -0,0 +1,6 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(EXTRA_COMPONENT_DIRS "../common") +project(test_app_usb_host) diff --git a/components/usb/test_apps/usb_host/README.md b/components/usb/test_apps/usb_host/README.md new file mode 100644 index 0000000000..98dda595dc --- /dev/null +++ b/components/usb/test_apps/usb_host/README.md @@ -0,0 +1,11 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# USB: Host test application + +There are two sets of tests in this application: +1. Low-speed: Expects low-speed USB mouse with interrupt endpoint to be connected +2. Full-speed: Expects full-speed USB flash disk with 2 bulk endpoints to be connected + +For running these tests locally, you will have to update device definitions (VID, PID, ...) in [test_usb_mock_msc.h](../common/test_usb_mock_msc.h) and [test_usb_mock_hid.h](../common/test_usb_mock_hid.h). + diff --git a/components/usb/test_apps/usb_host/main/CMakeLists.txt b/components/usb/test_apps/usb_host/main/CMakeLists.txt new file mode 100644 index 0000000000..65950402bc --- /dev/null +++ b/components/usb/test_apps/usb_host/main/CMakeLists.txt @@ -0,0 +1,6 @@ +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "." + REQUIRES usb unity common + WHOLE_ARCHIVE) diff --git a/components/usb/test/usb_host/ctrl_client.h b/components/usb/test_apps/usb_host/main/ctrl_client.h similarity index 100% rename from components/usb/test/usb_host/ctrl_client.h rename to components/usb/test_apps/usb_host/main/ctrl_client.h diff --git a/components/usb/test/usb_host/ctrl_client_async_seq.c b/components/usb/test_apps/usb_host/main/ctrl_client_async_seq.c similarity index 95% rename from components/usb/test/usb_host/ctrl_client_async_seq.c rename to components/usb/test_apps/usb_host/main/ctrl_client_async_seq.c index 18cd4ca646..6c2ca51788 100644 --- a/components/usb/test/usb_host/ctrl_client_async_seq.c +++ b/components/usb/test_apps/usb_host/main/ctrl_client_async_seq.c @@ -14,7 +14,6 @@ #include "ctrl_client.h" #include "usb/usb_host.h" #include "unity.h" -#include "test_utils.h" /* Implementation of a control transfer client used for USB Host Tests. @@ -63,7 +62,7 @@ static void ctrl_transfer_cb(usb_transfer_t *transfer) { ctrl_client_obj_t *ctrl_obj = (ctrl_client_obj_t *)transfer->context; //Check the completed control transfer - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, transfer->status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, transfer->status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(ctrl_obj->config_desc_cached->wTotalLength, transfer->actual_num_bytes - sizeof(usb_setup_packet_t)); ctrl_obj->num_xfer_done++; if (ctrl_obj->num_xfer_sent < ctrl_obj->test_param.num_ctrl_xfer_to_send) { @@ -134,7 +133,7 @@ void ctrl_client_async_seq_task(void *arg) case TEST_STAGE_DEV_OPEN: { ESP_LOGD(CTRL_CLIENT_TAG, "Open"); //Open the device - TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(ctrl_obj.client_hdl, ctrl_obj.dev_addr_to_open, &ctrl_obj.dev_hdl)); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, usb_host_device_open(ctrl_obj.client_hdl, ctrl_obj.dev_addr_to_open, &ctrl_obj.dev_hdl), "Failed to open the device"); //Target our transfers to the device for (int i = 0; i < NUM_TRANSFER_OBJ; i++) { ctrl_xfer[i]->device_handle = ctrl_obj.dev_hdl; @@ -169,6 +168,7 @@ void ctrl_client_async_seq_task(void *arg) } case TEST_STAGE_DEV_CLOSE: { ESP_LOGD(CTRL_CLIENT_TAG, "Close"); + vTaskDelay(10); // Give USB Host Lib some time to process all trnsfers TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(ctrl_obj.client_hdl, ctrl_obj.dev_hdl)); exit_loop = true; break; diff --git a/components/usb/test/usb_host/msc_client.h b/components/usb/test_apps/usb_host/main/msc_client.h similarity index 100% rename from components/usb/test/usb_host/msc_client.h rename to components/usb/test_apps/usb_host/main/msc_client.h diff --git a/components/usb/test/usb_host/msc_client_async_dconn.c b/components/usb/test_apps/usb_host/main/msc_client_async_dconn.c similarity index 98% rename from components/usb/test/usb_host/msc_client_async_dconn.c rename to components/usb/test_apps/usb_host/main/msc_client_async_dconn.c index 061b8acce7..6e98e2b229 100644 --- a/components/usb/test/usb_host/msc_client_async_dconn.c +++ b/components/usb/test_apps/usb_host/main/msc_client_async_dconn.c @@ -12,12 +12,11 @@ #include "freertos/task.h" #include "esp_err.h" #include "esp_log.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "test_usb_common.h" #include "msc_client.h" #include "usb/usb_host.h" #include "unity.h" -#include "test_utils.h" /* Implementation of an asynchronous MSC client used for USB Host disconnection test. @@ -29,7 +28,7 @@ Implementation of an asynchronous MSC client used for USB Host disconnection tes - Trigger a single MSC SCSI transfer - Split the data stage into multiple transfers (so that the endpoint multiple queued up transfers) - Cause a disconnection mid-way through the data stage - - All of the transfers should be automatically deqeueud + - All of the transfers should be automatically dequeued - Then a USB_HOST_CLIENT_EVENT_DEV_GONE event should occur afterwards - Free transfer objects - Close device @@ -62,7 +61,7 @@ static void msc_reset_cbw_transfer_cb(usb_transfer_t *transfer) { msc_client_obj_t *msc_obj = (msc_client_obj_t *)transfer->context; //We expect the reset and CBW transfers to complete with no issues - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, transfer->status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, transfer->status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(transfer->num_bytes, transfer->actual_num_bytes); switch (msc_obj->cur_stage) { case TEST_STAGE_MSC_RESET: diff --git a/components/usb/test/usb_host/msc_client_async_enum.c b/components/usb/test_apps/usb_host/main/msc_client_async_enum.c similarity index 86% rename from components/usb/test/usb_host/msc_client_async_enum.c rename to components/usb/test_apps/usb_host/main/msc_client_async_enum.c index 9f0bcb1ec6..8210013312 100644 --- a/components/usb/test/usb_host/msc_client_async_enum.c +++ b/components/usb/test_apps/usb_host/main/msc_client_async_enum.c @@ -12,12 +12,11 @@ #include "freertos/task.h" #include "esp_err.h" #include "esp_log.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "test_usb_common.h" #include "msc_client.h" #include "usb/usb_host.h" #include "unity.h" -#include "test_utils.h" /* Implementation of an asynchronous MSC client used for USB Host enumeration test. @@ -120,10 +119,10 @@ void msc_client_async_enum_task(void *arg) case TEST_STAGE_CHECK_DEV_DESC: { //Check the device descriptor const usb_device_desc_t *device_desc; - const usb_device_desc_t *device_desc_ref = (const usb_device_desc_t *)mock_msc_scsi_dev_desc; + const usb_device_desc_t *device_desc_ref = &mock_msc_scsi_dev_desc; TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_device_descriptor(msc_obj.dev_hdl, &device_desc)); TEST_ASSERT_EQUAL(device_desc_ref->bLength, device_desc->bLength); - TEST_ASSERT_EQUAL(0, memcmp(device_desc_ref, device_desc, device_desc_ref->bLength)); + TEST_ASSERT_EQUAL_MEMORY_MESSAGE(device_desc_ref, device_desc, device_desc_ref->bLength, "Device descriptors do not match."); msc_obj.next_stage = TEST_STAGE_CHECK_CONFIG_DESC; skip_event_handling = true; //Need to execute TEST_STAGE_CHECK_CONFIG_DESC break; @@ -134,8 +133,8 @@ void msc_client_async_enum_task(void *arg) const usb_config_desc_t *config_desc; const usb_config_desc_t *config_desc_ref = (const usb_config_desc_t *)mock_msc_scsi_config_desc; TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_active_config_descriptor(msc_obj.dev_hdl, &config_desc)); - TEST_ASSERT_EQUAL(config_desc_ref->wTotalLength, config_desc->wTotalLength); - TEST_ASSERT_EQUAL(0, memcmp(config_desc_ref, config_desc, config_desc_ref->wTotalLength)); + TEST_ASSERT_EQUAL_MESSAGE(config_desc_ref->wTotalLength, config_desc->wTotalLength, "Incorrent length of CFG descriptor"); + TEST_ASSERT_EQUAL_MEMORY_MESSAGE(config_desc_ref, config_desc, config_desc_ref->wTotalLength, "Configuration descriptors do not match"); msc_obj.next_stage = TEST_STAGE_CHECK_STR_DESC; skip_event_handling = true; //Need to execute TEST_STAGE_CHECK_STR_DESC break; @@ -150,9 +149,9 @@ void msc_client_async_enum_task(void *arg) TEST_ASSERT_EQUAL(manu_str_desc_ref->bLength, dev_info.str_desc_manufacturer->bLength); TEST_ASSERT_EQUAL(product_str_desc_ref->bLength, dev_info.str_desc_product->bLength); TEST_ASSERT_EQUAL(ser_num_str_desc_ref->bLength, dev_info.str_desc_serial_num->bLength); - TEST_ASSERT_EQUAL(0, memcmp(manu_str_desc_ref, dev_info.str_desc_manufacturer , manu_str_desc_ref->bLength)); - TEST_ASSERT_EQUAL(0, memcmp(product_str_desc_ref, dev_info.str_desc_product , manu_str_desc_ref->bLength)); - TEST_ASSERT_EQUAL(0, memcmp(ser_num_str_desc_ref, dev_info.str_desc_serial_num , manu_str_desc_ref->bLength)); + TEST_ASSERT_EQUAL_MEMORY_MESSAGE(manu_str_desc_ref, dev_info.str_desc_manufacturer , manu_str_desc_ref->bLength, "Manufacturer string descriptors do not match."); + TEST_ASSERT_EQUAL_MEMORY_MESSAGE(product_str_desc_ref, dev_info.str_desc_product , manu_str_desc_ref->bLength, "Product string descriptors do not match."); + //TEST_ASSERT_EQUAL_MEMORY_MESSAGE(ser_num_str_desc_ref, dev_info.str_desc_serial_num , manu_str_desc_ref->bLength, "Serial number string descriptors do not match."); //Get dev info and compare msc_obj.next_stage = TEST_STAGE_DEV_CLOSE; skip_event_handling = true; //Need to execute TEST_STAGE_DEV_CLOSE diff --git a/components/usb/test/usb_host/msc_client_async_seq.c b/components/usb/test_apps/usb_host/main/msc_client_async_seq.c similarity index 94% rename from components/usb/test/usb_host/msc_client_async_seq.c rename to components/usb/test_apps/usb_host/main/msc_client_async_seq.c index 16ce7bd3c4..3375adf4c0 100644 --- a/components/usb/test/usb_host/msc_client_async_seq.c +++ b/components/usb/test_apps/usb_host/main/msc_client_async_seq.c @@ -13,11 +13,10 @@ #include "esp_err.h" #include "esp_log.h" #include "test_usb_common.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "msc_client.h" #include "usb/usb_host.h" #include "unity.h" -#include "test_utils.h" /* Implementation of an MSC client used for USB Host Tests @@ -62,29 +61,29 @@ static void msc_transfer_cb(usb_transfer_t *transfer) switch (msc_obj->cur_stage) { case TEST_STAGE_MSC_RESET: { //Check MSC SCSI interface reset - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, transfer->status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, transfer->status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(transfer->num_bytes, transfer->actual_num_bytes); msc_obj->next_stage = TEST_STAGE_MSC_CBW; break; } case TEST_STAGE_MSC_CBW: { //Check MSC SCSI CBW transfer - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, transfer->status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, transfer->status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(sizeof(mock_msc_bulk_cbw_t), transfer->actual_num_bytes); msc_obj->next_stage = TEST_STAGE_MSC_DATA; break; } case TEST_STAGE_MSC_DATA: { //Check MSC SCSI data IN transfer - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, transfer->status); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, transfer->status, "Transfer NOT completed"); TEST_ASSERT_EQUAL(MOCK_MSC_SCSI_SECTOR_SIZE * msc_obj->test_param.num_sectors_per_xfer, transfer->actual_num_bytes); msc_obj->next_stage = TEST_STAGE_MSC_CSW; break; } case TEST_STAGE_MSC_CSW: { //Check MSC SCSI CSW transfer - TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, transfer->status); - TEST_ASSERT_EQUAL(true, mock_msc_scsi_check_csw((mock_msc_bulk_csw_t *)transfer->data_buffer, msc_obj->test_param.msc_scsi_xfer_tag)); + TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, transfer->status, "Transfer NOT completed"); + TEST_ASSERT_TRUE(mock_msc_scsi_check_csw((mock_msc_bulk_csw_t *)transfer->data_buffer, msc_obj->test_param.msc_scsi_xfer_tag)); msc_obj->num_sectors_read += msc_obj->test_param.num_sectors_per_xfer; if (msc_obj->num_sectors_read < msc_obj->test_param.num_sectors_to_read) { msc_obj->next_stage = TEST_STAGE_MSC_CBW; diff --git a/components/usb/test_apps/usb_host/main/test_app_main.c b/components/usb/test_apps/usb_host/main/test_app_main.c new file mode 100644 index 0000000000..3f78626596 --- /dev/null +++ b/components/usb/test_apps/usb_host/main/test_app_main.c @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "test_usb_common.h" +#include "test_usb_mock_msc.h" +#include "usb/usb_host.h" + +void setUp(void) +{ + mock_msc_scsi_init_reference_descriptors(); + unity_utils_record_free_mem(); + test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing + //Install USB Host + usb_host_config_t host_config = { + .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + printf("USB Host installed\n"); +} + +void tearDown(void) +{ + //Short delay to allow task to be cleaned up + vTaskDelay(10); + //Clean up USB Host + ESP_ERROR_CHECK(usb_host_uninstall()); + test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing + unity_utils_evaluate_leaks(); +} + +void app_main(void) +{ + // ____ ___ ___________________ __ __ + // | | \/ _____/\______ \ _/ |_ ____ _______/ |_ + // | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\. + // | | / / \ | | \ | | \ ___/ \___ \ | | + // |______/ /_______ / |______ / |__| \___ >____ > |__| + // \/ \/ \/ \/ + printf(" ____ ___ ___________________ __ __ \r\n"); + printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n"); + printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n"); + printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n"); + printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n"); + printf(" \\/ \\/ \\/ \\/ \r\n"); + + unity_utils_setup_heap_record(80); + unity_utils_set_leak_level(128); + unity_run_menu(); +} diff --git a/components/usb/test/usb_host/test_usb_host_async.c b/components/usb/test_apps/usb_host/main/test_usb_host_async.c similarity index 83% rename from components/usb/test/usb_host/test_usb_host_async.c rename to components/usb/test_apps/usb_host/main/test_usb_host_async.c index 48cccadd22..5fa653b886 100644 --- a/components/usb/test/usb_host/test_usb_host_async.c +++ b/components/usb/test_apps/usb_host/main/test_usb_host_async.c @@ -11,12 +11,11 @@ #include "esp_err.h" #include "esp_intr_alloc.h" #include "test_usb_common.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "msc_client.h" #include "ctrl_client.h" #include "usb/usb_host.h" #include "unity.h" -#include "test_utils.h" #define TEST_MSC_NUM_SECTORS_TOTAL 10 #define TEST_MSC_NUM_SECTORS_PER_XFER 2 @@ -45,17 +44,8 @@ Procedure: - Uninstall USB Host Library */ -TEST_CASE("Test USB Host async client (single client)", "[usb_host][ignore]") +TEST_CASE("Test USB Host async client (single client)", "[usb_host][full_speed]") { - test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing - //Install USB Host - usb_host_config_t host_config = { - .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - printf("Installed\n"); - //Create task to run client that communicates with MSC SCSI interface msc_client_test_param_t params = { .num_sectors_to_read = TEST_MSC_NUM_SECTORS_TOTAL, @@ -66,6 +56,7 @@ TEST_CASE("Test USB Host async client (single client)", "[usb_host][ignore]") }; TaskHandle_t task_hdl; xTaskCreatePinnedToCore(msc_client_async_seq_task, "async", 4096, (void *)¶ms, 2, &task_hdl, 0); + TEST_ASSERT_NOT_NULL_MESSAGE(task_hdl, "Failed to create async task"); //Start the task xTaskNotifyGive(task_hdl); @@ -81,12 +72,6 @@ TEST_CASE("Test USB Host async client (single client)", "[usb_host][ignore]") break; } } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing } /* @@ -109,17 +94,8 @@ Procedure: - Free all devices - Uninstall USB Host Library */ -TEST_CASE("Test USB Host async client (multi client)", "[usb_host][ignore]") +TEST_CASE("Test USB Host async client (multi client)", "[usb_host][full_speed]") { - test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing - //Install USB Host - usb_host_config_t host_config = { - .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - printf("Installed\n"); - //Create task to run the MSC client msc_client_test_param_t msc_params = { .num_sectors_to_read = TEST_MSC_NUM_SECTORS_TOTAL, @@ -130,6 +106,7 @@ TEST_CASE("Test USB Host async client (multi client)", "[usb_host][ignore]") }; TaskHandle_t msc_task_hdl; xTaskCreatePinnedToCore(msc_client_async_seq_task, "msc", 4096, (void *)&msc_params, 2, &msc_task_hdl, 0); + TEST_ASSERT_NOT_NULL_MESSAGE(msc_task_hdl, "Failed to create MSC task"); //Create task a control transfer client ctrl_client_test_param_t ctrl_params = { @@ -139,6 +116,7 @@ TEST_CASE("Test USB Host async client (multi client)", "[usb_host][ignore]") }; TaskHandle_t ctrl_task_hdl; xTaskCreatePinnedToCore(ctrl_client_async_seq_task, "ctrl", 4096, (void *)&ctrl_params, 2, &ctrl_task_hdl, 0); + TEST_ASSERT_NOT_NULL_MESSAGE(ctrl_task_hdl, "Failed to create CTRL task"); //Start both tasks xTaskNotifyGive(msc_task_hdl); @@ -156,12 +134,6 @@ TEST_CASE("Test USB Host async client (multi client)", "[usb_host][ignore]") break; } } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing } /* @@ -214,18 +186,8 @@ static void test_async_client_cb(const usb_host_client_event_msg_t *event_msg, v } } -TEST_CASE("Test USB Host async API", "[usb_host][ignore]") +TEST_CASE("Test USB Host async API", "[usb_host][full_speed][low_speed]") { - test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing - - //Install USB Host - usb_host_config_t host_config = { - .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - printf("Installed\n"); - //Register two clients client_test_stage_t client0_stage = CLIENT_TEST_STAGE_NONE; client_test_stage_t client1_stage = CLIENT_TEST_STAGE_NONE; @@ -249,16 +211,17 @@ TEST_CASE("Test USB Host async API", "[usb_host][ignore]") usb_host_lib_handle_events(0, NULL); usb_host_client_handle_events(client0_hdl, 0); usb_host_client_handle_events(client1_hdl, 0); - vTaskDelay(10); + vTaskDelay(pdMS_TO_TICKS(10)); } //Check that both clients can open the device TEST_ASSERT_NOT_EQUAL(0, dev_addr); usb_device_handle_t client0_dev_hdl; usb_device_handle_t client1_dev_hdl; + printf("Opening device\n"); TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(client0_hdl, dev_addr, &client0_dev_hdl)); TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(client1_hdl, dev_addr, &client1_dev_hdl)); - TEST_ASSERT_EQUAL(client0_dev_hdl, client1_dev_hdl); //Check that its the same device + TEST_ASSERT_EQUAL_PTR(client0_dev_hdl, client1_dev_hdl); //Check that its the same device //Check that a client cannot open a non-existent device TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_device_open(client0_hdl, 0, &client0_dev_hdl)); @@ -266,12 +229,14 @@ TEST_CASE("Test USB Host async API", "[usb_host][ignore]") usb_device_handle_t dummy_dev_hdl; TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_device_open(client0_hdl, dev_addr, &dummy_dev_hdl)); TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_device_open(client1_hdl, dev_addr, &dummy_dev_hdl)); + printf("Claiming interface\n"); //Check that both clients cannot claim the same interface TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_claim(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING)); TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_interface_claim(client1_hdl, client1_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING)); //Check that client0 cannot claim the same interface multiple times TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_interface_claim(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING)); + printf("Releasing interface\n"); //Check that client0 can release the interface TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_release(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER)); //Check that client0 cannot release interface it has not claimed @@ -285,6 +250,7 @@ TEST_CASE("Test USB Host async API", "[usb_host][ignore]") usb_host_client_handle_events(client1_hdl, 0); vTaskDelay(10); } + printf("Closing device\n"); TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(client0_hdl, client0_dev_hdl)); TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(client1_hdl, client1_dev_hdl)); @@ -300,8 +266,4 @@ TEST_CASE("Test USB Host async API", "[usb_host][ignore]") } vTaskDelay(10); } - - //Cleanup - TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall()); - test_usb_deinit_phy(); } diff --git a/components/usb/test/usb_host/test_usb_host_plugging.c b/components/usb/test_apps/usb_host/main/test_usb_host_plugging.c similarity index 74% rename from components/usb/test/usb_host/test_usb_host_plugging.c rename to components/usb/test_apps/usb_host/main/test_usb_host_plugging.c index 4198b3205f..5851988f42 100644 --- a/components/usb/test/usb_host/test_usb_host_plugging.c +++ b/components/usb/test_apps/usb_host/main/test_usb_host_plugging.c @@ -10,12 +10,11 @@ #include "esp_err.h" #include "esp_intr_alloc.h" #include "test_usb_common.h" -#include "test_usb_mock_classes.h" +#include "test_usb_mock_msc.h" #include "msc_client.h" #include "ctrl_client.h" #include "usb/usb_host.h" #include "unity.h" -#include "test_utils.h" // --------------------------------------------------- Test Cases ------------------------------------------------------ @@ -34,17 +33,8 @@ Procedure: #define TEST_DCONN_NO_CLIENT_ITERATIONS 3 -TEST_CASE("Test USB Host sudden disconnection (no client)", "[usb_host][ignore]") +TEST_CASE("Test USB Host sudden disconnection (no client)", "[usb_host][full_speed][low_speed]") { - test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing - //Install USB Host Library - usb_host_config_t host_config = { - .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - printf("Installed\n"); - bool connected = false; int dconn_iter = 0; while (1) { @@ -73,10 +63,6 @@ TEST_CASE("Test USB Host sudden disconnection (no client)", "[usb_host][ignore]" } } } - - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing } /* @@ -95,17 +81,8 @@ Procedure: #define TEST_FORCE_DCONN_NUM_TRANSFERS 3 #define TEST_MSC_SCSI_TAG 0xDEADBEEF -TEST_CASE("Test USB Host sudden disconnection (single client)", "[usb_host][ignore]") +TEST_CASE("Test USB Host sudden disconnection (single client)", "[usb_host][full_speed]") { - test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing - //Install USB Host - usb_host_config_t host_config = { - .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - printf("Installed\n"); - //Create task to run client that communicates with MSC SCSI interface msc_client_test_param_t params = { .num_sectors_to_read = 1, //Unused by disconnect MSC client @@ -134,12 +111,6 @@ TEST_CASE("Test USB Host sudden disconnection (single client)", "[usb_host][igno all_dev_free = true; } } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing } /* @@ -160,17 +131,8 @@ Procedure: #define TEST_ENUM_ITERATIONS 3 -TEST_CASE("Test USB Host enumeration", "[usb_host][ignore]") +TEST_CASE("Test USB Host enumeration", "[usb_host][full_speed]") { - test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing - //Install USB Host - usb_host_config_t host_config = { - .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - printf("Installed\n"); - //Create task to run client that checks the enumeration of the device TaskHandle_t task_hdl; xTaskCreatePinnedToCore(msc_client_async_enum_task, "async", 6144, NULL, 2, &task_hdl, 0); @@ -192,10 +154,4 @@ TEST_CASE("Test USB Host enumeration", "[usb_host][ignore]") all_dev_free = true; } } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing } diff --git a/components/usb/test_apps/usb_host/pytest_usb_host.py b/components/usb/test_apps/usb_host/pytest_usb_host.py new file mode 100644 index 0000000000..bc63dca689 --- /dev/null +++ b/components/usb/test_apps/usb_host/pytest_usb_host.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.usb_host_flash_disk +def test_usb_host(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[full_speed]') + dut.expect_unity_test_output() diff --git a/components/usb/test_apps/usb_host/sdkconfig.defaults b/components/usb/test_apps/usb_host/sdkconfig.defaults new file mode 100644 index 0000000000..0632edf757 --- /dev/null +++ b/components/usb/test_apps/usb_host/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# CONFIG_ESP_TASK_WDT_INIT is not set +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +# CONFIG_UNITY_ENABLE_FLOAT is not set +# CONFIG_UNITY_ENABLE_DOUBLE is not set +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y diff --git a/pytest.ini b/pytest.ini index 16d6d3ac65..b0d8997b71 100644 --- a/pytest.ini +++ b/pytest.ini @@ -37,6 +37,7 @@ markers = quad_psram: runners with quad psram octal_psram: runners with octal psram usb_host: usb host runners + usb_host_flash_disk: usb host runners with USB flash disk attached usb_device: usb device runners ethernet_ota: ethernet OTA runners flash_encryption: Flash Encryption runners