feat(examples/peripherals/usb/cherryusb/host/cherryusb_host): add CherryUSB host example

This commit is contained in:
LiPeng
2025-09-03 10:30:34 +08:00
parent 8ca3bba8f7
commit 53bd498b7b
9 changed files with 931 additions and 0 deletions
@@ -0,0 +1,44 @@
set(srcs "main.c")
if(CONFIG_CHERRYUSB_HOST_HID)
list(APPEND srcs "hid.c")
endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ACM OR CONFIG_CHERRYUSB_HOST_FTDI OR CONFIG_CHERRYUSB_HOST_CH34X OR
CONFIG_CHERRYUSB_HOST_CP210X OR CONFIG_CHERRYUSB_HOST_PL2303)
list(APPEND srcs "cdc_acm.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
)
if(CONFIG_CHERRYUSB_HOST_HID)
# Make sure the definitions in hid.c are linked correctly
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usbh_hid_run_real")
endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ACM)
# Make sure the definitions in hid.c are linked correctly
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usbh_cdc_acm_run_real")
endif()
if(CONFIG_CHERRYUSB_HOST_FTDI)
# Make sure the definitions in hid.c are linked correctly
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usbh_ftdi_run_real")
endif()
if(CONFIG_CHERRYUSB_HOST_CH34X)
# Make sure the definitions in hid.c are linked correctly
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usbh_ch34x_run_real")
endif()
if(CONFIG_CHERRYUSB_HOST_CP210X)
# Make sure the definitions in hid.c are linked correctly
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usbh_cp210x_run_real")
endif()
if(CONFIG_CHERRYUSB_HOST_PL2303)
# Make sure the definitions in hid.c are linked correctly
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usbh_pl2303_run_real")
endif()
@@ -0,0 +1,12 @@
menu "Example Configuration"
config EXAMPLE_HAL_USE_ESP32_S3_USB_OTG
bool "Use dev kit ESP32-S3-USB-OTG"
depends on IDF_TARGET_ESP32S3
default y
config EXAMPLE_CHERRYUSB_INIT_DEINIT_LOOP
bool "Perform init deinit of cherryusb stack in a loop"
default n
endmenu
@@ -0,0 +1,308 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "usbh_core.h"
#if CONFIG_CHERRYUSB_HOST_CDC_ACM
#include "usbh_cdc_acm.h"
#endif
#if CONFIG_CHERRYUSB_HOST_FTDI
#include "usbh_ftdi.h"
#endif
#if CONFIG_CHERRYUSB_HOST_CH34X
#include "usbh_ch34x.h"
#endif
#if CONFIG_CHERRYUSB_HOST_CP210X
#include "usbh_cp210x.h"
#endif
#if CONFIG_CHERRYUSB_HOST_PL2303
#include "usbh_pl2303.h"
#endif
static char *TAG = "CDC_ACM";
typedef enum {
SERIAL_TYPE_CDC_ACM = 0,
SERIAL_TYPE_FTDI,
SERIAL_TYPE_CH34X,
SERIAL_TYPE_CP210X,
SERIAL_TYPE_PL2303,
} serial_type_t;
typedef struct {
serial_type_t type;
struct usbh_hubport *hport;
struct usbh_urb *bulkout_urb;
struct usb_endpoint_descriptor *bulkout;
uint8_t *out_buffer;
struct usbh_urb *bulkin_urb;
struct usb_endpoint_descriptor *bulkin;
uint8_t *in_buffer;
} serial_t;
static void free_serial_buffer(serial_t *serial);
static esp_err_t serial_start_in(serial_t *serial);
#define alloc_serial_buffer(serial_class, serial_type, out_serial) \
{ \
out_serial = heap_caps_calloc(1, sizeof(serial_t), MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
if (out_serial) { \
out_serial->type = serial_type; \
out_serial->hport = serial_class->hport; \
out_serial->bulkout_urb = &serial_class->bulkout_urb; \
out_serial->bulkout = serial_class->bulkout; \
out_serial->out_buffer = heap_caps_aligned_alloc(CONFIG_USB_ALIGN_SIZE, out_serial->bulkout->wMaxPacketSize, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); \
out_serial->bulkin_urb = &serial_class->bulkin_urb; \
out_serial->bulkin = serial_class->bulkin; \
out_serial->in_buffer = heap_caps_aligned_alloc(CONFIG_USB_ALIGN_SIZE, out_serial->bulkin->wMaxPacketSize, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); \
if(out_serial->out_buffer == NULL || out_serial->in_buffer == NULL) { \
free_serial_buffer(out_serial); \
out_serial = NULL; \
} \
} \
}
static void free_serial_buffer(serial_t *serial)
{
if (serial == NULL) {
return;
}
if (serial->out_buffer) {
heap_caps_free(serial->out_buffer);
}
if (serial->in_buffer) {
heap_caps_free(serial->in_buffer);
}
heap_caps_free(serial);
}
static void serial_in_cb(void *arg, int nbytes)
{
// ESP_EARLY_LOGE(TAG, "in_cb: %d bytes", nbytes);
serial_t *serial = (serial_t *)arg;
uint8_t *data = serial->in_buffer;
if (nbytes < 0) {
return;
}
if (serial->type == SERIAL_TYPE_FTDI) {
if (nbytes <= 2) {
serial_start_in(serial);
return;
}
//FTDI Skip the first two bytes (header)
data += 2;
nbytes -= 2;
}
for (size_t i = 0; i < nbytes; i++) {
if (data[i] >= 0x20 && data[i] <= 0x7e) {
esp_rom_printf("%c", data[i]);
} else {
esp_rom_printf("[%02x]", data[i]);
}
}
esp_rom_printf("\r");
serial_start_in(serial);
}
static esp_err_t serial_start_in(serial_t *serial)
{
int ret;
usbh_bulk_urb_fill(serial->bulkin_urb, serial->hport, serial->bulkin, serial->in_buffer, serial->bulkin->wMaxPacketSize, 0, serial_in_cb, serial);
ret = usbh_submit_urb(serial->bulkin_urb);
if (ret != 0) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t serial_out(serial_t *serial, uint8_t *data, size_t len, uint32_t timeout)
{
int ret;
if (len > serial->bulkout->wMaxPacketSize) {
return ESP_ERR_INVALID_SIZE;
}
memcpy(serial->out_buffer, data, len);
usbh_bulk_urb_fill(serial->bulkout_urb, serial->hport, serial->bulkout, serial->out_buffer, len, timeout, NULL, NULL);
ret = usbh_submit_urb(serial->bulkout_urb);
if (ret != 0) {
return ESP_FAIL;
}
return ESP_OK;
}
#if CONFIG_CHERRYUSB_HOST_CDC_ACM
void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
{
serial_t *serial;
alloc_serial_buffer(cdc_acm_class, SERIAL_TYPE_CDC_ACM, serial);
if (serial == NULL) {
ESP_LOGW(TAG, "Malloc failed");
return;
}
cdc_acm_class->user_data = serial;
struct cdc_line_coding linecoding = {
.dwDTERate = 115200,
.bDataBits = 8,
.bParityType = 0,
.bCharFormat = 0,
};
usbh_cdc_acm_set_line_coding(cdc_acm_class, &linecoding);
serial_start_in(serial);
const char data[] = "CDC: Hello, world!\r\n";
serial_out(serial, (uint8_t *)data, sizeof(data), 1000);
}
void usbh_cdc_acm_run_real(struct usbh_cdc_acm *cdc_acm_class) __attribute__((alias("usbh_cdc_acm_run")));
void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
{
free_serial_buffer(cdc_acm_class->user_data);
}
#endif
#if CONFIG_CHERRYUSB_HOST_FTDI
void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
{
serial_t *serial;
alloc_serial_buffer(ftdi_class, SERIAL_TYPE_FTDI, serial);
if (serial == NULL) {
ESP_LOGW(TAG, "Malloc failed");
return;
}
ftdi_class->user_data = serial;
struct cdc_line_coding linecoding = {
.dwDTERate = 115200,
.bDataBits = 8,
.bParityType = 0,
.bCharFormat = 0,
};
usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
serial_start_in(serial);
const char data[] = "FTDI: Hello, world!\r\n";
serial_out(serial, (uint8_t *)data, sizeof(data), 1000);
}
void usbh_ftdi_run_real(struct usbh_ftdi *cdc_acm_class) __attribute__((alias("usbh_ftdi_run")));
void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
{
free_serial_buffer(ftdi_class->user_data);
}
#endif
#if CONFIG_CHERRYUSB_HOST_CH34X
void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
{
serial_t *serial;
alloc_serial_buffer(ch34x_class, SERIAL_TYPE_CH34X, serial);
if (serial == NULL) {
ESP_LOGW(TAG, "Malloc failed");
return;
}
ch34x_class->user_data = serial;
struct cdc_line_coding linecoding = {
.dwDTERate = 115200,
.bDataBits = 8,
.bParityType = 0,
.bCharFormat = 0,
};
usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
serial_start_in(serial);
const char data[] = "CH34x: Hello, world!\r\n";
serial_out(serial, (uint8_t *)data, sizeof(data), 1000);
}
void usbh_ch34x_run_real(struct usbh_ch34x *cdc_acm_class) __attribute__((alias("usbh_ch34x_run")));
void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
{
free_serial_buffer(ch34x_class->user_data);
}
#endif
#if CONFIG_CHERRYUSB_HOST_CP210X
void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
{
serial_t *serial;
alloc_serial_buffer(cp210x_class, SERIAL_TYPE_CP210X, serial);
if (serial == NULL) {
ESP_LOGW(TAG, "Malloc failed");
return;
}
cp210x_class->user_data = serial;
struct cdc_line_coding linecoding = {
.dwDTERate = 115200,
.bDataBits = 8,
.bParityType = 0,
.bCharFormat = 0,
};
usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
serial_start_in(serial);
const char data[] = "CP201x: Hello, world!\r\n";
serial_out(serial, (uint8_t *)data, sizeof(data), 1000);
}
void usbh_cp210x_run_real(struct usbh_cp210x *cdc_acm_class) __attribute__((alias("usbh_cp210x_run")));
void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
{
free_serial_buffer(cp210x_class->user_data);
}
#endif
#if CONFIG_CHERRYUSB_HOST_PL2303
void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
{
serial_t *serial;
alloc_serial_buffer(pl2303_class, SERIAL_TYPE_PL2303, serial);
if (serial == NULL) {
ESP_LOGW(TAG, "Malloc failed");
return;
}
pl2303_class->user_data = serial;
struct cdc_line_coding linecoding = {
.dwDTERate = 115200,
.bDataBits = 8,
.bParityType = 0,
.bCharFormat = 0,
};
usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
serial_start_in(serial);
const char data[] = "PL2303: Hello, world!\r\n";
serial_out(serial, (uint8_t *)data, sizeof(data), 1000);
}
void usbh_pl2303_run_real(struct usbh_pl2303 *cdc_acm_class) __attribute__((alias("usbh_pl2303_run")));
void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
{
free_serial_buffer(pl2303_class->user_data);
}
#endif
@@ -0,0 +1,413 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "usbh_core.h"
#include "usbh_hid.h"
static char *TAG = "HID";
typedef struct {
bool is_active;
esp_timer_handle_t timer;
uint8_t *buffer;
} hid_int_in_t;
/**
* @brief Key event
*/
typedef struct {
enum key_state {
KEY_STATE_PRESSED = 0x00,
KEY_STATE_RELEASED = 0x01
} state;
uint8_t modifier;
uint8_t key_code;
} key_event_t;
/* Main char symbol for ENTER key */
#define KEYBOARD_ENTER_MAIN_CHAR '\r'
/* When set to 1 pressing ENTER will be extending with LineFeed during serial debug output */
#define KEYBOARD_ENTER_LF_EXTEND 1
/**
* @brief Scancode to ascii table
*/
const uint8_t keycode2ascii [57][2] = {
{0, 0}, /* HID_KEY_NO_PRESS */
{0, 0}, /* HID_KEY_ROLLOVER */
{0, 0}, /* HID_KEY_POST_FAIL */
{0, 0}, /* HID_KBD_USAGE_ERRUNDEF */
{'a', 'A'}, /* HID_KEY_A */
{'b', 'B'}, /* HID_KEY_B */
{'c', 'C'}, /* HID_KEY_C */
{'d', 'D'}, /* HID_KEY_D */
{'e', 'E'}, /* HID_KEY_E */
{'f', 'F'}, /* HID_KEY_F */
{'g', 'G'}, /* HID_KEY_G */
{'h', 'H'}, /* HID_KEY_H */
{'i', 'I'}, /* HID_KEY_I */
{'j', 'J'}, /* HID_KEY_J */
{'k', 'K'}, /* HID_KEY_K */
{'l', 'L'}, /* HID_KEY_L */
{'m', 'M'}, /* HID_KEY_M */
{'n', 'N'}, /* HID_KEY_N */
{'o', 'O'}, /* HID_KEY_O */
{'p', 'P'}, /* HID_KEY_P */
{'q', 'Q'}, /* HID_KEY_Q */
{'r', 'R'}, /* HID_KEY_R */
{'s', 'S'}, /* HID_KEY_S */
{'t', 'T'}, /* HID_KEY_T */
{'u', 'U'}, /* HID_KEY_U */
{'v', 'V'}, /* HID_KEY_V */
{'w', 'W'}, /* HID_KEY_W */
{'x', 'X'}, /* HID_KEY_X */
{'y', 'Y'}, /* HID_KEY_Y */
{'z', 'Z'}, /* HID_KEY_Z */
{'1', '!'}, /* HID_KEY_1 */
{'2', '@'}, /* HID_KEY_2 */
{'3', '#'}, /* HID_KEY_3 */
{'4', '$'}, /* HID_KEY_4 */
{'5', '%'}, /* HID_KEY_5 */
{'6', '^'}, /* HID_KEY_6 */
{'7', '&'}, /* HID_KEY_7 */
{'8', '*'}, /* HID_KEY_8 */
{'9', '('}, /* HID_KEY_9 */
{'0', ')'}, /* HID_KEY_0 */
{KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER */
{0, 0}, /* HID_KEY_ESC */
{'\b', 0}, /* HID_KEY_DEL */
{0, 0}, /* HID_KEY_TAB */
{' ', ' '}, /* HID_KEY_SPACE */
{'-', '_'}, /* HID_KEY_MINUS */
{'=', '+'}, /* HID_KEY_EQUAL */
{'[', '{'}, /* HID_KEY_OPEN_BRACKET */
{']', '}'}, /* HID_KEY_CLOSE_BRACKET */
{'\\', '|'}, /* HID_KEY_BACK_SLASH */
{'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
{';', ':'}, /* HID_KEY_COLON */
{'\'', '"'}, /* HID_KEY_QUOTE */
{'`', '~'}, /* HID_KEY_TILDE */
{',', '<'}, /* HID_KEY_LESS */
{'.', '>'}, /* HID_KEY_GREATER */
{'/', '?'} /* HID_KBD_USAGE_QUESTION */
};
/**
* @brief Makes new line depending on report output protocol type
*
* @param[in] proto Current protocol to output
*/
static void hid_print_new_device_report_header(int proto)
{
static int prev_proto_output = -1;
if (prev_proto_output != proto) {
prev_proto_output = proto;
esp_rom_printf("\r\n");
if (proto == HID_PROTOCOL_MOUSE) {
esp_rom_printf("Mouse\r\n");
} else if (proto == HID_PROTOCOL_KEYBOARD) {
esp_rom_printf("Keyboard\r\n");
} else {
esp_rom_printf("Generic\r\n");
}
}
}
/**
* @brief HID Keyboard modifier verification for capitalization application (right or left shift)
*
* @param[in] modifier
* @return true Modifier was pressed (left or right shift)
* @return false Modifier was not pressed (left or right shift)
*
*/
static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier)
{
if (((modifier & HID_MODIFIER_LSHIFT) == HID_MODIFIER_LSHIFT) ||
((modifier & HID_MODIFIER_RSHIFT) == HID_MODIFIER_RSHIFT)) {
return true;
}
return false;
}
/**
* @brief HID Keyboard get char symbol from key code
*
* @param[in] modifier Keyboard modifier data
* @param[in] key_code Keyboard key code
* @param[in] key_char Pointer to key char data
*
* @return true Key scancode converted successfully
* @return false Key scancode unknown
*/
static inline bool hid_keyboard_get_char(uint8_t modifier,
uint8_t key_code,
unsigned char *key_char)
{
uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0;
if ((key_code >= HID_KBD_USAGE_A) && (key_code <= HID_KBD_USAGE_QUESTION)) {
*key_char = keycode2ascii[key_code][mod];
} else {
// All other key pressed
return false;
}
return true;
}
/**
* @brief HID Keyboard print char symbol
*
* @param[in] key_char Keyboard char to stdout
*/
static inline void hid_keyboard_print_char(unsigned int key_char)
{
if (!!key_char) {
esp_rom_printf("%c", key_char);
#if (KEYBOARD_ENTER_LF_EXTEND)
if (KEYBOARD_ENTER_MAIN_CHAR == key_char) {
esp_rom_printf("\n");
}
#endif // KEYBOARD_ENTER_LF_EXTEND
}
}
/**
* @brief Key Event. Key event with the key code, state and modifier.
*
* @param[in] key_event Pointer to Key Event structure
*
*/
static void key_event_callback(key_event_t *key_event)
{
unsigned char key_char;
hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD);
if (KEY_STATE_PRESSED == key_event->state) {
if (hid_keyboard_get_char(key_event->modifier,
key_event->key_code, &key_char)) {
hid_keyboard_print_char(key_char);
}
}
}
/**
* @brief Key buffer scan code search.
*
* @param[in] src Pointer to source buffer where to search
* @param[in] key Key scancode to search
* @param[in] length Size of the source buffer
*/
static inline bool key_found(const uint8_t *const src,
uint8_t key,
unsigned int length)
{
for (unsigned int i = 0; i < length; i++) {
if (src[i] == key) {
return true;
}
}
return false;
}
static void usbh_hid_keyboard_report_callback(void *arg, int nbytes)
{
struct usb_hid_kbd_report *kb_report = (struct usb_hid_kbd_report *)arg;
if (nbytes < sizeof(struct usb_hid_kbd_report)) {
return;
}
static uint8_t prev_keys[sizeof(kb_report->key)] = { 0 };
key_event_t key_event;
for (int i = 0; i < sizeof(kb_report->key); i++) {
// key has been released verification
if (prev_keys[i] > HID_KBD_USAGE_ERRUNDEF &&
!key_found(kb_report->key, prev_keys[i], sizeof(kb_report->key))) {
key_event.key_code = prev_keys[i];
key_event.modifier = 0;
key_event.state = KEY_STATE_RELEASED;
key_event_callback(&key_event);
}
// key has been pressed verification
if (kb_report->key[i] > HID_KBD_USAGE_ERRUNDEF &&
!key_found(prev_keys, kb_report->key[i], sizeof(kb_report->key))) {
key_event.key_code = kb_report->key[i];
key_event.modifier = kb_report->modifier;
key_event.state = KEY_STATE_PRESSED;
key_event_callback(&key_event);
}
}
memcpy(prev_keys, &kb_report->key, sizeof(kb_report->key));
}
static void usbh_hid_mouse_report_callback(void *arg, int nbytes)
{
struct usb_hid_mouse_report *mouse_report = (struct usb_hid_mouse_report *)arg;
// At least 3 bytes are reported, followed by optional data
if (nbytes < 3) {
return;
}
static int x_pos = 0;
static int y_pos = 0;
// Calculate absolute position from displacement
x_pos += (int8_t)mouse_report->xdisp;
y_pos += (int8_t)mouse_report->ydisp;
hid_print_new_device_report_header(HID_PROTOCOL_MOUSE);
esp_rom_printf("X: %06d\tY: %06d\t|%c|%c|\n",
x_pos, y_pos,
((mouse_report->buttons & HID_MOUSE_INPUT_BUTTON_LEFT) ? 'o' : ' '),
((mouse_report->buttons & HID_MOUSE_INPUT_BUTTON_RIGHT) ? 'o' : ' '));
}
static void usbh_hid_generic_report_callback(void *arg, int nbytes)
{
uint8_t *data = arg;
hid_print_new_device_report_header(HID_PROTOCOL_NONE);
for (size_t i = 0; i < nbytes; i++) {
esp_rom_printf("%02x ", data[i]);
}
esp_rom_printf("\r");
}
static void usbh_hid_callback(void *arg, int nbytes)
{
struct usbh_hid *hid_class = (struct usbh_hid *)arg;
hid_int_in_t *hid_intin = (hid_int_in_t *)hid_class->user_data;
hid_intin->is_active = false;
if (nbytes <= 0) {
return;
}
uint8_t sub_class = hid_class->hport->config.intf[hid_class->intf].altsetting[0].intf_desc.bInterfaceSubClass;
uint8_t protocol = hid_class->hport->config.intf[hid_class->intf].altsetting[0].intf_desc.bInterfaceProtocol;
usbh_complete_callback_t complete_cb = usbh_hid_generic_report_callback;
if (sub_class == HID_SUBCLASS_BOOTIF) {
if (protocol == HID_PROTOCOL_KEYBOARD) {
complete_cb = usbh_hid_keyboard_report_callback;
} else if (protocol == HID_PROTOCOL_MOUSE) {
complete_cb = usbh_hid_mouse_report_callback;
}
}
complete_cb(hid_intin->buffer, nbytes);
}
static void intin_timer_cb(void *arg)
{
int ret;
struct usbh_hid *hid_class = (struct usbh_hid *)arg;
hid_int_in_t *hid_intin = (hid_int_in_t *)hid_class->user_data;
if (hid_intin->is_active) {
return;
}
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_intin->buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_callback, hid_class);
hid_intin->is_active = true;
ret = usbh_submit_urb(&hid_class->intin_urb);
if (ret != 0) {
if (ret == -USB_ERR_NOTCONN) {
esp_timer_stop(hid_intin->timer);
return;
}
hid_intin->is_active = false;
ESP_EARLY_LOGE(TAG, "usbh_submit_urb failed");
}
}
void usbh_hid_run(struct usbh_hid *hid_class)
{
int ret;
esp_err_t err;
uint8_t sub_class = hid_class->hport->config.intf[hid_class->intf].altsetting[0].intf_desc.bInterfaceSubClass;
uint8_t protocol = hid_class->hport->config.intf[hid_class->intf].altsetting[0].intf_desc.bInterfaceProtocol;
ESP_LOGI(TAG, "intf %u, SubClass %u, Protocol %u", hid_class->intf, sub_class, protocol);
if (sub_class == HID_SUBCLASS_BOOTIF) {
ret = usbh_hid_set_protocol(hid_class, HID_PROTOCOL_BOOT);
if (ret < 0) {
return;
}
}
if (hid_class->intin == NULL) {
ESP_LOGW(TAG, "no intin ep desc");
return;
}
hid_int_in_t *hid_intin = heap_caps_calloc(1, sizeof(hid_int_in_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
if (hid_intin == NULL) {
ESP_LOGW(TAG, "Malloc failed");
return;
}
hid_intin->buffer = heap_caps_aligned_alloc(CONFIG_USB_ALIGN_SIZE, hid_class->intin->wMaxPacketSize, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
if (hid_intin->buffer == NULL) {
ESP_LOGW(TAG, "Malloc failed");
goto error;
}
hid_intin->is_active = false;
esp_timer_create_args_t timer_cfg = {
.callback = intin_timer_cb,
.arg = hid_class,
#if CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
.dispatch_method = ESP_TIMER_ISR,
#else
.dispatch_method = ESP_TIMER_TASK,
#endif
.name = "intin timer",
.skip_unhandled_events = true,
};
err = esp_timer_create(&timer_cfg, &hid_intin->timer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "timer create failed");
goto error;
}
hid_class->user_data = hid_intin;
//周期间隔,低速、全速单位为1ms,高速和超高速是125us
ret = USBH_GET_URB_INTERVAL(hid_class->intin->bInterval, hid_class->hport->speed);
ret = (hid_class->hport->speed < USB_SPEED_HIGH) ? (ret * 1000) : (ret * 125);
esp_timer_start_periodic(hid_intin->timer, ret);
return;
error:
if (hid_intin->buffer) {
heap_caps_free(hid_intin->buffer);
}
heap_caps_free(hid_intin);
}
void usbh_hid_run_real(struct usbh_hid *) __attribute__((alias("usbh_hid_run")));
void usbh_hid_stop(struct usbh_hid *hid_class)
{
hid_int_in_t *hid_intin = (hid_int_in_t *)hid_class->user_data;
if (hid_intin) {
esp_timer_stop(hid_intin->timer);
esp_timer_delete(hid_intin->timer);
heap_caps_free(hid_intin->buffer);
heap_caps_free(hid_intin);
}
ESP_LOGW(TAG, "hid stop");
}
@@ -0,0 +1,3 @@
## IDF Component Manager Manifest File
dependencies:
cherry-embedded/cherryusb: ~=1.5.0~4
@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#if EXAMPLE_HAL_USE_ESP32_S3_USB_OTG
#include "driver/gpio.h"
#define BOOST_EN 13
#define DEV_VBUS_EN 12
#define LIMIT_EN 17
#define USB_SEL 18
#endif
#include "usbh_core.h"
#include "usbh_hid.h"
static char *TAG = "HOST";
void app_main(void)
{
#if EXAMPLE_HAL_USE_ESP32_S3_USB_OTG
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = 0,
.pull_up_en = 0,
.pin_bit_mask = (1ULL << BOOST_EN) | (1ULL << DEV_VBUS_EN) | (1ULL << LIMIT_EN) | (1ULL << USB_SEL),
};
gpio_config(&io_conf);
gpio_set_level(BOOST_EN, 0);
gpio_set_level(DEV_VBUS_EN, 1);
gpio_set_level(LIMIT_EN, 1);
gpio_set_level(USB_SEL, 1);
#endif
#if EXAMPLE_CHERRYUSB_INIT_DEINIT_LOOP
reinit:
#endif
usbh_initialize(0, ESP_USBH_BASE);
ESP_LOGI(TAG, "Init usb");
#if EXAMPLE_CHERRYUSB_INIT_DEINIT_LOOP
for (int i = 10; i >= 0; i--) {
ESP_LOGW(TAG, "Deinit usb after %d seconds...", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
ESP_LOGW(TAG, "Deinit usb");
usbh_deinitialize(0);
goto reinit;
#endif
}