Device Protocol Stack
The device protocol stack is mainly responsible for enumeration and driver loading. We won’t discuss enumeration here, but for driver loading (i.e., interface driver loading), it mainly relies on the usbd_add_interface function to record the passed-in interface driver and save it to the interface array table. When the host makes class requests, it can search the interface table for access. After calling usbd_desc_register, interface registration and endpoint registration need to be performed according to the following rules:
Call usbd_add_interface as many times as there are interfaces, with parameters filling in the relevant xxx_init_intf. If not supported, manually create an intf and fill it in
Call usbd_add_endpoint as many times as there are endpoints. When interrupts complete, the registered endpoint callback will be called.
Refer to the diagram below:
CORE
Endpoint Structure
The endpoint structure is mainly used to register interrupt completion callback functions for different endpoint addresses.
struct usbd_endpoint {
uint8_t ep_addr;
usbd_endpoint_callback ep_cb;
};
ep_addr Endpoint address (with direction)
ep_cb Endpoint completion interrupt callback function.
Note
To summarize in one sentence: in callback function is equivalent to DMA transmission completion interrupt callback function; out callback function is equivalent to DMA reception completion interrupt callback function
Interface Structure
The interface structure is mainly used to register requests other than standard device requests for different class devices, including class device requests, vendor device requests, and custom device requests, as well as related notification callback functions in the protocol stack.
struct usbd_interface {
usbd_request_handler class_interface_handler;
usbd_request_handler class_endpoint_handler;
usbd_request_handler vendor_handler;
usbd_notify_handler notify_handler;
const uint8_t *hid_report_descriptor;
uint32_t hid_report_descriptor_len;
uint8_t intf_num;
};
class_interface_handler class setup request callback function, recipient is interface
class_endpoint_handler class setup request callback function, recipient is endpoint
vendor_handler vendor setup request callback function
notify_handler interrupt flag, protocol stack related status callback function
hid_report_descriptor hid report descriptor
hid_report_descriptor_len hid report descriptor length
intf_num current interface offset
usbd_desc_register
usbd_desc_register is used to register USB descriptors. Descriptor types include: device descriptor, configuration descriptor (including configuration descriptor, interface descriptor, class descriptor, endpoint descriptor), string descriptor, device qualifier descriptor, other speed descriptor, BOS descriptor, WinUSB descriptor.
// Enable CONFIG_USBDEV_ADVANCE_DESC
void usbd_desc_register(uint8_t busid, const struct usb_descriptor *desc);
// Disable CONFIG_USBDEV_ADVANCE_DESC
void usbd_desc_register(uint8_t busid, const uint8_t *desc);
void usbd_msosv1_desc_register(uint8_t busid, struct usb_msosv1_descriptor *desc);
void usbd_msosv2_desc_register(uint8_t busid, struct usb_msosv2_descriptor *desc);
void usbd_bos_desc_register(uint8_t busid, struct usb_bos_descriptor *desc);
void usbd_webusb_desc_register(uint8_t busid, struct usb_webusb_descriptor *desc);
desc Descriptor handle
Note
Currently CONFIG_USBDEV_ADVANCE_DESC is enabled by default. If you need to use the old version API, please disable this macro. Starting from v1.6.0, only APIs with CONFIG_USBDEV_ADVANCE_DESC enabled are available
usbd_add_interface
usbd_add_interface adds an interface driver. The addition order must follow the interface order in the descriptor.
void usbd_add_interface(uint8_t busid, struct usbd_interface *intf);
busid USB bus ID
intf Interface driver handle, usually obtained from different class xxx_init_intf functions
usbd_add_endpoint
usbd_add_endpoint adds an endpoint interrupt completion callback function.
void usbd_add_endpoint(uint8_t busid, struct usbd_endpoint *ep);
busid USB bus ID
ep Endpoint handle
usbd_initialize
usbd_initialize is used to initialize USB device register configuration, USB clock, interrupts, etc. Note that this function must be called last after registering descriptor APIs. If using an OS, it must be executed within a thread.
int usbd_initialize(uint8_t busid, uintptr_t reg_base, usbd_event_handler_t event_handler);
busid USB bus ID
reg_base USB device register base address
event_handler Protocol stack interrupt or status callback function, event events
return Returns 0 for success, other values indicate failure
Event events include:
USBD_EVENT_ERROR, /** USB error reported by the controller */
USBD_EVENT_RESET, /** USB reset */
USBD_EVENT_SOF, /** Start of Frame received */
USBD_EVENT_CONNECTED, /** USB connected*/
USBD_EVENT_DISCONNECTED, /** USB disconnected */
USBD_EVENT_SUSPEND, /** USB connection suspended by the HOST */
USBD_EVENT_RESUME, /** USB connection resumed by the HOST */
/* USB DEVICE STATUS */
USBD_EVENT_CONFIGURED, /** USB configuration done */
USBD_EVENT_SET_INTERFACE, /** USB interface selected */
USBD_EVENT_SET_REMOTE_WAKEUP, /** USB set remote wakeup */
USBD_EVENT_CLR_REMOTE_WAKEUP, /** USB clear remote wakeup */
USBD_EVENT_INIT, /** USB init done when call usbd_initialize */
USBD_EVENT_DEINIT, /** USB deinit done when call usbd_deinitialize */
USBD_EVENT_UNKNOWN
Note
Most IPs do not support USBD_EVENT_CONNECTED and USBD_EVENT_DISCONNECTED events. Currently only HPM chips support them. For other chips, design your own VBUS detection circuit as an alternative
usbd_deinitialize
usbd_deinitialize is used to deinitialize USB device, turn off USB device clock, interrupts, etc.
int usbd_deinitialize(uint8_t busid);
busid USB bus ID
return Returns 0 for success, other values indicate failure
CDC ACM
usbd_cdc_acm_init_intf
usbd_cdc_acm_init_intf is used to initialize USB CDC ACM class interface and implement related functions for this interface.
cdc_acm_class_interface_request_handleris used to handle USB CDC ACM class Setup requests.cdc_notify_handleris used to handle other USB CDC interrupt callback functions.
struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf);
busid USB bus ID
return Interface handle
usbd_cdc_acm_set_line_coding
usbd_cdc_acm_set_line_coding is used to configure the serial port. If only using USB without serial port, this interface does not need to be implemented by the user and can use the default.
void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding);
busid USB bus ID
intf Control interface number
line_coding Serial port configuration
usbd_cdc_acm_get_line_coding
usbd_cdc_acm_get_line_coding is used to get serial port configuration. If only using USB without serial port, this interface does not need to be implemented by the user and can use the default.
void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding);
busid USB bus ID
intf Control interface number
line_coding Serial port configuration
usbd_cdc_acm_set_dtr
usbd_cdc_acm_set_dtr is used to control serial port DTR. If only using USB without serial port, this interface does not need to be implemented by the user and can use the default.
void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr);
busid USB bus ID
intf Control interface number
dtr dtr = 1 means pull low level, 0 means pull high level
usbd_cdc_acm_set_rts
usbd_cdc_acm_set_rts is used to control serial port RTS. If only using USB without serial port, this interface does not need to be implemented by the user and can use the default.
void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts);
busid USB bus ID
intf Control interface number
rts rts = 1 means pull low level, 0 means pull high level
CDC_ACM_DESCRIPTOR_INIT
CDC_ACM_DESCRIPTOR_INIT configures the default CDC ACM required descriptors and parameters for user convenience. Total length is CDC_ACM_DESCRIPTOR_LEN.
CDC_ACM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, str_idx);
bFirstInterface Indicates the offset of the first interface of this CDC ACM in all interfaces
int_ep Indicates interrupt endpoint address (with direction)
out_ep Indicates bulk out endpoint address (with direction)
in_ep Indicates bulk in endpoint address (with direction)
str_idx String ID corresponding to control interface
HID
usbd_hid_init_intf
usbd_hid_init_intf is used to initialize USB HID class interface and implement related functions for this interface:
hid_class_interface_request_handleris used to handle USB HID class Setup requests.hid_notify_handleris used to handle other USB HID interrupt callback functions.
struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t *desc, uint32_t desc_len);
busid USB bus ID
desc Report descriptor
desc_len Report descriptor length
MSC
usbd_msc_init_intf
usbd_msc_init_intf is used to initialize MSC class interface, implement related functions for this interface, and register endpoint callback functions. (Since MSC BOT protocol is fixed, user implementation is not needed, so endpoint callback functions naturally don’t need user implementation).
msc_storage_class_interface_request_handleris used to handle USB MSC Setup interrupt requests.msc_storage_notify_handleris used to implement other USB MSC interrupt callback functions.mass_storage_bulk_outis used to handle USB MSC endpoint out interrupts.mass_storage_bulk_inis used to handle USB MSC endpoint in interrupts.
struct usbd_interface *usbd_msc_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t out_ep, const uint8_t in_ep);
busid USB bus ID
out_ep out endpoint address
in_ep in endpoint address
usbd_msc_get_cap
usbd_msc_get_cap is used to get the LUN, number of sectors, and sector size of the storage device. Users must implement this function.
void usbd_msc_get_cap(uint8_t busid, uint8_t lun, uint32_t *block_num, uint16_t *block_size);
busid USB bus ID
lun Storage logical unit, currently unused, defaults to supporting one
block_num Number of storage sectors
block_size Storage sector size
usbd_msc_sector_read
usbd_msc_sector_read is used to read data from a storage device starting at a specific sector address. Users must implement this function.
int usbd_msc_sector_read(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length);
busid USB bus ID
lun Storage logical unit, currently unused, defaults to supporting one
sector Sector offset
buffer Pointer to store read data
length Read length
usbd_msc_sector_write
usbd_msc_sector_write is used to write data to a storage device starting at a specific sector. Users must implement this function.
int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length);
busid USB bus ID
lun Storage logical unit, currently unused, defaults to supporting one
sector Sector offset
buffer Write data pointer
length Write length
UAC
usbd_audio_init_intf
usbd_audio_init_intf is used to initialize USB Audio class interface and implement related functions for this interface:
audio_class_interface_request_handleris used to handle USB Audio Setup interface recipient interrupt requests.audio_class_endpoint_request_handleris used to handle USB Audio Setup endpoint recipient interrupt requests.audio_notify_handleris used to implement other USB Audio interrupt callback functions.
struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface *intf,
uint16_t uac_version,
struct audio_entity_info *table,
uint8_t num);
busid USB bus ID
intf Interface handle
uac_version Audio class version, UAC1.0 or UAC2.0
table Audio entity information table
num Audio entity information table length
usbd_audio_open
usbd_audio_open is used to start audio data transmission. Host sends start command callback function.
void usbd_audio_open(uint8_t intf);
intf Interface number to open
usbd_audio_close
usbd_audio_close is used to stop audio data transmission. Host sends stop command callback function.
void usbd_audio_close(uint8_t intf);
intf Interface number to close
usbd_audio_set_mute
usbd_audio_set_mute is used to set mute.
void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute);
busid USB bus ID
ep Endpoint to set mute
ch Channel to set mute
mute 1 means mute, 0 means opposite
usbd_audio_set_volume
usbd_audio_set_volume is used to set volume.
void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db);
busid USB bus ID
ep Endpoint to set volume
ch Channel to set volume
volume_db Volume to set in decibels, range -100dB ~ 0dB
usbd_audio_set_sampling_freq
usbd_audio_set_sampling_freq is used to set the sampling rate of the audio module on the device
void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq);
ep Endpoint to set sampling rate
sampling_freq Sampling rate to set
usbd_audio_get_sampling_freq_table
usbd_audio_get_sampling_freq_table is used to get the list of supported sampling rates. If the function is not implemented, the default sampling rate list is used. UAC2 only.
void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table);
ep Endpoint to get sampling rate
sampling_freq_table Sampling rate list address, format refers to default sampling rate list
UVC
usbd_video_init_intf
usbd_video_init_intf is used to initialize USB Video class interface and implement related functions for this interface:
video_class_interface_request_handleris used to handle USB Video Setup interrupt requests.video_notify_handleris used to implement other USB Video interrupt callback functions.
struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface *intf,
uint32_t dwFrameInterval,
uint32_t dwMaxVideoFrameSize,
uint32_t dwMaxPayloadTransferSize);
busid USB bus ID
intf Interface handle
dwFrameInterval Video frame interval, unit 100ns
dwMaxVideoFrameSize Maximum video frame size
dwMaxPayloadTransferSize Maximum payload transfer size
usbd_video_open
usbd_video_open is used to start video data transmission.
void usbd_video_open(uint8_t intf);
intf Interface number to open
usbd_video_close
usbd_video_close is used to stop video data transmission.
void usbd_video_open(uint8_t intf);
intf Interface number to close
usbd_video_stream_start_write
usbd_video_stream_start_write is used to start sending one frame of video data stream. Must be used together with usbd_video_stream_split_transfer.
int usbd_video_stream_start_write(uint8_t busid, uint8_t ep, uint8_t *ep_buf, uint8_t *stream_buf, uint32_t stream_len, bool do_copy);
busid USB bus ID
ep Video data endpoint address
ep_buf Video data endpoint transfer buffer
stream_buf One frame video data source buffer
stream_len One frame video data source buffer size
do_copy Whether to copy stream_buf data to ep_buf. This parameter is false only when stream_buf is in nocache area and DCACHE_ENABLE is not enabled
usbd_video_stream_split_transfer
usbd_video_stream_split_transfer is used to split video data stream transmission. Must be used together with usbd_video_stream_start_write.
int usbd_video_stream_split_transfer(uint8_t busid, uint8_t ep);
busid USB bus ID
ep Video data endpoint address
return Returns true when one frame data transmission is complete, false when data transmission is not complete