Skip to content

Commit 3aa598d

Browse files
Merge pull request #71 from espressif/feat/cdc_acm_open_any
Add option to open a CDC device with any VID or PID
2 parents 9be78b9 + 1ed2eb3 commit 3aa598d

File tree

6 files changed

+75
-17
lines changed

6 files changed

+75
-17
lines changed

host/class/cdc/usb_host_cdc_acm/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
## [Unreleased]
1+
## 2.0.5
22

33
- Fixed CDC descriptor parsing logic, when some CDC devices could not be opened
4+
- Added an option to open a CDC device with any VID/PID
5+
- Fixed crash on ESP32-P4 if Receive buffer append function (introduced in v2.0.0) was used; this function does not work on P4 yet
46

57
## 2.0.4
68

host/class/cdc/usb_host_cdc_acm/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ The following steps outline the typical API call pattern of the CDC-ACM Class Dr
4343
6. An opened device can be closed via `cdc_acm_host_close()`
4444
7. The CDC-ACM driver can be uninstalled via `cdc_acm_host_uninstall()`
4545

46+
Use `CDC_HOST_ANY_*` macros to signal to `cdc_acm_host_open()` function that you don't care about the device's VID and PID. In this case, first USB device will be opened. It is recommended to use this feature if only one device can ever be in the system (there is no USB HUB connected).
47+
4648
## Examples
4749

4850
- For an example with a CDC-ACM device, refer to [cdc_acm_host](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_host)

host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "freertos/task.h"
1313
#include "freertos/semphr.h"
1414
#include "freertos/event_groups.h"
15+
#include "soc/soc_caps.h"
1516
#include "esp_log.h"
1617
#include "esp_check.h"
1718
#include "esp_system.h"
@@ -172,7 +173,8 @@ static void cdc_acm_reset_in_transfer(cdc_dev_t *cdc_dev)
172173
uint8_t **ptr = (uint8_t **)(&(transfer->data_buffer));
173174
*ptr = cdc_dev->data.in_data_buffer_base;
174175
transfer->num_bytes = transfer->data_buffer_size;
175-
// This is a hotfix for IDF changes, where 'transfer->data_buffer_size' does not contain actual buffer length, but *allocated* buffer length, which can be larger
176+
// This is a hotfix for IDF changes, where 'transfer->data_buffer_size' does not contain actual buffer length,
177+
// but *allocated* buffer length, which can be larger if CONFIG_HEAP_POISONING_COMPREHENSIVE is enabled
176178
transfer->num_bytes -= transfer->data_buffer_size % cdc_dev->data.in_mps;
177179
}
178180

@@ -255,7 +257,7 @@ static esp_err_t cdc_acm_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t e
255257
cdc_dev->data.intf_desc->bAlternateSetting),
256258
err, TAG, "Could not claim interface");
257259
if (cdc_dev->data.in_xfer) {
258-
ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer");
260+
ESP_LOGD(TAG, "Submitting poll for BULK IN transfer");
259261
ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->data.in_xfer));
260262
}
261263

@@ -270,7 +272,7 @@ static esp_err_t cdc_acm_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t e
270272
cdc_dev->notif.intf_desc->bAlternateSetting),
271273
err, TAG, "Could not claim interface");
272274
}
273-
ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer");
275+
ESP_LOGD(TAG, "Submitting poll for INTR IN transfer");
274276
ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->notif.xfer));
275277
}
276278

@@ -337,7 +339,8 @@ static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, in
337339
SLIST_FOREACH(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry) {
338340
const usb_device_desc_t *device_desc;
339341
ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc));
340-
if (device_desc->idVendor == vid && device_desc->idProduct == pid) {
342+
if ((vid == device_desc->idVendor || vid == CDC_HOST_ANY_VID) &&
343+
(pid == device_desc->idProduct || pid == CDC_HOST_ANY_PID)) {
341344
// Return path 1:
342345
(*dev)->dev_hdl = cdc_dev->dev_hdl;
343346
return ESP_OK;
@@ -365,7 +368,8 @@ static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, in
365368
assert(current_device);
366369
const usb_device_desc_t *device_desc;
367370
ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc));
368-
if (device_desc->idVendor == vid && device_desc->idProduct == pid) {
371+
if ((vid == device_desc->idVendor || vid == CDC_HOST_ANY_VID) &&
372+
(pid == device_desc->idProduct || pid == CDC_HOST_ANY_PID)) {
369373
// Return path 2:
370374
(*dev)->dev_hdl = current_device;
371375
return ESP_OK;
@@ -788,7 +792,7 @@ static bool cdc_acm_is_transfer_completed(usb_transfer_t *transfer)
788792

789793
static void in_xfer_cb(usb_transfer_t *transfer)
790794
{
791-
ESP_LOGD("CDC_ACM", "in xfer cb");
795+
ESP_LOGD(TAG, "in xfer cb");
792796
cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context;
793797

794798
if (!cdc_acm_is_transfer_completed(transfer)) {
@@ -803,6 +807,7 @@ static void in_xfer_cb(usb_transfer_t *transfer)
803807
// In this case, the next received data must be appended to the existing buffer.
804808
// Since the data_buffer in usb_transfer_t is a constant pointer, we must cast away to const qualifier.
805809
if (!data_processed) {
810+
#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
806811
// In case the received data was not processed, the next RX data must be appended to current buffer
807812
uint8_t **ptr = (uint8_t **)(&(transfer->data_buffer));
808813
*ptr += transfer->actual_num_bytes;
@@ -827,18 +832,23 @@ static void in_xfer_cb(usb_transfer_t *transfer)
827832
cdc_acm_reset_in_transfer(cdc_dev);
828833
cdc_dev->serial_state.bOverRun = false;
829834
}
835+
#else
836+
// For targets that must sync internal memory through L1CACHE, we cannot change the data_buffer
837+
// because it would lead to unaligned cache sync, which is not allowed
838+
ESP_LOGW(TAG, "RX buffer append is not yet supported on ESP32-P4!");
839+
#endif
830840
} else {
831841
cdc_acm_reset_in_transfer(cdc_dev);
832842
}
833843
}
834844

835-
ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer");
845+
ESP_LOGD(TAG, "Submitting poll for BULK IN transfer");
836846
usb_host_transfer_submit(cdc_dev->data.in_xfer);
837847
}
838848

839849
static void notif_xfer_cb(usb_transfer_t *transfer)
840850
{
841-
ESP_LOGD("CDC_ACM", "notif xfer cb");
851+
ESP_LOGD(TAG, "notif xfer cb");
842852
cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context;
843853

844854
if (cdc_acm_is_transfer_completed(transfer)) {
@@ -867,20 +877,20 @@ static void notif_xfer_cb(usb_transfer_t *transfer)
867877
}
868878
case USB_CDC_NOTIF_RESPONSE_AVAILABLE: // Encapsulated commands not implemented - fallthrough
869879
default:
870-
ESP_LOGW("CDC_ACM", "Unsupported notification type 0x%02X", notif->bNotificationCode);
871-
ESP_LOG_BUFFER_HEX("CDC_ACM", transfer->data_buffer, transfer->actual_num_bytes);
880+
ESP_LOGW(TAG, "Unsupported notification type 0x%02X", notif->bNotificationCode);
881+
ESP_LOG_BUFFER_HEX(TAG, transfer->data_buffer, transfer->actual_num_bytes);
872882
break;
873883
}
874884

875885
// Start polling for new data again
876-
ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer");
886+
ESP_LOGD(TAG, "Submitting poll for INTR IN transfer");
877887
usb_host_transfer_submit(cdc_dev->notif.xfer);
878888
}
879889
}
880890

881891
static void out_xfer_cb(usb_transfer_t *transfer)
882892
{
883-
ESP_LOGD("CDC_ACM", "out/ctrl xfer cb");
893+
ESP_LOGD(TAG, "out/ctrl xfer cb");
884894
assert(transfer->context);
885895
xSemaphoreGive((SemaphoreHandle_t)transfer->context);
886896
}
@@ -945,7 +955,7 @@ esp_err_t cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t
945955
return ESP_ERR_TIMEOUT;
946956
}
947957

948-
ESP_LOGD("CDC_ACM", "Submitting BULK OUT transfer");
958+
ESP_LOGD(TAG, "Submitting BULK OUT transfer");
949959
SemaphoreHandle_t transfer_finished_semaphore = (SemaphoreHandle_t)cdc_dev->data.out_xfer->context;
950960
xSemaphoreTake(transfer_finished_semaphore, 0); // Make sure the semaphore is taken before we submit new transfer
951961

host/class/cdc/usb_host_cdc_acm/idf_component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## IDF Component Manager Manifest File
2-
version: "2.0.4"
2+
version: "2.0.5"
33
description: USB Host CDC-ACM driver
44
tags:
55
- usb

host/class/cdc/usb_host_cdc_acm/include/usb/cdc_acm_host.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#include "usb/usb_types_cdc.h"
1212
#include "esp_err.h"
1313

14+
// Pass these to cdc_acm_host_open() to signal that you don't care about VID/PID of the opened device
15+
#define CDC_HOST_ANY_VID (0)
16+
#define CDC_HOST_ANY_PID (0)
17+
1418
#ifdef __cplusplus
1519
extern "C" {
1620
#endif
@@ -166,8 +170,11 @@ esp_err_t cdc_acm_host_register_new_dev_callback(cdc_acm_new_dev_callback_t new_
166170
*
167171
* The driver first looks for CDC compliant descriptor, if it is not found the driver checks if the interface has 2 Bulk endpoints that can be used for data
168172
*
169-
* @param[in] vid Device's Vendor ID
170-
* @param[in] pid Device's Product ID
173+
* Use CDC_HOST_ANY_* macros to signal that you don't care about the device's VID and PID. In this case, first USB device will be opened.
174+
* It is recommended to use this feature if only one device can ever be in the system (there is no USB HUB connected).
175+
*
176+
* @param[in] vid Device's Vendor ID, set to CDC_HOST_ANY_VID for any
177+
* @param[in] pid Device's Product ID, set to CDC_HOST_ANY_PID for any
171178
* @param[in] interface_idx Index of device's interface used for CDC-ACM communication
172179
* @param[in] dev_config Configuration structure of the device
173180
* @param[out] cdc_hdl_ret CDC device handle

host/class/cdc/usb_host_cdc_acm/test_app/main/test_cdc_acm_host.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,43 @@ TEST_CASE("tx_timeout", "[cdc_acm]")
674674
vTaskDelay(20); //Short delay to allow task to be cleaned up
675675
}
676676

677+
/**
678+
* @brief Test: Opening with any VID/PID
679+
*
680+
* #. Try to open a device with all combinations of any VID/PID
681+
* #. Try to open a non-existing device with all combinations of any VID/PID
682+
*/
683+
TEST_CASE("any_vid_pid", "[cdc_acm]")
684+
{
685+
cdc_acm_dev_hdl_t cdc_dev = NULL;
686+
test_install_cdc_driver();
687+
688+
const cdc_acm_host_device_config_t dev_config = {
689+
.connection_timeout_ms = 500,
690+
.out_buffer_size = 64,
691+
.event_cb = notif_cb,
692+
.data_cb = handle_rx,
693+
.user_arg = tx_buf,
694+
};
695+
696+
printf("Opening existing CDC-ACM devices with any VID/PID\n");
697+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(CDC_HOST_ANY_VID, CDC_HOST_ANY_PID, 0, &dev_config, &cdc_dev));
698+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev));
699+
700+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, CDC_HOST_ANY_PID, 0, &dev_config, &cdc_dev));
701+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev));
702+
703+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(CDC_HOST_ANY_VID, 0x4002, 0, &dev_config, &cdc_dev));
704+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev));
705+
706+
printf("Opening non-existing CDC-ACM devices with any VID/PID\n");
707+
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_open(0x1234, CDC_HOST_ANY_PID, 0, &dev_config, &cdc_dev));
708+
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_open(CDC_HOST_ANY_VID, 0x1234, 0, &dev_config, &cdc_dev));
709+
710+
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall());
711+
vTaskDelay(20); //Short delay to allow task to be cleaned up
712+
}
713+
677714
/* Following test case implements dual CDC-ACM USB device that can be used as mock device for CDC-ACM Host tests */
678715
void run_usb_dual_cdc_device(void);
679716
TEST_CASE("mock_device_app", "[cdc_acm_device][ignore]")

0 commit comments

Comments
 (0)