forked from espressif/esp-idf
usb: Add USB Host Library sudden disconnect tests
This commit is contained in:
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
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -20,6 +12,8 @@
|
|||||||
|
|
||||||
// ---------------------------------------------------- MSC SCSI -------------------------------------------------------
|
// ---------------------------------------------------- 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)
|
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->dCBWSignature = 0x43425355; //Fixed value
|
||||||
|
@@ -1,16 +1,8 @@
|
|||||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This header contains bare-bone mock implementations of some device classes in order to test various layers of the USB
|
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 -------------------------------------------------------
|
// ---------------------------------------------------- MSC SCSI -------------------------------------------------------
|
||||||
|
|
||||||
|
const char *MSC_CLIENT_TAG;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note: The mock MSC SCSI tests requires that USB flash drive be connected. The flash drive should...
|
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_private.h"
|
||||||
#include "usb/usb_types_ch9.h"
|
#include "usb/usb_types_ch9.h"
|
||||||
#include "test_hcd_common.h"
|
#include "test_hcd_common.h"
|
||||||
|
#include "test_usb_common.h"
|
||||||
|
|
||||||
#define PORT_NUM 1
|
#define PORT_NUM 1
|
||||||
#define EVENT_QUEUE_LEN 5
|
#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 -------------------------------------------------
|
// ----------------------------------------------- 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)
|
hcd_port_handle_t test_hcd_setup(void)
|
||||||
{
|
{
|
||||||
//Create a queue for port callback to queue up port events
|
//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_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl));
|
||||||
TEST_ASSERT_NOT_EQUAL(NULL, port_hdl);
|
TEST_ASSERT_NOT_EQUAL(NULL, port_hdl);
|
||||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(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;
|
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));
|
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISCONNECTED, hcd_port_get_state(port_hdl));
|
||||||
//Wait for connection event
|
//Wait for connection event
|
||||||
printf("Waiting for connection\n");
|
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_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_EVENT_CONNECTION, hcd_port_handle_event(port_hdl));
|
||||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(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
|
//Wait for a safe disconnect
|
||||||
printf("Waiting for disconnection\n");
|
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_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_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
|
||||||
TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(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 -------------------------------------------------
|
// ----------------------------------------------- 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.
|
* @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
|
* @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
|
* @param port_hdl Port handle
|
||||||
* @return usb_speed_t Speed of the connected device
|
* @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
|
* @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 port_hdl Port handle
|
||||||
* @param already_disabled Whether the HCD port is already in the disabled state
|
* @param already_disabled Whether the HCD port is already in the disabled state
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
#include "test_usb_mock_classes.h"
|
#include "test_usb_mock_classes.h"
|
||||||
|
#include "test_usb_common.h"
|
||||||
#include "test_hcd_common.h"
|
#include "test_hcd_common.h"
|
||||||
|
|
||||||
#define NUM_URBS 3
|
#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
|
//Add a short delay to let the transfers run for a bit
|
||||||
esp_rom_delay_us(POST_ENQUEUE_DELAY_US);
|
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
|
//Disconnect event should have occurred. Handle the port event
|
||||||
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
|
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_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "esp_rom_sys.h"
|
#include "esp_rom_sys.h"
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
|
#include "test_usb_common.h"
|
||||||
#include "test_hcd_common.h"
|
#include "test_hcd_common.h"
|
||||||
|
|
||||||
#define TEST_DEV_ADDR 0
|
#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
|
//Add a short delay to let the transfers run for a bit
|
||||||
esp_rom_delay_us(POST_ENQUEUE_DELAY_US);
|
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
|
//Disconnect event should have occurred. Handle the port event
|
||||||
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
|
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_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
|
||||||
@@ -304,7 +305,7 @@ static void concurrent_task(void *arg)
|
|||||||
xSemaphoreTake(sync_sem, portMAX_DELAY);
|
xSemaphoreTake(sync_sem, portMAX_DELAY);
|
||||||
vTaskDelay(pdMS_TO_TICKS(10)); //Give a short delay let reset command start in main thread
|
vTaskDelay(pdMS_TO_TICKS(10)); //Give a short delay let reset command start in main thread
|
||||||
//Force a disconnection
|
//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
|
vTaskDelay(portMAX_DELAY); //Block forever and wait to be deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,3 +15,5 @@ typedef struct {
|
|||||||
} msc_client_test_param_t;
|
} msc_client_test_param_t;
|
||||||
|
|
||||||
void msc_client_async_seq_task(void *arg);
|
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 <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <stdlib.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "test_usb_mock_classes.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 MAX(x,y) (((x) >= (y)) ? (x) : (y))
|
||||||
#define MSC_CLIENT_MAX_EVENT_MSGS 5
|
#define MSC_CLIENT_MAX_EVENT_MSGS 5
|
||||||
|
|
||||||
const char *MSC_CLIENT_TAG = "MSC Client";
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TEST_STAGE_WAIT_CONN,
|
TEST_STAGE_WAIT_CONN,
|
||||||
TEST_STAGE_DEV_OPEN,
|
TEST_STAGE_DEV_OPEN,
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
#include "esp_err.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "test_usb_mock_classes.h"
|
#include "test_usb_mock_classes.h"
|
||||||
#include "msc_client.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());
|
||||||
|
}
|
Reference in New Issue
Block a user