#include "usb_device.h"
#include <stdbool.h>
#include <stdatomic.h>
#ifndef USB_DEVICE_CDC_H
#define USB_DEVICE_CDC_H
#define USB_CLASS_CDC_CODE 0x02
#define USB_CLASS_DATA_CODE 0x0A
#define USB_SUBCLASS_CDC_ACM_CODE 0x02
/**
* @var USB_CLASS_CDC_ACM
* @brief Vtable of usb cdc acm class.
* @see usb_cdc_acm_configure to call after usb_device_init call to configure all descriptors.
* The usb cdc class is a standard usb class for communications and data transfer.
* acm stands for Abstract Control Model. It is used mainly in older modems to convert uart
* to usb. Here cdc acm is used since it is widely supported on computers, but there are no
* supported cdc acm commands. AT commands are not handled.
*/
extern usb_class_vtable_t USB_CLASS_CDC_ACM;
typedef enum __attribute__((packed)) {
SEND_ENCAPSULATED_COMMAND = 0,
GET_ENCAPSULATED_RESPONSE = 1,
/* SET_COMM_FEATURE = 2, */
/* GET_COMM_FEATURE = 3, */
/* CLEAR_COMM_FEATURE = 4, */
/* SET_AUX_LINE_STATE = 0x10, */
/* SET_HOOK_STATE = 0x11, */
/* PULSE_SETUP = 0x12, */
/* SEND_PULSE = 0x13, */
/* SET_PULSE_TIME = 0x14, */
/* RING_AUX_JACK = 0x15, */
/* SET_LINE_CODING = 0x20, */
/* GET_LINE_CODING = 0x21, */
} usb_cdc_request_code_type_t;
typedef enum __attribute__((packed)) {
CS_INTERFACE = 0x24,
CS_ENDPOINT = 0x25
} usb_cdc_functional_descriptor_type_t;
typedef enum __attribute__((packed)) {
HEADER_FUNCTIONAL_DESCRIPTOR_FUNCTIONAL_DESCRIPTOR = 0x00,
CALL_MANAGEMENT_FUNCTIONAL_FUNCTIONAL_DESCRIPTOR = 0x01,
ABSTRACT_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x02,
DIRECT_LINE_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x03,
TELEPHONE_RINGER_FUNCTIONAL_DESCRIPTOR = 0x04,
TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPABILITIES_FUNCTIONAL_DESCRIPTOR = 0x05,
UNION_FUNCTIONAL_DESCRIPTOR = 0x06,
CONTRY_SELECTION_FUNCTIONAL_DESCRIPTOR = 0x07,
TELEPHONE_OPERATIONAL_MODES_FUNCTIONAL_DESCRIPTOR = 0x08,
USB_TERMINAL_FUNCTIONAL_DESCRIPTOR = 0x09,
NETWORK_CHANNEL_FUNCTIONAL_DESCRIPTOR = 0x0A,
PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0x0B,
EXTENSION_UNIT_FUNCTIONAL_DESCRIPTOR = 0x0C,
MULTI_CHANNEL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x0D,
CAPI_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x0E,
ETHERNET_NETWORKING_FUNCTIONAL_DESCRIPTOR = 0x0F,
ATM_NETWORKING_FUNCTIONAL_DESCRIPTOR = 0x10,
WIRELESS_HANDSET_CONTROL_MODEL_FUNCTIONAL_DESCRIPTOR = 0x11,
} usb_cdc_functional_descriptor_subtype_t;
#pragma GCC diagnostic error "-Wpadded"
typedef struct __attribute__((packed)) {
uint8_t bFunctionLength;
usb_cdc_functional_descriptor_type_t bDescriptorType;
usb_cdc_functional_descriptor_subtype_t bDescriptorSubType;
} usb_cdc_functional_descriptor_header_t;
_Static_assert(sizeof(usb_cdc_functional_descriptor_header_t) == 3, "Size check");
typedef struct __attribute__((packed)) {
usb_cdc_functional_descriptor_header_t header;
uint16_t bcdCDC;
} usb_cdc_header_functional_decriptor_t;
_Static_assert(sizeof(usb_cdc_header_functional_decriptor_t) == 5, "Size check");
typedef struct {
usb_cdc_functional_descriptor_header_t header;
uint8_t bmCapabilities;
} usb_cdc_acm_functional_decriptor_t;
_Static_assert(sizeof(usb_cdc_acm_functional_decriptor_t) == 4, "Size check");
typedef struct {
usb_cdc_functional_descriptor_header_t header;
uint8_t bControlInterface;
uint8_t bSubordinateInterface0;
} usb_cdc_union_functional_decriptor_t;
_Static_assert(sizeof(usb_cdc_union_functional_decriptor_t) == 5, "Size check");
typedef struct {
usb_cdc_functional_descriptor_header_t header;
uint8_t bmCapabilities;
uint8_t bDataInterface;
} usb_cdc_call_management_functional_decriptor_t;
_Static_assert(sizeof(usb_cdc_call_management_functional_decriptor_t) == 5, "Size check");
typedef uint16_t (*cdc_receive_callback_t)(usb_device_t* device, uint8_t* data, uint16_t count);
typedef uint8_t* (*cdc_receive_location_callback_t)(usb_device_t* device, uint16_t count);
/* typedef enum { */
/* CDC_STATE_NONE, */
/* CDC_STATE_GOT_DATA, */
/* CDC_STATE_FULL_FIFO, */
/* } cdc_rx_state_t; */
typedef enum {
CDC_TX_STATE_NONE,
CDC_TX_STATE_SENDING,
CDC_TX_STATE_,
} cdc_tx_state_t;
#pragma GCC diagnostic ignored "-Wpadded"
typedef struct {
usb_class_header_t header;
uint8_t functional_descriptors_count;
usb_cdc_functional_descriptor_header_t** functional_descriptors;
cdc_receive_callback_t receive_callback;
cdc_receive_location_callback_t receive_location_callback;
// Internal state.
queue_t* rx_buffer;
cdc_tx_state_t tx_state;
uint8_t got_setup;
bool endpoint_nak;
uint32_t lost_data;
bool reenabled_endpoint;
} usb_device_cdc_t;
/**
* @brief Configures cdc acm descriptors
* @details All the descriptors for cdc acm device are initialized here.
* That means that after call to this function, usb_device_setup can be safely
* called, and all setup commands handled.
* @param[in,out] device The usb device.
* @param[in] rx_queue_size The size, in bytes, of the rx queue. When the queue is full, data cannot be received. There should not be any data lost, but it's not a 100% guarantee for now.
*/
void cdc_acm_configure(usb_device_t *device, uint16_t rx_queue_size);
void cdc_data_set_receive_callback(usb_device_t *device,
cdc_receive_callback_t callback);
void cdc_data_set_receive_location_callback(usb_device_t *device,
cdc_receive_location_callback_t callback);
int32_t cdc_data_send_blocking(usb_device_t *device, uint8_t *data, uint16_t size);
int32_t cdc_data_send(usb_device_t* device, uint8_t* data, uint16_t size);
uint16_t cdc_data_receive(usb_device_t* device, uint8_t* buffer, uint16_t max_size);
bool cdc_got_params(usb_device_t* device);
#endif // USB_DEVICE_CDC_H