mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
b6dfadb168
This commit updates the USB Host Library as follows: - usb_helpers.h - Removed dependency on USB Host Library API - Added function to print string descriptors - usbh - Fixed bug where an interface/endpoint could be claimed/allocated multiple times - Removed redundant device ref_count change - Added unit test for USB Host Library API usage
308 lines
12 KiB
C
308 lines
12 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/semphr.h"
|
|
#include "esp_err.h"
|
|
#include "esp_intr_alloc.h"
|
|
#include "test_usb_common.h"
|
|
#include "test_usb_mock_classes.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
|
|
#define TEST_MSC_SCSI_TAG 0xDEADBEEF
|
|
#define TEST_CTRL_NUM_TRANSFERS 30
|
|
|
|
// --------------------------------------------------- Test Cases ------------------------------------------------------
|
|
|
|
/*
|
|
Test USB Host Asynchronous API single client
|
|
|
|
Requires: This test requires an MSC SCSI device to be attached (see the MSC mock class)
|
|
|
|
Purpose:
|
|
- Test that USB Host Asynchronous API works correctly with a single client
|
|
- Test that a client can be created
|
|
- Test that client can operate concurrently in a separate thread
|
|
- Test that the main thread is able to detect library events (such as USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
|
|
|
|
Procedure:
|
|
- Install USB Host Library
|
|
- Create a task to run an MSC client
|
|
- Start the MSC client task. It will execute a bunch of MSC SCSI sector reads
|
|
- Wait for the host library event handler to report a USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS event
|
|
- Free all devices
|
|
- Uninstall USB Host Library
|
|
*/
|
|
|
|
TEST_CASE("Test USB Host async client (single client)", "[usb_host][ignore]")
|
|
{
|
|
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,
|
|
.num_sectors_per_xfer = TEST_MSC_NUM_SECTORS_PER_XFER,
|
|
.msc_scsi_xfer_tag = TEST_MSC_SCSI_TAG,
|
|
.idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR,
|
|
.idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT,
|
|
};
|
|
TaskHandle_t task_hdl;
|
|
xTaskCreatePinnedToCore(msc_client_async_seq_task, "async", 4096, (void *)¶ms, 2, &task_hdl, 0);
|
|
//Start the task
|
|
xTaskNotifyGive(task_hdl);
|
|
|
|
while (1) {
|
|
//Start handling system events
|
|
uint32_t event_flags;
|
|
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
|
printf("No more clients\n");
|
|
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
|
|
}
|
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
|
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
|
|
}
|
|
|
|
/*
|
|
Test USB Host Asynchronous API with multiple clients
|
|
|
|
Requires: This test requires an MSC SCSI device to be attached (see the MSC mock class)
|
|
|
|
Purpose:
|
|
- Test the USB Host Asynchronous API works correctly with multiple clients
|
|
- Test that multiple clients can be created
|
|
- Test that multiple clients can operate concurrently in separate threads
|
|
- Test that the main thread is able to detect library events (such as USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
|
|
|
|
Procedure:
|
|
- Install USB Host Library
|
|
- Create separate tasks to run an MSC client and Ctrl Client
|
|
- MSC Client will execute a bunch of MSC SCSI sector reads
|
|
- Ctrl Client will execute a bunch of control transfers
|
|
- Wait for the host library event handler to report a USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS event
|
|
- Free all devices
|
|
- Uninstall USB Host Library
|
|
*/
|
|
TEST_CASE("Test USB Host async client (multi client)", "[usb_host][ignore]")
|
|
{
|
|
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,
|
|
.num_sectors_per_xfer = TEST_MSC_NUM_SECTORS_PER_XFER,
|
|
.msc_scsi_xfer_tag = TEST_MSC_SCSI_TAG,
|
|
.idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR,
|
|
.idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT,
|
|
};
|
|
TaskHandle_t msc_task_hdl;
|
|
xTaskCreatePinnedToCore(msc_client_async_seq_task, "msc", 4096, (void *)&msc_params, 2, &msc_task_hdl, 0);
|
|
|
|
//Create task a control transfer client
|
|
ctrl_client_test_param_t ctrl_params = {
|
|
.num_ctrl_xfer_to_send = TEST_CTRL_NUM_TRANSFERS,
|
|
.idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR,
|
|
.idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT,
|
|
};
|
|
TaskHandle_t ctrl_task_hdl;
|
|
xTaskCreatePinnedToCore(ctrl_client_async_seq_task, "ctrl", 4096, (void *)&ctrl_params, 2, &ctrl_task_hdl, 0);
|
|
|
|
//Start both tasks
|
|
xTaskNotifyGive(msc_task_hdl);
|
|
xTaskNotifyGive(ctrl_task_hdl);
|
|
|
|
while (1) {
|
|
//Start handling system events
|
|
uint32_t event_flags;
|
|
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
|
printf("No more clients\n");
|
|
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
|
|
}
|
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
|
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
|
|
}
|
|
|
|
/*
|
|
Test USB Host Asynchronous API Usage
|
|
|
|
Requires: This test requires an MSC SCSI device to be attached (see the MSC mock class)
|
|
|
|
Purpose:
|
|
- Test that incorrect usage of USB Host Asynchronous API will returns errors
|
|
|
|
Procedure:
|
|
- Install USB Host Library
|
|
- Register two clients and all event handler functions from the same loop
|
|
- Wait for each client to detect device connection
|
|
- Check that both clients can open the same device
|
|
- Check that a client cannot open a non-existent device
|
|
- Check that only one client can claim a particular interface
|
|
- Check that a client cannot release an already released interface
|
|
- Wait for device disconnection
|
|
- Cleanup
|
|
*/
|
|
|
|
static uint8_t dev_addr = 0;
|
|
|
|
typedef enum {
|
|
CLIENT_TEST_STAGE_NONE,
|
|
CLIENT_TEST_STAGE_CONN,
|
|
CLIENT_TEST_STAGE_DCONN,
|
|
} client_test_stage_t;
|
|
|
|
static void test_async_client_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
|
|
{
|
|
client_test_stage_t *stage = (client_test_stage_t *)arg;
|
|
|
|
switch (event_msg->event) {
|
|
case USB_HOST_CLIENT_EVENT_NEW_DEV:
|
|
if (dev_addr == 0) {
|
|
dev_addr = event_msg->new_dev.address;
|
|
} else {
|
|
TEST_ASSERT_EQUAL(dev_addr, event_msg->new_dev.address);
|
|
}
|
|
*stage = CLIENT_TEST_STAGE_CONN;
|
|
break;
|
|
case USB_HOST_CLIENT_EVENT_DEV_GONE:
|
|
*stage = CLIENT_TEST_STAGE_DCONN;
|
|
break;
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Test USB Host async API", "[usb_host][ignore]")
|
|
{
|
|
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;
|
|
|
|
usb_host_client_config_t client_config = {
|
|
.is_synchronous = false,
|
|
.max_num_event_msg = 5,
|
|
.async = {
|
|
.client_event_callback = test_async_client_cb,
|
|
.callback_arg = (void *)&client0_stage,
|
|
},
|
|
};
|
|
usb_host_client_handle_t client0_hdl;
|
|
usb_host_client_handle_t client1_hdl;
|
|
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &client0_hdl));
|
|
client_config.async.callback_arg = (void *)&client1_stage;
|
|
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &client1_hdl));
|
|
|
|
//Wait until the device connects and the clients receive the event
|
|
while (!(client0_stage == CLIENT_TEST_STAGE_CONN && client1_stage == CLIENT_TEST_STAGE_CONN)) {
|
|
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);
|
|
}
|
|
|
|
//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;
|
|
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
|
|
//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));
|
|
|
|
//Check that the device cannot be opened again by the same client
|
|
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));
|
|
//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));
|
|
|
|
//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
|
|
TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_interface_release(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER));
|
|
|
|
//Wait until the device disconnects and the clients receive the event
|
|
test_usb_set_phy_state(false, 0);
|
|
while (!(client0_stage == CLIENT_TEST_STAGE_DCONN && client1_stage == CLIENT_TEST_STAGE_DCONN)) {
|
|
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);
|
|
}
|
|
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));
|
|
|
|
//Deregister the clients
|
|
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_deregister(client0_hdl));
|
|
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_deregister(client1_hdl));
|
|
|
|
while (1) {
|
|
uint32_t event_flags;
|
|
usb_host_lib_handle_events(0, &event_flags);
|
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
|
break;
|
|
}
|
|
vTaskDelay(10);
|
|
}
|
|
|
|
//Cleanup
|
|
TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall());
|
|
test_usb_deinit_phy();
|
|
}
|