Skip to content

Commit

Permalink
refactor(cdc_acm): Merge open() and open_vendor_specific() functions
Browse files Browse the repository at this point in the history
The CDC compliance is now detected automatically
tore-espressif committed Sep 3, 2024
1 parent f95adbe commit c73ae4a
Showing 6 changed files with 135 additions and 187 deletions.
3 changes: 2 additions & 1 deletion host/class/cdc/usb_host_cdc_acm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [Unreleased]
## 2.0.4

- Fixed Control transfer allocation size for too small EP0 Max Packet Size (https://github.com/espressif/esp-idf/issues/14345)
- Merged `open()` and `open_vendor_specific()` functions. All types of CDC devices are now opened with `cdc_acm_host_open()`, CDC compliance is detected automatically

## 2.0.3

3 changes: 2 additions & 1 deletion host/class/cdc/usb_host_cdc_acm/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# USB Host CDC-ACM Class Driver

[![Component Registry](https://components.espressif.com/components/espressif/usb_host_cdc_acm/badge.svg)](https://components.espressif.com/components/espressif/usb_host_cdc_acm)
![maintenance-status](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg)

This component contains an implementation of a USB CDC-ACM Host Class Driver that is implemented on top of the [USB Host Library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html).

@@ -36,7 +37,7 @@ The following steps outline the typical API call pattern of the CDC-ACM Class Dr

1. Install the USB Host Library via `usb_host_install()`
2. Install the CDC-ACM driver via `cdc_acm_host_install()`
3. Call `cdc_acm_host_open()`/`cdc_acm_host_open_vendor_specific()` to open a target CDC-ACM/CDC-like device. These functions will block until the target device is connected or time-out
3. Call `cdc_acm_host_open()` to open a CDC-ACM/CDC-like device. This function will block until the target device is connected or timeout
4. To transmit data, call `cdc_acm_host_data_tx_blocking()`
5. When data is received, the driver will automatically run the receive data callback
6. An opened device can be closed via `cdc_acm_host_close()`
182 changes: 62 additions & 120 deletions host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c
Original file line number Diff line number Diff line change
@@ -560,14 +560,10 @@ static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev)
*/
static esp_err_t cdc_acm_transfers_allocate(cdc_dev_t *cdc_dev, const usb_ep_desc_t *notif_ep_desc, const usb_ep_desc_t *in_ep_desc, size_t in_buf_len, const usb_ep_desc_t *out_ep_desc, size_t out_buf_len)
{
assert(in_ep_desc);
assert(out_ep_desc);
esp_err_t ret;

// 0. Check IN and OUT endpoints
// If your open functions fail here, it signals that you either selected wrong interface number
// or that you are trying to open IAD CDC device with cdc_acm_host_open_vendor_specific() instead of cdc_acm_host_open()
// Refer to README.md for more information
ESP_RETURN_ON_FALSE(in_ep_desc && out_ep_desc, ESP_ERR_NOT_FOUND, TAG, "IN or OUT endpoint not found in this data interface");

// 1. Setup notification transfer if it is supported
if (notif_ep_desc) {
ESP_GOTO_ON_ERROR(
@@ -632,15 +628,15 @@ static esp_err_t cdc_acm_transfers_allocate(cdc_dev_t *cdc_dev, const usb_ep_des
}

/**
* @brief Find CDC interface in Device and Configuration descriptors
* @brief Check if the required interface is CDC compliant
*
* @param[in] device_desc Pointer to Device descriptor
* @param[in] config_desc Pointer do Configuration descriptor
* @param[in] intf_idx Index of the required interface
* @return true The required interface is CDC
* @return false The required interface is NOT CDC
* @return true The required interface is CDC compliant
* @return false The required interface is NOT CDC compliant
*/
static bool cdc_acm_find_interface(const usb_device_desc_t *device_desc, const usb_config_desc_t *config_desc, uint8_t intf_idx)
static bool cdc_acm_is_cdc_compliant(const usb_device_desc_t *device_desc, const usb_config_desc_t *config_desc, uint8_t intf_idx)
{
int desc_offset = 0;
if (((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) &&
@@ -715,9 +711,10 @@ static void cdc_acm_parse_functional_descriptors(cdc_dev_t *cdc_dev, const usb_i
/**
* @brief Parse CDC interface descriptor
*
* #. Make sure that the required interface is CDC
* #. Parse interface descriptors and save them
* #. Parse functional descriptors and save them
* #. Check if the required interface exists
* #. Parse the interface descriptor
* #. Check if the device is CDC compliant
* #. For CDC compliant devices also parse second interface descriptor and functional descriptors
*
* @note This function is called in open procedure of CDC compliant devices only.
* @param[in] cdc_dev Pointer to CDC device
@@ -738,49 +735,66 @@ static esp_err_t cdc_acm_parse_interface(cdc_dev_t *cdc_dev, uint8_t intf_idx, c
// Get required descriptors
ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc));
ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc));
const usb_intf_desc_t *first_intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx, 0, &desc_offset);
ESP_RETURN_ON_FALSE(
first_intf_desc,
ESP_ERR_NOT_FOUND, TAG, "Required interface no %d was not found.", intf_idx);

int temp_offset = desc_offset;
for (int i = 0; i < first_intf_desc->bNumEndpoints; i++) {
const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(first_intf_desc, i, config_desc->wTotalLength, &desc_offset);
assert(this_ep);

// Find and save endpoint descriptors
if (cdc_acm_find_interface(device_desc, config_desc, intf_idx)) {
const uint8_t notif_intf_idx = intf_idx;
const uint8_t data_intf_idx = intf_idx + 1;
if (USB_EP_DESC_GET_XFERTYPE(this_ep) == USB_TRANSFER_TYPE_INTR) {
cdc_dev->notif.intf_desc = first_intf_desc;
*notif_ep = this_ep;
} else if (USB_EP_DESC_GET_XFERTYPE(this_ep) == USB_TRANSFER_TYPE_BULK) {
cdc_dev->data.intf_desc = first_intf_desc;
if (USB_EP_DESC_GET_EP_DIR(this_ep)) {
*in_ep = this_ep;
} else {
*out_ep = this_ep;
}
}
desc_offset = temp_offset;
}

// 1. Notification IF and EP
cdc_dev->notif.intf_desc = usb_parse_interface_descriptor(config_desc, notif_intf_idx, 0, &desc_offset);
assert(cdc_dev->notif.intf_desc);
const bool cdc_compliant = cdc_acm_is_cdc_compliant(device_desc, config_desc, intf_idx);
if (cdc_compliant) {
cdc_dev->notif.intf_desc = first_intf_desc; // We make sure that intf_desc is set for CDC compliant devices that use EP0 as notification element
cdc_acm_parse_functional_descriptors(cdc_dev, cdc_dev->notif.intf_desc, config_desc->wTotalLength, &desc_offset);
*notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->notif.intf_desc, 0, config_desc->wTotalLength, &desc_offset);
assert(notif_ep);
}

// 2. Data IF and EP
if (!cdc_dev->data.intf_desc && cdc_compliant) {
// CDC compliant devices have data endpoints in the second interface
// Some devices offer alternate settings for data interface:
// First interface with 0 endpoints (default control pipe only) and second with standard 2 endpoints for full-duplex data
// We always select interface with 2 bulk endpoints
const int num_of_alternate = usb_parse_interface_number_of_alternate(config_desc, data_intf_idx);
const int num_of_alternate = usb_parse_interface_number_of_alternate(config_desc, intf_idx + 1);
for (int i = 0; i < num_of_alternate + 1; i++) {
const usb_intf_desc_t *d = usb_parse_interface_descriptor(config_desc, data_intf_idx, i, &desc_offset);
if (d && d->bNumEndpoints == 2) {
// We found interface index with two endpoints, save it
cdc_dev->data.intf_desc = d;
const usb_intf_desc_t *second_intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx + 1, i, &desc_offset);
temp_offset = desc_offset;
if (second_intf_desc && second_intf_desc->bNumEndpoints == 2) {
for (int i = 0; i < second_intf_desc->bNumEndpoints; i++) {
const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(second_intf_desc, i, config_desc->wTotalLength, &desc_offset);
assert(this_ep);
if (USB_EP_DESC_GET_XFERTYPE(this_ep) == USB_TRANSFER_TYPE_BULK) {
cdc_dev->data.intf_desc = second_intf_desc;
if (USB_EP_DESC_GET_EP_DIR(this_ep)) {
*in_ep = this_ep;
} else {
*out_ep = this_ep;
}
}
desc_offset = temp_offset;
}
break;
}
}
if (!cdc_dev->data.intf_desc) {
return ESP_ERR_NOT_FOUND;
}
int temp_offset = desc_offset;
for (int i = 0; i < 2; i++) {
const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset);
assert(this_ep);
if (USB_EP_DESC_GET_EP_DIR(this_ep)) {
*in_ep = this_ep;
} else {
*out_ep = this_ep;
}
desc_offset = temp_offset;
}
return ESP_OK;
}
return ESP_ERR_NOT_FOUND;

// If we did not find IN and OUT data endpoints, the device cannot be used
return (*in_ep && *out_ep) ? ESP_OK : ESP_ERR_NOT_FOUND;
}

esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret)
@@ -804,17 +818,12 @@ esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, c
const usb_ep_desc_t *out_ep = NULL;
ESP_GOTO_ON_ERROR(
cdc_acm_parse_interface(cdc_dev, interface_idx, &notif_ep, &in_ep, &out_ep),
err, TAG, "Could not find required interface");

// Check whether found Interfaces are really CDC-ACM
assert(cdc_dev->notif.intf_desc->bInterfaceClass == USB_CLASS_COMM);
assert(cdc_dev->notif.intf_desc->bInterfaceSubClass == USB_CDC_SUBCLASS_ACM);
assert(cdc_dev->notif.intf_desc->bNumEndpoints == 1);
assert(cdc_dev->data.intf_desc->bInterfaceClass == USB_CLASS_CDC_DATA);
assert(cdc_dev->data.intf_desc->bNumEndpoints == 2);
err, TAG, "Could not open required interface as CDC");

// Save Communication and Data protocols
cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol;
if (cdc_dev->notif.intf_desc) {
cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol;
}
cdc_dev->data_protocol = (cdc_data_protocol_t)cdc_dev->data.intf_desc->bInterfaceProtocol;

// The following line is here for backward compatibility with v1.0.*
@@ -836,73 +845,6 @@ esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, c
return ret;
}

esp_err_t cdc_acm_host_open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_num, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret)
{
esp_err_t ret;
CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG);
CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG);

xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY);

// Find underlying USB device
cdc_dev_t *cdc_dev;
ret = cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev);
if (ESP_OK != ret) {
goto exit;
}

// Open procedure for CDC-ACM non-compliant devices:
const usb_config_desc_t *config_desc;
int desc_offset;
ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc));
cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, interface_num, 0, &desc_offset);
ESP_GOTO_ON_FALSE(
cdc_dev->data.intf_desc,
ESP_ERR_NOT_FOUND, err, TAG, "Required interface no %d was not found.", interface_num);
const int temp_offset = desc_offset; // Save this offset for later

// The interface can have 2-3 endpoints. 2 for data and 1 optional for notifications
const usb_ep_desc_t *in_ep = NULL;
const usb_ep_desc_t *out_ep = NULL;
const usb_ep_desc_t *notif_ep = NULL;

// Go through all interface's endpoints and parse Interrupt and Bulk endpoints
for (int i = 0; i < cdc_dev->data.intf_desc->bNumEndpoints; i++) {
const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset);
assert(this_ep);

if (USB_EP_DESC_GET_XFERTYPE(this_ep) == USB_TRANSFER_TYPE_INTR) {
// Notification channel does not have its dedicated interface (data and notif interface is the same)
cdc_dev->notif.intf_desc = cdc_dev->data.intf_desc;
notif_ep = this_ep;
} else if (USB_EP_DESC_GET_XFERTYPE(this_ep) == USB_TRANSFER_TYPE_BULK) {
if (USB_EP_DESC_GET_EP_DIR(this_ep)) {
in_ep = this_ep;
} else {
out_ep = this_ep;
}
}
desc_offset = temp_offset;
}

// The following line is here for backward compatibility with v1.0.*
// where fixed size of IN buffer (equal to IN Maximum Packet Size) was used
const size_t in_buf_size = (dev_config->data_cb && (dev_config->in_buffer_size == 0)) ? USB_EP_DESC_GET_MPS(in_ep) : dev_config->in_buffer_size;

// Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle
ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, in_buf_size, out_ep, dev_config->out_buffer_size), err, TAG, );
ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,);
*cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev;
xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
return ESP_OK;
err:
cdc_acm_device_remove(cdc_dev);
exit:
xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
return ret;
}

esp_err_t cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl)
{
CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
2 changes: 1 addition & 1 deletion host/class/cdc/usb_host_cdc_acm/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## IDF Component Manager Manifest File
version: "2.0.3"
version: "2.0.4"
description: USB Host CDC-ACM driver
tags:
- usb
91 changes: 68 additions & 23 deletions host/class/cdc/usb_host_cdc_acm/include/usb/cdc_acm_host.h
Original file line number Diff line number Diff line change
@@ -17,6 +17,47 @@ extern "C" {

typedef struct cdc_dev_s *cdc_acm_dev_hdl_t;

/**
* @brief USB CDC PSTN Call Descriptor
*
* @see Table 3, USB CDC-PSTN specification rev. 1.2
*/
typedef struct {
uint8_t bFunctionLength;
const uint8_t bDescriptorType;
const cdc_desc_subtype_t bDescriptorSubtype;
union {
struct {
uint8_t call_management: 1; // Device handles call management itself
uint8_t call_over_data_if: 1; // Device sends/receives call management information over Data Class interface
uint8_t reserved: 6;
};
uint8_t val;
} bmCapabilities;
uint8_t bDataInterface; // Interface number of Data Class interface optionally used for call management
} __attribute__((packed)) cdc_acm_call_desc_t;

/**
* @brief USB CDC PSTN Abstract Control Model Descriptor
*
* @see Table 4, USB CDC-PSTN specification rev. 1.2
*/
typedef struct {
uint8_t bFunctionLength;
const uint8_t bDescriptorType;
const cdc_desc_subtype_t bDescriptorSubtype;
union {
struct {
uint8_t feature: 1; // Device supports Set/Clear/Get_Comm_Feature requests
uint8_t serial: 1; // Device supports Set/Get_Line_Coding, Set_Control_Line_State and Serial_State request and notifications
uint8_t send_break: 1; // Device supports Send_Break request
uint8_t network: 1; // Device supports Network_Connection notification
uint8_t reserved: 4;
};
uint8_t val;
} bmCapabilities;
} __attribute__((packed)) cdc_acm_acm_desc_t;

/**
* @brief Line Coding structure
* @see Table 17, USB CDC-PSTN specification rev. 1.2
@@ -131,7 +172,10 @@ typedef struct {
* - This function should be called before calling any other CDC driver functions
*
* @param[in] driver_config Driver configuration structure. If set to NULL, a default configuration will be used.
* @return esp_err_t
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_STATE: The CDC driver is already installed or USB host library is not installed
* - ESP_ERR_NO_MEM: Not enough memory for installing the driver
*/
esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config);

@@ -140,7 +184,10 @@ esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config
*
* - Users must ensure that all CDC devices must be closed via cdc_acm_host_close() before calling this function
*
* @return esp_err_t
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_STATE: The CDC driver is not installed or not all CDC devices are closed
* - ESP_ERR_NOT_FINISHED: The CDC driver failed to uninstall completely
*/
esp_err_t cdc_acm_host_uninstall(void);

@@ -150,39 +197,35 @@ esp_err_t cdc_acm_host_uninstall(void);
* The callback will be called for every new USB device, not just CDC-ACM class.
*
* @param[in] new_dev_cb New device callback function
* @return esp_err_t
* @return
* - ESP_OK: Success
*/
esp_err_t cdc_acm_host_register_new_dev_callback(cdc_acm_new_dev_callback_t new_dev_cb);

/**
* @brief Open CDC-ACM compliant device
* @brief Open CDC-ACM device
*
* CDC-ACM compliant device must contain either an Interface Association Descriptor or CDC-Union descriptor,
* which are used for the driver's configuration.
* 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
*
* @param[in] vid Device's Vendor ID
* @param[in] pid Device's Product ID
* @param[in] interface_idx Index of device's interface used for CDC-ACM communication
* @param[in] dev_config Configuration structure of the device
* @param[out] cdc_hdl_ret CDC device handle
* @return esp_err_t
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_STATE: The CDC driver is not installed
* - ESP_ERR_INVALID_ARG: dev_config or cdc_hdl_ret is NULL
* - ESP_ERR_NO_MEM: Not enough memory for opening the device
* - ESP_ERR_NOT_FOUND: USB device with specified VID/PID is not connected or does not have specified interface
*/
esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret);

/**
* @brief Open CDC-ACM non-compliant device
*
* CDC-ACM non-compliant device acts as CDC-ACM device but doesn't support all its features.
* User must provide the interface index that will be used (zero for non-composite devices).
*
* @param[in] vid Device's Vendor ID
* @param[in] pid Device's Product ID
* @param[in] interface_idx Index of device's interface used for CDC-ACM like communication
* @param[in] dev_config Configuration structure of the device
* @param[out] cdc_hdl_ret CDC device handle
* @return esp_err_t
*/
esp_err_t cdc_acm_host_open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_num, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret);
// This function is deprecated, please use cdc_acm_host_open()
static inline esp_err_t cdc_acm_host_open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_num, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret)
{
return cdc_acm_host_open(vid, pid, interface_num, dev_config, cdc_hdl_ret);
}

/**
* @brief Close CDC device and release its resources
@@ -268,7 +311,9 @@ void cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl);
* @param cdc_hdl CDC handle obtained from cdc_acm_host_open()
* @param[out] comm Communication protocol
* @param[out] data Data protocol
* @return esp_err_t
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: Invalid device
*/
esp_err_t cdc_acm_host_protocols_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_comm_protocol_t *comm, cdc_data_protocol_t *data);

@@ -329,7 +374,7 @@ class CdcAcmDevice {

inline esp_err_t open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config)
{
return cdc_acm_host_open_vendor_specific(vid, pid, interface_idx, dev_config, &this->cdc_hdl);
return cdc_acm_host_open(vid, pid, interface_idx, dev_config, &this->cdc_hdl);
}

inline esp_err_t close()
41 changes: 0 additions & 41 deletions host/class/cdc/usb_host_cdc_acm/include/usb/usb_types_cdc.h
Original file line number Diff line number Diff line change
@@ -205,44 +205,3 @@ typedef struct {
const uint8_t bControlInterface; // Master/controlling interface
uint8_t bSubordinateInterface[]; // Slave/subordinate interfaces
} __attribute__((packed)) cdc_union_desc_t;

/**
* @brief USB CDC PSTN Call Descriptor
*
* @see Table 3, USB CDC-PSTN specification rev. 1.2
*/
typedef struct {
uint8_t bFunctionLength;
const uint8_t bDescriptorType;
const cdc_desc_subtype_t bDescriptorSubtype;
union {
struct {
uint8_t call_management: 1; // Device handles call management itself
uint8_t call_over_data_if: 1; // Device sends/receives call management information over Data Class interface
uint8_t reserved: 6;
};
uint8_t val;
} bmCapabilities;
uint8_t bDataInterface; // Interface number of Data Class interface optionally used for call management
} __attribute__((packed)) cdc_acm_call_desc_t;

/**
* @brief USB CDC PSTN Abstract Control Model Descriptor
*
* @see Table 4, USB CDC-PSTN specification rev. 1.2
*/
typedef struct {
uint8_t bFunctionLength;
const uint8_t bDescriptorType;
const cdc_desc_subtype_t bDescriptorSubtype;
union {
struct {
uint8_t feature: 1; // Device supports Set/Clear/Get_Comm_Feature requests
uint8_t serial: 1; // Device supports Set/Get_Line_Coding, Set_Control_Line_State and Serial_State request and notifications
uint8_t send_break: 1; // Device supports Send_Break request
uint8_t network: 1; // Device supports Network_Connection notification
uint8_t reserved: 4;
};
uint8_t val;
} bmCapabilities;
} __attribute__((packed)) cdc_acm_acm_desc_t;

0 comments on commit c73ae4a

Please sign in to comment.