Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,28 +465,36 @@ static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev)
assert(cdc_dev);
if (cdc_dev->notif.xfer != NULL) {
usb_host_transfer_free(cdc_dev->notif.xfer);
cdc_dev->notif.xfer = NULL;
}
if (cdc_dev->data.in_xfer != NULL) {
cdc_acm_reset_in_transfer(cdc_dev);
usb_host_transfer_free(cdc_dev->data.in_xfer);
cdc_dev->data.in_xfer = NULL;
}
if (cdc_dev->data.out_xfer != NULL) {
if (cdc_dev->data.out_xfer->context != NULL) {
vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->data.out_xfer->context);
cdc_dev->data.out_xfer->context = NULL;
}
if (cdc_dev->data.out_mux != NULL) {
vSemaphoreDelete(cdc_dev->data.out_mux);
cdc_dev->data.out_mux = NULL;
}
usb_host_transfer_free(cdc_dev->data.out_xfer);
cdc_dev->data.out_xfer = NULL;
}
if (cdc_dev->ctrl_transfer != NULL) {
if (cdc_dev->ctrl_transfer->context != NULL) {
vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context);
cdc_dev->ctrl_transfer->context = NULL;
}
if (cdc_dev->ctrl_mux != NULL) {
vSemaphoreDelete(cdc_dev->ctrl_mux);
cdc_dev->ctrl_mux = NULL;
}
usb_host_transfer_free(cdc_dev->ctrl_transfer);
cdc_dev->ctrl_transfer = NULL;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ static cdc_func_array_t *cdc_parse_functional_descriptors(const usb_intf_desc_t
}
func_desc_cnt++;
}
*desc_cnt = func_desc_cnt;

// Allocate memory for the functional descriptors pointers
cdc_func_array_t *func_desc = malloc(func_desc_cnt * (sizeof(usb_standard_desc_t *)));
Expand All @@ -106,7 +107,6 @@ static cdc_func_array_t *cdc_parse_functional_descriptors(const usb_intf_desc_t
cdc_desc = (const usb_standard_desc_t *)usb_parse_next_descriptor(cdc_desc, total_len, &intf_offset);
(*func_desc)[i] = cdc_desc;
}
*desc_cnt = func_desc_cnt;
return func_desc;
}

Expand Down Expand Up @@ -143,6 +143,10 @@ esp_err_t cdc_parse_interface_descriptor(const usb_device_desc_t *device_desc, c
if (cdc_compliant) {
info_ret->notif_intf = first_intf_desc; // We make sure that intf_desc is set for CDC compliant devices that use EP0 as notification element
info_ret->func = cdc_parse_functional_descriptors(first_intf_desc, config_desc->wTotalLength, desc_offset, &info_ret->func_cnt);
if (info_ret->func == NULL && info_ret->func_cnt > 0) {
// There are CDC specific descriptors, but we failed to allocate memory for them
return ESP_ERR_NO_MEM;
}
}

if (!info_ret->data_intf && cdc_compliant) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ static void _set_remote_wakeup(cdc_acm_dev_hdl_t *dev, uint8_t address)

SCENARIO("Invalid custom command")
{
Mockusb_host_Init();
_add_mocked_devices();

GIVEN("Mocked device is opened") {
Expand Down Expand Up @@ -224,6 +225,7 @@ SCENARIO("Invalid custom command")

SCENARIO("Interact with mocked USB devices")
{
Mockusb_host_Init();
// We put the device adding to the SECTION, to run it just once, not repeatedly for all the following SECTIONs
SECTION("Add mocked devices") {

Expand Down Expand Up @@ -423,6 +425,7 @@ SCENARIO("Interact with mocked USB devices")

SCENARIO("TinyUSB serial")
{
Mockusb_host_Init();
GIVEN("CDC driver installed") {
_add_mocked_devices();
REQUIRE(ESP_OK == test_cdc_acm_host_install(nullptr));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extern "C" {

SCENARIO("Test mocked device opening and closing")
{
Mockusb_host_Init();
usb_host_mock_dev_list_init();
SECTION("Fail to open CDC-ACM Device: CDC-ACM Host is not installed") {
REQUIRE(ESP_ERR_INVALID_STATE == cdc_acm_host_open(0, 0, 0, nullptr, nullptr));
Expand All @@ -38,7 +39,7 @@ SCENARIO("Test mocked device opening and closing")

cdc_acm_dev_hdl_t dev = nullptr;
// Define input parameters for cdc_acm_host_open
// VID, PID, interface index and IN EP of the added cp120x device
// VID, PID, interface index and IN EP of the added cp210x device
const uint16_t vid = 0x10C4, pid = 0xEA60;
const uint8_t interface_index = 0;
const uint8_t in_ep = 0x82;
Expand Down Expand Up @@ -129,3 +130,78 @@ SCENARIO("Test mocked device opening and closing")
REQUIRE(ESP_OK == test_cdc_acm_host_uninstall());
}
}

/**
* @brief Test usb_host_transfer_alloc() failures
*
* CH340 exposes an interrupt notification endpoint plus bulk IN/OUT, so cdc_acm_transfers_allocate()
* calls usb_host_transfer_alloc() in this order: notification, control, bulk IN, bulk OUT.
*
* Test how the driver reacts to ESP_ERR_NO_MEM from usb_host_transfer_alloc()
*/
SCENARIO("Test usb_host_transfer_alloc() failures")
{
Mockusb_host_Init();
usb_host_mock_dev_list_init();

GIVEN("CDC driver is installed and USB host is mocked without transfer alloc stub") {
REQUIRE(ESP_OK == test_cdc_acm_host_install(nullptr));

usb_host_device_addr_list_fill_Stub(usb_host_device_addr_list_fill_mock_callback);
usb_host_device_open_Stub(usb_host_device_open_mock_callback);
usb_host_get_device_descriptor_Stub(usb_host_get_device_descriptor_mock_callback);
usb_host_device_close_Stub(usb_host_device_close_mock_callback);
usb_host_get_active_config_descriptor_Stub(usb_host_get_active_config_descriptor_mock_callback);
// Deliberately no usb_host_transfer_alloc_Stub / usb_host_transfer_free_Stub: use CMock expectations below.

const uint16_t vid = 0x1A86;
const uint16_t pid = 0x7523;
const uint8_t interface_index = 0;
cdc_acm_host_device_config_t dev_config = {
.connection_timeout_ms = 1,
.out_buffer_size = 64,
.in_buffer_size = 64,
.event_cb = nullptr,
.data_cb = nullptr,
.user_arg = nullptr,
};

REQUIRE(ESP_OK == usb_host_mock_add_device(5, (const usb_device_desc_t *)ch340_device_desc,
(const usb_config_desc_t *)ch340_config_desc));


// Fake transfers to return from usb_host_transfer_alloc
usb_transfer_t xfer0{};
usb_transfer_t xfer1{};
usb_transfer_t xfer2{};
usb_transfer_t *ptr[3] = { &xfer0, &xfer1, &xfer2 };

// CH340 allocates 4 transfers: notification, control, bulk IN, bulk OUT
// Test reaction of failure of all of them
for (int i = 0; i < 4; i++) {
SECTION("Failing to allocate transfer " + std::to_string(i + 1) + " of 4") {
// First i transfer allocations should succeed
for (int j = 0; j < i; j++) {
usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK);
usb_host_transfer_alloc_ReturnThruPtr_transfer(&ptr[j]);
}

// Last transfer allocation should fail
usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_ERR_NO_MEM);

// All allocated transfers should be freed
for (int j = 0; j < i; j++) {
usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK);
}

usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK);

cdc_acm_dev_hdl_t dev = nullptr;
REQUIRE(ESP_ERR_NO_MEM == cdc_acm_host_open(vid, pid, interface_index, &dev_config, &dev));
REQUIRE(dev == nullptr);
}
}

REQUIRE(ESP_OK == test_cdc_acm_host_uninstall());
}
}
Loading