mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-20 00:36:01 -04:00
usb: Add USB Host Library sudden disconnect tests
This commit is contained in:
parent
6a12e28333
commit
54b6c902d3
33
components/usb/test/common/test_usb_common.c
Normal file
33
components/usb/test/common/test_usb_common.c
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "test_usb_common.h"
|
||||
|
||||
void test_usb_force_conn_state(bool connected, TickType_t delay_ticks)
|
||||
{
|
||||
if (delay_ticks > 0) {
|
||||
//Delay of 0 ticks causes a yield. So skip if delay_ticks is 0.
|
||||
vTaskDelay(delay_ticks);
|
||||
}
|
||||
usb_wrap_dev_t *wrap = &USB_WRAP;
|
||||
if (connected) {
|
||||
//Disable test mode to return to previous internal PHY configuration
|
||||
wrap->test_conf.test_enable = 0;
|
||||
} else {
|
||||
/*
|
||||
Mimic a disconnection by using the internal PHY's test mode.
|
||||
Force Output Enable to 1 (even if the controller isn't outputting). With test_tx_dp and test_tx_dm set to 0,
|
||||
this will look like a disconnection.
|
||||
*/
|
||||
wrap->test_conf.val = 0;
|
||||
wrap->test_conf.test_usb_wrap_oe = 1;
|
||||
wrap->test_conf.test_enable = 1;
|
||||
}
|
||||
}
|
16
components/usb/test/common/test_usb_common.h
Normal file
16
components/usb/test/common/test_usb_common.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
/**
|
||||
* @brief For the USB PHY into the connected or disconnected state
|
||||
*
|
||||
* @param connected For into connected state if true, disconnected if false
|
||||
* @param delay_ticks Delay in ticks before forcing state
|
||||
*/
|
||||
void test_usb_force_conn_state(bool connected, TickType_t delay_ticks);
|
@ -1,16 +1,8 @@
|
||||
// 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: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@ -20,6 +12,8 @@
|
||||
|
||||
// ---------------------------------------------------- MSC SCSI -------------------------------------------------------
|
||||
|
||||
const char *MSC_CLIENT_TAG = "MSC Client";
|
||||
|
||||
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
|
||||
|
@ -1,16 +1,8 @@
|
||||
// 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: 2015-2021 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
|
||||
@ -29,6 +21,8 @@ extern "C" {
|
||||
|
||||
// ---------------------------------------------------- MSC SCSI -------------------------------------------------------
|
||||
|
||||
const char *MSC_CLIENT_TAG;
|
||||
|
||||
/*
|
||||
Note: The mock MSC SCSI tests requires that USB flash drive be connected. The flash drive should...
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "usb_private.h"
|
||||
#include "usb/usb_types_ch9.h"
|
||||
#include "test_hcd_common.h"
|
||||
#include "test_usb_common.h"
|
||||
|
||||
#define PORT_NUM 1
|
||||
#define EVENT_QUEUE_LEN 5
|
||||
@ -135,28 +136,6 @@ int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl)
|
||||
|
||||
// ----------------------------------------------- Driver/Port Related -------------------------------------------------
|
||||
|
||||
void test_hcd_force_conn_state(bool connected, TickType_t delay_ticks)
|
||||
{
|
||||
if (delay_ticks > 0) {
|
||||
//Delay of 0 ticks causes a yield. So skip if delay_ticks is 0.
|
||||
vTaskDelay(delay_ticks);
|
||||
}
|
||||
usb_wrap_dev_t *wrap = &USB_WRAP;
|
||||
if (connected) {
|
||||
//Disable test mode to return to previous internal PHY configuration
|
||||
wrap->test_conf.test_enable = 0;
|
||||
} else {
|
||||
/*
|
||||
Mimic a disconnection by using the internal PHY's test mode.
|
||||
Force Output Enable to 1 (even if the controller isn't outputting). With test_tx_dp and test_tx_dm set to 0,
|
||||
this will look like a disconnection.
|
||||
*/
|
||||
wrap->test_conf.val = 0;
|
||||
wrap->test_conf.test_usb_wrap_oe = 1;
|
||||
wrap->test_conf.test_enable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
hcd_port_handle_t test_hcd_setup(void)
|
||||
{
|
||||
//Create a queue for port callback to queue up port events
|
||||
@ -178,7 +157,7 @@ hcd_port_handle_t test_hcd_setup(void)
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl));
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, port_hdl);
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl));
|
||||
test_hcd_force_conn_state(false, 0); //Force disconnected state on PHY
|
||||
test_usb_force_conn_state(false, 0); //Force disconnected state on PHY
|
||||
return port_hdl;
|
||||
}
|
||||
|
||||
@ -201,7 +180,7 @@ usb_speed_t test_hcd_wait_for_conn(hcd_port_handle_t port_hdl)
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISCONNECTED, hcd_port_get_state(port_hdl));
|
||||
//Wait for connection event
|
||||
printf("Waiting for connection\n");
|
||||
test_hcd_force_conn_state(true, pdMS_TO_TICKS(100)); //Allow for connected state on PHY
|
||||
test_usb_force_conn_state(true, pdMS_TO_TICKS(100)); //Allow for connected state on PHY
|
||||
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_CONNECTION);
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_EVENT_CONNECTION, hcd_port_handle_event(port_hdl));
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
|
||||
@ -230,7 +209,7 @@ void test_hcd_wait_for_disconn(hcd_port_handle_t port_hdl, bool already_disabled
|
||||
}
|
||||
//Wait for a safe disconnect
|
||||
printf("Waiting for disconnection\n");
|
||||
test_hcd_force_conn_state(false, pdMS_TO_TICKS(100)); //Force disconnected state on PHY
|
||||
test_usb_force_conn_state(false, pdMS_TO_TICKS(100)); //Force disconnected state on PHY
|
||||
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(port_hdl));
|
||||
|
@ -48,14 +48,6 @@ int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl);
|
||||
|
||||
// ----------------------------------------------- Driver/Port Related -------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief For the USB PHY into the connected or disconnected state
|
||||
*
|
||||
* @param connected For into connected state if true, disconnected if false
|
||||
* @param delay_ticks Delay in ticks before forcing state
|
||||
*/
|
||||
void test_hcd_force_conn_state(bool connected, TickType_t delay_ticks);
|
||||
|
||||
/**
|
||||
* @brief Sets up the HCD and initializes an HCD port.
|
||||
*
|
||||
@ -73,7 +65,7 @@ void test_hcd_teardown(hcd_port_handle_t port_hdl);
|
||||
/**
|
||||
* @brief Wait for a connection on an HCD port
|
||||
*
|
||||
* @note This function will internally call test_hcd_force_conn_state() to allow for a connection
|
||||
* @note This function will internally call test_usb_force_conn_state() to allow for a connection
|
||||
*
|
||||
* @param port_hdl Port handle
|
||||
* @return usb_speed_t Speed of the connected device
|
||||
@ -83,7 +75,7 @@ usb_speed_t test_hcd_wait_for_conn(hcd_port_handle_t port_hdl);
|
||||
/**
|
||||
* @brief Wait for a disconnection on an HCD port
|
||||
*
|
||||
* @note This fucntion will internally call test_hcd_force_conn_state() to force a disconnection
|
||||
* @note This fucntion will internally call test_usb_force_conn_state() to force a disconnection
|
||||
*
|
||||
* @param port_hdl Port handle
|
||||
* @param already_disabled Whether the HCD port is already in the disabled state
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "test_usb_mock_classes.h"
|
||||
#include "test_usb_common.h"
|
||||
#include "test_hcd_common.h"
|
||||
|
||||
#define NUM_URBS 3
|
||||
@ -154,7 +155,7 @@ TEST_CASE("Test HCD isochronous pipe sudden disconnect", "[hcd][ignore]")
|
||||
}
|
||||
//Add a short delay to let the transfers run for a bit
|
||||
esp_rom_delay_us(POST_ENQUEUE_DELAY_US);
|
||||
test_hcd_force_conn_state(false, 0);
|
||||
test_usb_force_conn_state(false, 0);
|
||||
//Disconnect event should have occurred. Handle the port event
|
||||
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "unity.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "test_utils.h"
|
||||
#include "test_usb_common.h"
|
||||
#include "test_hcd_common.h"
|
||||
|
||||
#define TEST_DEV_ADDR 0
|
||||
@ -65,7 +66,7 @@ TEST_CASE("Test HCD port sudden disconnect", "[hcd][ignore]")
|
||||
}
|
||||
//Add a short delay to let the transfers run for a bit
|
||||
esp_rom_delay_us(POST_ENQUEUE_DELAY_US);
|
||||
test_hcd_force_conn_state(false, 0);
|
||||
test_usb_force_conn_state(false, 0);
|
||||
//Disconnect event should have occurred. Handle the port event
|
||||
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
|
||||
TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
|
||||
@ -304,7 +305,7 @@ static void concurrent_task(void *arg)
|
||||
xSemaphoreTake(sync_sem, portMAX_DELAY);
|
||||
vTaskDelay(pdMS_TO_TICKS(10)); //Give a short delay let reset command start in main thread
|
||||
//Force a disconnection
|
||||
test_hcd_force_conn_state(false, 0);
|
||||
test_usb_force_conn_state(false, 0);
|
||||
vTaskDelay(portMAX_DELAY); //Block forever and wait to be deleted
|
||||
}
|
||||
|
||||
|
@ -15,3 +15,5 @@ typedef struct {
|
||||
} msc_client_test_param_t;
|
||||
|
||||
void msc_client_async_seq_task(void *arg);
|
||||
|
||||
void msc_client_async_dconn_task(void *arg);
|
||||
|
246
components/usb/test/usb_host/msc_client_async_dconn.c
Normal file
246
components/usb/test/usb_host/msc_client_async_dconn.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "test_usb_mock_classes.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.
|
||||
|
||||
- The MSC client will:
|
||||
- Register itself as a client
|
||||
- Receive USB_HOST_CLIENT_EVENT_NEW_DEV event message, and open the device
|
||||
- Allocate IN and OUT transfer objects for MSC SCSI transfers
|
||||
- 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
|
||||
- Then a USB_HOST_CLIENT_EVENT_DEV_GONE event should occur afterwards
|
||||
- Free transfer objects
|
||||
- Close device
|
||||
- Deregister MSC client
|
||||
*/
|
||||
|
||||
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
|
||||
#define MSC_CLIENT_MAX_EVENT_MSGS 5
|
||||
|
||||
typedef enum {
|
||||
TEST_STAGE_WAIT_CONN,
|
||||
TEST_STAGE_DEV_OPEN,
|
||||
TEST_STAGE_MSC_RESET,
|
||||
TEST_STAGE_MSC_CBW,
|
||||
TEST_STAGE_MSC_DATA_DCONN,
|
||||
TEST_STAGE_DEV_CLOSE,
|
||||
} test_stage_t;
|
||||
|
||||
typedef struct {
|
||||
msc_client_test_param_t test_param;
|
||||
test_stage_t cur_stage;
|
||||
test_stage_t next_stage;
|
||||
uint8_t dev_addr_to_open;
|
||||
usb_host_client_handle_t client_hdl;
|
||||
usb_device_handle_t dev_hdl;
|
||||
int num_data_transfers;
|
||||
int event_count;
|
||||
} msc_client_obj_t;
|
||||
|
||||
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(transfer->num_bytes, transfer->actual_num_bytes);
|
||||
switch (msc_obj->cur_stage) {
|
||||
case TEST_STAGE_MSC_RESET:
|
||||
msc_obj->next_stage = TEST_STAGE_MSC_CBW;
|
||||
break;
|
||||
case TEST_STAGE_MSC_CBW:
|
||||
msc_obj->next_stage = TEST_STAGE_MSC_DATA_DCONN;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void msc_data_transfer_cb(usb_transfer_t *transfer)
|
||||
{
|
||||
//The data stage should have either completed, or failed due to the disconnection.
|
||||
TEST_ASSERT(transfer->status == USB_TRANSFER_STATUS_COMPLETED || transfer->status == USB_TRANSFER_STATUS_NO_DEVICE);
|
||||
if (transfer->status == USB_TRANSFER_STATUS_COMPLETED) {
|
||||
TEST_ASSERT_EQUAL(transfer->num_bytes, transfer->actual_num_bytes);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL(0, transfer->actual_num_bytes);
|
||||
}
|
||||
msc_client_obj_t *msc_obj = (msc_client_obj_t *)transfer->context;
|
||||
msc_obj->event_count++;
|
||||
//If all transfers dequeued and device gone event occurred. Go to next stage
|
||||
if (msc_obj->event_count >= msc_obj->num_data_transfers + 1) {
|
||||
msc_obj->next_stage = TEST_STAGE_DEV_CLOSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void msc_client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
|
||||
{
|
||||
msc_client_obj_t *msc_obj = (msc_client_obj_t *)arg;
|
||||
switch (event_msg->event) {
|
||||
case USB_HOST_CLIENT_EVENT_NEW_DEV:
|
||||
TEST_ASSERT_EQUAL(TEST_STAGE_WAIT_CONN, msc_obj->cur_stage);
|
||||
msc_obj->next_stage = TEST_STAGE_DEV_OPEN;
|
||||
msc_obj->dev_addr_to_open = event_msg->new_dev.address;
|
||||
break;
|
||||
case USB_HOST_CLIENT_EVENT_DEV_GONE:
|
||||
msc_obj->event_count++;
|
||||
//If all transfers dequeued and device gone event occurred. Go to next stage
|
||||
if (msc_obj->event_count >= msc_obj->num_data_transfers + 1) {
|
||||
msc_obj->next_stage = TEST_STAGE_DEV_CLOSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(); //Should never occur in this test
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void msc_client_async_dconn_task(void *arg)
|
||||
{
|
||||
msc_client_obj_t msc_obj;
|
||||
memcpy(&msc_obj.test_param, arg, sizeof(msc_client_test_param_t));
|
||||
msc_obj.cur_stage = TEST_STAGE_WAIT_CONN;
|
||||
msc_obj.next_stage = TEST_STAGE_WAIT_CONN;
|
||||
msc_obj.dev_addr_to_open = 0;
|
||||
msc_obj.client_hdl = NULL;
|
||||
msc_obj.dev_hdl = NULL;
|
||||
msc_obj.num_data_transfers = msc_obj.test_param.num_sectors_per_xfer / MOCK_MSC_SCSI_SECTOR_SIZE;
|
||||
msc_obj.event_count = 0;
|
||||
|
||||
//Register client
|
||||
usb_host_client_config_t client_config = {
|
||||
.client_event_callback = msc_client_event_cb,
|
||||
.callback_arg = (void *)&msc_obj,
|
||||
.max_num_event_msg = MSC_CLIENT_MAX_EVENT_MSGS,
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &msc_obj.client_hdl));
|
||||
|
||||
//Allocate transfers
|
||||
usb_transfer_t *xfer_out; //Must be large enough to contain CBW and MSC reset control transfer
|
||||
usb_transfer_t *xfer_in[msc_obj.num_data_transfers]; //We manually split the data stage into multiple transfers
|
||||
size_t xfer_out_size = MAX(sizeof(mock_msc_bulk_cbw_t), sizeof(usb_setup_packet_t));
|
||||
size_t xfer_in_size = MOCK_MSC_SCSI_SECTOR_SIZE;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_alloc(xfer_out_size, 0, &xfer_out));
|
||||
xfer_out->context = (void *)&msc_obj;
|
||||
for (int i = 0; i < msc_obj.num_data_transfers; i++) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_alloc(xfer_in_size, 0, &xfer_in[i]));
|
||||
xfer_in[i]->context = (void *)&msc_obj;
|
||||
}
|
||||
|
||||
//Wait to be started by main thread
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Starting");
|
||||
|
||||
bool exit_loop = false;
|
||||
bool skip_event_handling = false;
|
||||
while (!exit_loop) {
|
||||
if (!skip_event_handling) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_handle_events(msc_obj.client_hdl, portMAX_DELAY));
|
||||
}
|
||||
skip_event_handling = false;
|
||||
if (msc_obj.cur_stage == msc_obj.next_stage) {
|
||||
continue;
|
||||
}
|
||||
msc_obj.cur_stage = msc_obj.next_stage;
|
||||
|
||||
switch (msc_obj.cur_stage) {
|
||||
case TEST_STAGE_DEV_OPEN: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Open");
|
||||
//Open the device
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(msc_obj.client_hdl, msc_obj.dev_addr_to_open, &msc_obj.dev_hdl));
|
||||
//Target our transfers to the device
|
||||
xfer_out->device_handle = msc_obj.dev_hdl;
|
||||
xfer_out->callback = msc_reset_cbw_transfer_cb;
|
||||
for (int i = 0; i < msc_obj.num_data_transfers; i++) {
|
||||
xfer_in[i]->device_handle = msc_obj.dev_hdl;
|
||||
xfer_in[i]->callback = msc_data_transfer_cb;
|
||||
}
|
||||
//Check the VID/PID of the opened device
|
||||
const usb_device_desc_t *device_desc;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_device_descriptor(msc_obj.dev_hdl, &device_desc));
|
||||
TEST_ASSERT_EQUAL(msc_obj.test_param.idVendor, device_desc->idVendor);
|
||||
TEST_ASSERT_EQUAL(msc_obj.test_param.idProduct, device_desc->idProduct);
|
||||
//Claim the MSC interface
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_claim(msc_obj.client_hdl, msc_obj.dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING));
|
||||
msc_obj.next_stage = TEST_STAGE_MSC_RESET;
|
||||
skip_event_handling = true; //Need to execute TEST_STAGE_MSC_RESET
|
||||
break;
|
||||
}
|
||||
case TEST_STAGE_MSC_RESET: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "MSC Reset");
|
||||
//Send an MSC SCSI interface reset
|
||||
MOCK_MSC_SCSI_REQ_INIT_RESET((usb_setup_packet_t *)xfer_out->data_buffer, MOCK_MSC_SCSI_INTF_NUMBER);
|
||||
xfer_out->num_bytes = sizeof(usb_setup_packet_t);
|
||||
xfer_out->bEndpointAddress = 0;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_submit_control(msc_obj.client_hdl, xfer_out));
|
||||
//Next stage set from transfer callback
|
||||
break;
|
||||
}
|
||||
case TEST_STAGE_MSC_CBW: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "CBW");
|
||||
mock_msc_scsi_init_cbw((mock_msc_bulk_cbw_t *)xfer_out->data_buffer, true, 0, msc_obj.test_param.num_sectors_per_xfer, msc_obj.test_param.msc_scsi_xfer_tag);
|
||||
xfer_out->num_bytes = sizeof(mock_msc_bulk_cbw_t);
|
||||
xfer_out->bEndpointAddress = MOCK_MSC_SCSI_BULK_OUT_EP_ADDR;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_submit(xfer_out));
|
||||
//Next stage set from transfer callback
|
||||
break;
|
||||
}
|
||||
case TEST_STAGE_MSC_DATA_DCONN: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Data and disconnect");
|
||||
//Setup the Data IN transfers
|
||||
for (int i = 0; i < msc_obj.num_data_transfers; i++) {
|
||||
xfer_in[i]->num_bytes = usb_round_up_to_mps(MOCK_MSC_SCSI_SECTOR_SIZE, MOCK_MSC_SCSI_BULK_EP_MPS);
|
||||
xfer_in[i]->bEndpointAddress = MOCK_MSC_SCSI_BULK_IN_EP_ADDR;
|
||||
}
|
||||
//Submit those transfers
|
||||
for (int i = 0; i < msc_obj.num_data_transfers; i++) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_submit(xfer_in[i]));
|
||||
}
|
||||
//Trigger a disconnect
|
||||
test_usb_force_conn_state(false, 0);
|
||||
//Next stage set from transfer callback
|
||||
break;
|
||||
}
|
||||
case TEST_STAGE_DEV_CLOSE: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Close");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_release(msc_obj.client_hdl, msc_obj.dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(msc_obj.client_hdl, msc_obj.dev_hdl));
|
||||
exit_loop = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Free transfers
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_free(xfer_out));
|
||||
for (int i = 0; i < msc_obj.num_data_transfers; i++) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_free(xfer_in[i]));
|
||||
}
|
||||
//Deregister the client
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_deregister(msc_obj.client_hdl));
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Done");
|
||||
vTaskDelete(NULL);
|
||||
}
|
@ -6,10 +6,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "test_usb_mock_classes.h"
|
||||
@ -38,8 +37,6 @@ Implementation of an MSC client used for USB Host Tests
|
||||
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
|
||||
#define MSC_CLIENT_MAX_EVENT_MSGS 5
|
||||
|
||||
const char *MSC_CLIENT_TAG = "MSC Client";
|
||||
|
||||
typedef enum {
|
||||
TEST_STAGE_WAIT_CONN,
|
||||
TEST_STAGE_DEV_OPEN,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "test_usb_mock_classes.h"
|
||||
#include "msc_client.h"
|
||||
|
111
components/usb/test/usb_host/test_usb_host_plugging.c
Normal file
111
components/usb/test/usb_host/test_usb_host_plugging.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.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"
|
||||
|
||||
// --------------------------------------------------- Test Cases ------------------------------------------------------
|
||||
|
||||
//Safe approximation of time it takes to connect and enumerate the device
|
||||
#define TEST_FORCE_DCONN_DELAY_MS 400
|
||||
|
||||
static void trigger_dconn_timer_cb(TimerHandle_t xTimer)
|
||||
{
|
||||
printf("Forcing Sudden Disconnect\n");
|
||||
test_usb_force_conn_state(false, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Test USB Host sudden disconnection (no client)", "[usb_host][ignore]")
|
||||
{
|
||||
//Install USB Host Library
|
||||
usb_host_config_t host_config = {
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
ESP_ERROR_CHECK(usb_host_install(&host_config));
|
||||
printf("Installed\n");
|
||||
|
||||
//Allocate timer to force disconnection after a short delay
|
||||
TimerHandle_t timer_hdl = xTimerCreate("dconn",
|
||||
pdMS_TO_TICKS(TEST_FORCE_DCONN_DELAY_MS),
|
||||
pdFALSE,
|
||||
NULL,
|
||||
trigger_dconn_timer_cb);
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, timer_hdl);
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTimerStart(timer_hdl, portMAX_DELAY));
|
||||
|
||||
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_ALL_FREE) {
|
||||
printf("All devices cleaned up\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Cleanup timer
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTimerDelete(timer_hdl, portMAX_DELAY));
|
||||
//Clean up USB Host
|
||||
ESP_ERROR_CHECK(usb_host_uninstall());
|
||||
}
|
||||
|
||||
#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]")
|
||||
{
|
||||
//Install USB Host
|
||||
usb_host_config_t host_config = {
|
||||
.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
|
||||
.num_sectors_per_xfer = TEST_FORCE_DCONN_NUM_TRANSFERS * MOCK_MSC_SCSI_SECTOR_SIZE,
|
||||
.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_dconn_task, "async", 4096, (void *)¶ms, 2, &task_hdl, 0);
|
||||
//Start the task
|
||||
xTaskNotifyGive(task_hdl);
|
||||
|
||||
bool all_clients_gone = false;
|
||||
bool all_dev_free = false;
|
||||
while (!all_clients_gone || !all_dev_free) {
|
||||
//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");
|
||||
all_clients_gone = true;
|
||||
}
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
printf("All device's freed\n");
|
||||
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());
|
||||
}
|
@ -2538,8 +2538,6 @@ components/unity/include/unity_fixture_extras.h
|
||||
components/unity/include/unity_test_runner.h
|
||||
components/unity/unity_port_esp32.c
|
||||
components/unity/unity_runner.c
|
||||
components/usb/test/common/test_usb_mock_classes.c
|
||||
components/usb/test/common/test_usb_mock_classes.h
|
||||
components/usb/test/hcd/test_hcd_ctrl.c
|
||||
components/vfs/include/esp_vfs_common.h
|
||||
components/vfs/include/esp_vfs_eventfd.h
|
||||
|
Loading…
Reference in New Issue
Block a user