Merge branch 'feature/usb_v5_update' into 'master'

usb: Run usb_host tests in CI

See merge request espressif/esp-idf!19468
This commit is contained in:
Tomas Rezucha 2022-12-01 15:29:01 +08:00
commit 7ae5705977
42 changed files with 891 additions and 675 deletions

View File

@ -235,7 +235,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"
@ -243,6 +243,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/**/*"
@ -546,6 +552,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-downloadable-tools
- <<: *if-dev-push
@ -573,6 +581,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-downloadable-tools
- <<: *if-dev-push
@ -599,6 +609,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-downloadable-tools
- <<: *if-dev-push
@ -625,6 +637,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-downloadable-tools
- <<: *if-dev-push
@ -651,6 +665,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-downloadable-tools
- <<: *if-dev-push
@ -677,6 +693,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-downloadable-tools
- <<: *if-dev-push
@ -703,6 +721,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-downloadable-tools
- <<: *if-dev-push
@ -729,6 +749,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-downloadable-tools
- <<: *if-dev-push
@ -1285,6 +1307,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
@ -1712,6 +1736,19 @@
- <<: *if-dev-push
changes: *patterns-component_ut-adc
.rules:test:component_ut-esp32s3-usb:
rules:
- <<: *if-revert-branch
when: never
- <<: *if-protected
- <<: *if-label-build-only
when: never
- <<: *if-label-component_ut
- <<: *if-label-component_ut_esp32s3
- <<: *if-label-target_test
- <<: *if-dev-push
changes: *patterns-component_ut-usb
.rules:test:custom_test-esp32:
rules:
- <<: *if-revert-branch

View File

@ -640,6 +640,14 @@ example_test_pytest_openthread_br:
- esp32h4
- i154_multi_dut
component_ut_pytest_esp32s3_usb_host:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32s3-usb
needs:
- build_pytest_components_esp32s3
tags: [ esp32s3, usb_host_flash_disk ]
.pytest_test_apps_dir_template:
extends: .pytest_template
variables:

View File

@ -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

View File

@ -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
)

View File

@ -1,132 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#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);
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#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);
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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

View File

@ -0,0 +1,158 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#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];
}
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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

View File

@ -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)

View File

@ -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).

View File

@ -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)

View File

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2022 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();
}

View File

@ -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);
}

View File

@ -8,7 +8,7 @@
#include <string.h>
#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);

View File

@ -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 ----------------------------------------------------

View File

@ -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 <stdio.h>
#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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -6,7 +6,6 @@
#include <stdio.h>
#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);

View File

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2022 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()

View File

@ -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

View File

@ -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)

View File

@ -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).

View File

@ -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)

View File

@ -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;

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2022 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();
}

View File

@ -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 *)&params, 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();
}

View File

@ -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
}

View File

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2022 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()

View File

@ -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

View File

@ -41,6 +41,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