Merge branch 'feature/usb/host/hid_example_update_v5.1' into 'release/v5.1'

USB Host (HID): HID Host example update (backport v5.1)

See merge request espressif/esp-idf!24532
This commit is contained in:
morris 2023-07-10 13:31:53 +08:00
commit fed2e21594
18 changed files with 227 additions and 2639 deletions

View File

@ -1,3 +0,0 @@
idf_component_register(SRCS "hid_host.c"
PRIV_REQUIRES usb
INCLUDE_DIRS "include")

File diff suppressed because it is too large Load Diff

View File

@ -1,147 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief HID Subclass
*
* @see 4.2 Subclass, p.8 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_SUBCLASS_NO_SUBCLASS = 0x00,
HID_SUBCLASS_BOOT_INTERFACE = 0x01
} __attribute__((packed)) hid_subclass_t;
/**
* @brief HID Protocols
*
* @see 4.3 Protocols, p.9 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_PROTOCOL_NONE = 0x00,
HID_PROTOCOL_KEYBOARD = 0x01,
HID_PROTOCOL_MOUSE = 0x02,
HID_PROTOCOL_MAX
} __attribute__((packed)) hid_protocol_t;
/**
* @brief HID Descriptor
*
* @see 6.2.1 HID Descriptor, p.22 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef struct {
uint8_t bLength; // Numeric expression that is the total size of the HID descriptor
uint8_t bDescriptorType; // Constant name specifying type of HID descriptor
uint16_t bcdHID; // Numeric expression identifying the HIDClass Specification release
int8_t bCountryCode; // Numeric expression identifying country code of the localized hardware
uint8_t bNumDescriptors; // Numeric expression specifying the number of class descriptors (always at least one i.e. Report descriptor.)
uint8_t bReportDescriptorType; // Constant name identifying type of class descriptor. See Section 7.1.2: Set_Descriptor Request for a table of class descriptor constants
uint16_t wReportDescriptorLength; // Numeric expression that is the total size of the Report descriptor
// Optional descriptors may follow further
} __attribute__((packed)) hid_descriptor_t;
/**
* @brief HID Country Codes
*
* @see 6.2.1 HID Descriptor, p.23 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_COUNTRY_CODE_NOT_SUPPORTED = 0x00,
HID_COUNTRY_CODE_ARABIC = 0x01,
HID_COUNTRY_CODE_BELGIAN = 0x02,
HID_COUNTRY_CODE_CANADIAN_BILINGUAL = 0x03,
HID_COUNTRY_CODE_CANADIAN_FRENCH = 0x04,
HID_COUNTRY_CODE_CZECH = 0x05,
HID_COUNTRY_CODE_DANISH = 0x06,
HID_COUNTRY_CODE_FINNISH = 0x07,
HID_COUNTRY_CODE_FRENCH = 0x08,
HID_COUNTRY_CODE_GERMAN = 0x09,
HID_COUNTRY_CODE_GREEK = 0x0A,
HID_COUNTRY_CODE_HEBREW = 0x0B,
HID_COUNTRY_CODE_HUNGARY = 0x0C,
HID_COUNTRY_CODE_ISO = 0x0D,
HID_COUNTRY_CODE_ITALIAN = 0x0E,
HID_COUNTRY_CODE_JAPAN = 0x0F,
HID_COUNTRY_CODE_KOREAN = 0x10,
HID_COUNTRY_CODE_LATIN_AMERICAN = 0x11,
HID_COUNTRY_CODE_NETHERLANDS = 0x12,
HID_COUNTRY_CODE_NORWEGIAN = 0x13,
HID_COUNTRY_CODE_PERSIAN = 0x14,
HID_COUNTRY_CODE_POLAND = 0x15,
HID_COUNTRY_CODE_PORTUGUESE = 0x16,
HID_COUNTRY_CODE_RUSSIA = 0x17,
HID_COUNTRY_CODE_SLOVAKIA = 0x18,
HID_COUNTRY_CODE_SPANISH = 0x19,
HID_COUNTRY_CODE_SWEDISH = 0x1A,
HID_COUNTRY_CODE_SWISS_F = 0x1B,
HID_COUNTRY_CODE_SWISS_G = 0x1C,
HID_COUNTRY_CODE_SWITZERLAND = 0x1D,
HID_COUNTRY_CODE_TAIWAN = 0x1E,
HID_COUNTRY_CODE_TURKISH_Q = 0x1F,
HID_COUNTRY_CODE_UK = 0x20,
HID_COUNTRY_CODE_US = 0x21,
HID_COUNTRY_CODE_YUGOSLAVIA = 0x22,
HID_COUNTRY_CODE_TURKISH_F = 0x23
} __attribute__((packed)) hid_country_code_t;
/**
* @brief HID Class Descriptor Types
*
* @see 7.1, p.49 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_CLASS_DESCRIPTOR_TYPE_HID = 0x21,
HID_CLASS_DESCRIPTOR_TYPE_REPORT = 0x22,
HID_CLASS_DESCRIPTOR_TYPE_PHYSICAL = 0x23
} __attribute__((packed)) hid_class_descritpor_type_t;
/**
* @brief HID Class-Specific Requests
*
* @see 7.2, p.50 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_CLASS_SPECIFIC_REQ_GET_REPORT = 0x01,
HID_CLASS_SPECIFIC_REQ_GET_IDLE = 0x02,
HID_CLASS_SPECIFIC_REQ_GET_PROTOCOL = 0x03,
HID_CLASS_SPECIFIC_REQ_SET_REPORT = 0x09,
HID_CLASS_SPECIFIC_REQ_SET_IDLE = 0x0A,
HID_CLASS_SPECIFIC_REQ_SET_PROTOCOL = 0x0B
} __attribute__((packed)) hid_class_specific_req_t;
/**
* @brief HID Report Types
*
* @see 7.2.1, p.51 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_REPORT_TYPE_INPUT = 0x01,
HID_REPORT_TYPE_OUTPUT = 0x02,
HID_REPORT_TYPE_FEATURE = 0x03,
} __attribute__((packed)) hid_report_type_t;
/**
* @brief HID Report protocol
*
* @see 7.2.5/7.2.6, p.54 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_REPORT_PROTOCOL_BOOT = 0x00,
HID_REPORT_PROTOCOL_REPORT = 0x01,
HID_REPORT_PROTOCOL_MAX
} __attribute__((packed)) hid_report_protocol_t;
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -1,174 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <wchar.h>
#include <stdint.h>
#include "esp_err.h"
#include <freertos/FreeRTOS.h>
#include "hid.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct hid_host_device *hid_host_device_handle_t; /**< Handle to a HID */
typedef struct hid_interface *hid_host_interface_handle_t; /**< Handle to a particular HID interface */
/**
* @brief HID Interface input report callback
*
* @param[in] data Pointer to buffer containing input report data
* @param[in] length Data length
*/
typedef void (*hid_input_report_cb_t)(const uint8_t *const data, const int length);
/**
* @brief USB HID event containing event type and associated device handle
*/
typedef struct {
enum {
HID_DEVICE_CONNECTED, /**< HID device has been connected to the system.*/
HID_DEVICE_DISCONNECTED, /**< HID device has been disconnected from the system.*/
} event;
union {
uint8_t address; /**< Address of connected HID device.*/
hid_host_device_handle_t handle; /**< HID device handle to disconnected device.*/
} device;
} hid_host_event_t;
/**
* @brief USB HID interface event for application logic
*/
typedef enum {
HID_DEVICE_INTERFACE_INIT = 0x00, /**< Interface available for application logic */
HID_DEVICE_INTERFACE_CLAIM, /**< Interface was claimed */
HID_DEVICE_INTERFACE_RELEASE, /**< Interface was released */
HID_DEVICE_INTERFACE_TRANSFER_ERROR /**< Interface transfer error occurred */
} hid_interface_event_t;
/**
* @brief USB HID Host interface event containing event type and associated parameters
*/
typedef struct {
hid_interface_event_t event; /**< USB HID Interface event */
struct interface_event_param {
uint8_t dev_addr; /**< USB Device address */
uint8_t num; /**< USB Interface number */
hid_protocol_t proto; /**< USB HID Interface protocol */
} interface;
} hid_host_interface_event_t;
/**
* @brief USB HID host event callback.
*
* @param[in] event mass storage event
*/
typedef void (*hid_host_event_cb_t)(const hid_host_event_t *event, void *arg);
typedef void (*hid_host_interface_event_cb_t)(const hid_host_interface_event_t *event, void *arg);
/**
* @brief HID configuration structure.
*/
typedef struct {
bool create_background_task; /**< When set to true, background task handling USB events is created.
Otherwise user has to periodically call hid_host_handle_events function */
size_t task_priority; /**< Task priority of created background task */
size_t stack_size; /**< Stack size of created background task */
BaseType_t core_id; /**< Select core on which background task will run or tskNO_AFFINITY */
hid_host_event_cb_t callback; /**< Callback invoked when HID event occurs. Must not be NULL. */
void *callback_arg; /**< User provided argument passed to callback */
} hid_host_driver_config_t;
typedef struct {
uint8_t dev_addr;
hid_host_interface_event_cb_t iface_event_cb; /**< Callback invoked when HID event occurs. Must not be NULL. */
void *iface_event_arg; /**< User provided argument passed to callback */
} hid_host_device_config_t;
typedef struct {
hid_protocol_t proto;
hid_input_report_cb_t callback;
} hid_host_interface_config_t;
/**
* @brief Install USB Host HID Class driver
*
* @param[in] config configuration structure HID to create
* @return esp_err_r
*/
esp_err_t hid_host_install(const hid_host_driver_config_t *config);
/**
* @brief Uninstall HID Class driver
* @return esp_err_t
*/
esp_err_t hid_host_uninstall(void);
/**
* @brief HID Host USB event handler
*
* If HID Host install was made with create_background_task=false configuration,
* application needs to handle USB Host events itself.
* Do not used id HID host install was made with create_background_task=true configuration
*
* @param[in] arg Pointer to handler argument
* @return none
*/
void hid_host_event_handler_task(void *arg);
/**
* @brief Initialization of HID device.
*
* @param[in] hid_host_dev_config Pointer to HID Host device configuration structure
* @param[out] hid_device_handle Pointer to HID device handle
* @return esp_err_t
*/
esp_err_t hid_host_install_device(const hid_host_device_config_t *hid_host_dev_config,
hid_host_device_handle_t *hid_device_handle);
/**
* @brief Deinitialization of HID device.
*
* @param[in] hid_device_handle Device handle obtained from hid_host_install_device function
* @return esp_err_t
*/
esp_err_t hid_host_uninstall_device(hid_host_device_handle_t hid_device_handle);
/**
* @brief Print configuration descriptor.
*
* @param[in] hid_device_handle Device handle obtained from hid_host_install_device function
* @return esp_err_t
*/
esp_err_t hid_host_print_descriptors(hid_host_device_handle_t hid_device_handle);
/**
* @brief USB HID Interface claim
*
* @param[in] iface_config Pointer to Interface configuration structure
* @param[out] iface_handle Pointer to Interface handle
* @param[in] esp_err_t
*/
esp_err_t hid_host_claim_interface(const hid_host_interface_config_t *iface_config,
hid_host_interface_handle_t *iface_handle);
/**
* @brief USB HID Interface release
*
* @param[in] iface_handle Interface handle obtained from hid_host_claim_interface function
* @param[in] esp_err_t
*/
esp_err_t hid_host_release_interface(hid_host_interface_handle_t iface_handle);
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -1,292 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------------------ HID usage keys ---------------------------------------------------------------
/**
* @brief HID Keys
*
*/
typedef enum {
HID_KEY_NO_PRESS = 0x00,
HID_KEY_ROLLOVER = 0x01,
HID_KEY_POST_FAIL = 0x02,
HID_KEY_ERROR_UNDEFINED = 0x03,
HID_KEY_A = 0x04,
HID_KEY_B = 0x05,
HID_KEY_C = 0x06,
HID_KEY_D = 0x07,
HID_KEY_E = 0x08,
HID_KEY_F = 0x09,
HID_KEY_G = 0x0A,
HID_KEY_H = 0x0B,
HID_KEY_I = 0x0C,
HID_KEY_J = 0x0D,
HID_KEY_K = 0x0E,
HID_KEY_L = 0x0F,
HID_KEY_M = 0x10,
HID_KEY_N = 0x11,
HID_KEY_O = 0x12,
HID_KEY_P = 0x13,
HID_KEY_Q = 0x14,
HID_KEY_R = 0x15,
HID_KEY_S = 0x16,
HID_KEY_T = 0x17,
HID_KEY_U = 0x18,
HID_KEY_V = 0x19,
HID_KEY_W = 0x1A,
HID_KEY_X = 0x1B,
HID_KEY_Y = 0x1C,
HID_KEY_Z = 0x1D,
HID_KEY_1 = 0x1E,
HID_KEY_2 = 0x1F,
HID_KEY_3 = 0x20,
HID_KEY_4 = 0x21,
HID_KEY_5 = 0x22,
HID_KEY_6 = 0x23,
HID_KEY_7 = 0x24,
HID_KEY_8 = 0x25,
HID_KEY_9 = 0x26,
HID_KEY_0 = 0x27,
HID_KEY_ENTER = 0x28,
HID_KEY_ESC = 0x29,
HID_KEY_DEL = 0x2A,
HID_KEY_TAB = 0x2B,
HID_KEY_SPACE = 0x2C,
HID_KEY_MINUS = 0x2D,
HID_KEY_EQUAL = 0x2E,
HID_KEY_OPEN_BRACKET = 0x2F,
HID_KEY_CLOSE_BRACKET = 0x30,
HID_KEY_BACK_SLASH = 0x31,
HID_KEY_SHARP = 0x32,
HID_KEY_COLON = 0x33,
HID_KEY_QUOTE = 0x34,
HID_KEY_TILDE = 0x35,
HID_KEY_LESS = 0x36,
HID_KEY_GREATER = 0x37,
HID_KEY_SLASH = 0x38,
HID_KEY_CAPS_LOCK = 0x39,
HID_KEY_F1 = 0x3A,
HID_KEY_F2 = 0x3B,
HID_KEY_F3 = 0x3C,
HID_KEY_F4 = 0x3D,
HID_KEY_F5 = 0x3E,
HID_KEY_F6 = 0x3F,
HID_KEY_F7 = 0x40,
HID_KEY_F8 = 0x41,
HID_KEY_F9 = 0x42,
HID_KEY_F10 = 0x43,
HID_KEY_F11 = 0x44,
HID_KEY_F12 = 0x45,
HID_KEY_PRINT_SCREEN = 0x46,
HID_KEY_SCROLL_LOCK = 0x47,
HID_KEY_PAUSE = 0x48,
HID_KEY_INSERT = 0x49,
HID_KEY_HOME = 0x4A,
HID_KEY_PAGEUP = 0x4B,
HID_KEY_DELETE = 0x4C,
HID_KEY_END = 0x4D,
HID_KEY_PAGEDOWN = 0x4E,
HID_KEY_RIGHT = 0x4F,
HID_KEY_LEFT = 0x50,
HID_KEY_DOWN = 0x51,
HID_KEY_UP = 0x52,
HID_KEY_NUM_LOCK = 0x53,
HID_KEY_KEYPAD_DIV = 0x54,
HID_KEY_KEYPAD_MUL = 0x55,
HID_KEY_KEYPAD_SUB = 0x56,
HID_KEY_KEYPAD_ADD = 0x57,
HID_KEY_KEYPAD_ENTER = 0x58,
HID_KEY_KEYPAD_1 = 0x59,
HID_KEY_KEYPAD_2 = 0x5A,
HID_KEY_KEYPAD_3 = 0x5B,
HID_KEY_KEYPAD_4 = 0x5C,
HID_KEY_KEYPAD_5 = 0x5D,
HID_KEY_KEYPAD_6 = 0x5E,
HID_KEY_KEYPAD_7 = 0x5F,
HID_KEY_KEYPAD_8 = 0x60,
HID_KEY_KEYPAD_9 = 0x61,
HID_KEY_KEYPAD_0 = 0x62,
HID_KEY_KEYPAD_DELETE = 0x63,
HID_KEY_KEYPAD_SLASH = 0x64,
HID_KEY_APPLICATION = 0x65,
HID_KEY_POWER = 0x66,
HID_KEY_KEYPAD_EQUAL = 0x67,
HID_KEY_F13 = 0x68,
HID_KEY_F14 = 0x69,
HID_KEY_F15 = 0x6A,
HID_KEY_F16 = 0x6B,
HID_KEY_F17 = 0x6C,
HID_KEY_F18 = 0x6D,
HID_KEY_F19 = 0x6E,
HID_KEY_F20 = 0x6F,
HID_KEY_F21 = 0x70,
HID_KEY_F22 = 0x71,
HID_KEY_F23 = 0x72,
HID_KEY_F24 = 0x73,
HID_KEY_EXECUTE = 0x74,
HID_KEY_HELP = 0x75,
HID_KEY_MENU = 0x76,
HID_KEY_SELECT = 0x77,
HID_KEY_STOP = 0x78,
HID_KEY_AGAIN = 0x79,
HID_KEY_UNDO = 0x7A,
HID_KEY_CUT = 0x7B,
HID_KEY_COPY = 0x7C,
HID_KEY_PASTE = 0x7D,
HID_KEY_FIND = 0x7E,
HID_KEY_MUTE = 0x7F,
HID_KEY_VOLUME_UP = 0x80,
HID_KEY_VOLUME_DOWN = 0x81,
HID_KEY_LOCKING_CAPS_LOCK = 0x82,
HID_KEY_LOCKING_NUM_LOCK = 0x83,
HID_KEY_LOCKING_SCROLL_LOCK = 0x84,
HID_KEY_KEYPAD_COMMA = 0x85,
HID_KEY_KEYPAD_EQUAL_SIGN = 0x86,
HID_KEY_INTERNATIONAL_1 = 0x87,
HID_KEY_INTERNATIONAL_2 = 0x88,
HID_KEY_INTERNATIONAL_3 = 0x89,
HID_KEY_INTERNATIONAL_4 = 0x8A,
HID_KEY_INTERNATIONAL_5 = 0x8B,
HID_KEY_INTERNATIONAL_6 = 0x8C,
HID_KEY_INTERNATIONAL_7 = 0x8D,
HID_KEY_INTERNATIONAL_8 = 0x8E,
HID_KEY_INTERNATIONAL_9 = 0x8F,
HID_KEY_LANG_1 = 0x90,
HID_KEY_LANG_2 = 0x91,
HID_KEY_LANG_3 = 0x92,
HID_KEY_LANG_4 = 0x93,
HID_KEY_LANG_5 = 0x94,
HID_KEY_LANG_6 = 0x95,
HID_KEY_LANG_7 = 0x96,
HID_KEY_LANG_8 = 0x97,
HID_KEY_LANG_9 = 0x98,
HID_KEY_ALTERNATE_ERASE = 0x99,
HID_KEY_SYSREQ = 0x9A,
HID_KEY_CANCEL = 0x9B,
HID_KEY_CLEAR = 0x9C,
HID_KEY_PRIOR = 0x9D,
HID_KEY_RETURN = 0x9E,
HID_KEY_SEPARATOR = 0x9F,
HID_KEY_OUT = 0xA0,
HID_KEY_OPER = 0xA1,
HID_KEY_CLEAR_AGAIN = 0xA2,
HID_KEY_CRSEL = 0xA3,
HID_KEY_EXSEL = 0xA4,
HID_KEY_KEYPAD_00 = 0xB0,
HID_KEY_KEYPAD_000 = 0xB1,
HID_KEY_THOUSANDS_SEPARATOR = 0xB2,
HID_KEY_DECIMAL_SEPARATOR = 0xB3,
HID_KEY_CURRENCY_UNIT = 0xB4,
HID_KEY_CURRENCY_SUB_UNIT = 0xB5,
HID_KEY_KEYPAD_OPEN_PARENTHESIS = 0xB6,
HID_KEY_KEYPAD_CLOSE_PARENTHESIS = 0xB7,
HID_KEY_KEYPAD_OPEN_BRACE = 0xB8,
HID_KEY_KEYPAD_CLOSE_BRACE = 0xB9,
HID_KEY_KEYPAD_TAB = 0xBA,
HID_KEY_KEYPAD_BACKSPACE = 0xBB,
HID_KEY_KEYPAD_A = 0xBC,
HID_KEY_KEYPAD_B = 0xBD,
HID_KEY_KEYPAD_C = 0xBE,
HID_KEY_KEYPAD_D = 0xBF,
HID_KEY_KEYPAD_E = 0xC0,
HID_KEY_KEYPAD_F = 0xC1,
HID_KEY_KEYPAD_XOR = 0xC2,
HID_KEY_KEYPAD_CARET = 0xC3,
HID_KEY_KEYPAD_PERCENT = 0xC4,
HID_KEY_KEYPAD_LESSER = 0xC5,
HID_KEY_KEYPAD_GREATER = 0xC6,
HID_KEY_KEYPAD_AND = 0xC7,
HID_KEY_KEYPAD_LOGICAL_AND = 0xC8,
HID_KEY_KEYPAD_OR = 0xC9,
HID_KEY_KEYPAD_LOGICAL_OR = 0xCA,
HID_KEY_KEYPAD_COLON = 0xCB,
HID_KEY_KEYPAD_SHARP = 0xCC,
HID_KEY_KEYPAD_SPACE = 0xCD,
HID_KEY_KEYPAD_AT = 0xCE,
HID_KEY_KEYPAD_BANG = 0xCF,
HID_KEY_KEYPAD_MEMORY_STORE = 0xD0,
HID_KEY_KEYPAD_MEMORY_RECALL = 0xD1,
HID_KEY_KEYPAD_MEMORY_CLEAD = 0xD2,
HID_KEY_KEYPAD_MEMORY_ADD = 0xD3,
HID_KEY_KEYPAD_MEMORY_SUBSTRACT = 0xD4,
HID_KEY_KEYPAD_MEMORY_MULTIPLY = 0xD5,
HID_KEY_KEYPAD_MEMORY_DIVIDE = 0xD6,
HID_KEY_KEYPAD_SIGN = 0xD7,
HID_KEY_KEYPAD_CLEAR = 0xD8,
HID_KEY_KEYPAD_CLEAR_ENTRY = 0xD9,
HID_KEY_KEYPAD_BINARY = 0xDA,
HID_KEY_KEYPAD_OCTAL = 0xDB,
HID_KEY_KEYPAD_DECIMAL = 0xDC,
HID_KEY_KEYPAD_HEXADECIMAL = 0xDD,
HID_KEY_LEFT_CONTROL = 0xE0,
HID_KEY_LEFT_SHIFT = 0xE1,
HID_KEY_LEFT_ALT = 0xE2,
HID_KEY_LEFT_GUI = 0xE3,
HID_KEY_RIGHT_CONTROL = 0xE0,
HID_KEY_RIGHT_SHIFT = 0xE1,
HID_KEY_RIGHT_ALT = 0xE2,
HID_KEY_RIGHT_GUI = 0xE3
} __attribute__((packed)) hid_key_t;
// Modifier bit mask
#define HID_LEFT_CONTROL (1 << 0)
#define HID_LEFT_SHIFT (1 << 1)
#define HID_LEFT_ALT (1 << 2)
#define HID_LEFT_GUI (1 << 3)
#define HID_RIGHT_CONTROL (1 << 4)
#define HID_RIGHT_SHIFT (1 << 5)
#define HID_RIGHT_ALT (1 << 6)
#define HID_RIGHT_GUI (1 << 7)
/**
* @brief HID Keyboard Key number for Boot Interface
*
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef enum {
HID_KEYBOARD_KEY_NUMBER0 = 0,
HID_KEYBOARD_KEY_NUMBER1,
HID_KEYBOARD_KEY_NUMBER2,
HID_KEYBOARD_KEY_NUMBER3,
HID_KEYBOARD_KEY_NUMBER4,
HID_KEYBOARD_KEY_NUMBER5,
HID_KEYBOARD_KEY_MAX,
} hid_keyboard_key_number_t;
/**
* @brief HID Keyboard Input Report for Boot Interfaces
*
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef struct {
union {
struct {
uint8_t left_ctr: 1;
uint8_t left_shift: 1;
uint8_t left_alt: 1;
uint8_t left_gui: 1;
uint8_t rigth_ctr: 1;
uint8_t right_shift: 1;
uint8_t right_alt: 1;
uint8_t right_gui: 1;
};
uint8_t val;
} modifier;
uint8_t reserved;
uint8_t key[HID_KEYBOARD_KEY_MAX];
} __attribute__((packed)) hid_keyboard_input_report_boot_t;
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -1,36 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief HID Mouse Input Report for Boot Interfaces
*
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
*/
typedef struct {
union {
struct {
uint8_t button1: 1;
uint8_t button2: 1;
uint8_t button3: 1;
uint8_t reserved: 5;
};
uint8_t val;
} buttons;
int8_t x_displacement;
int8_t y_displacement;
} __attribute__((packed)) hid_mouse_input_report_boot_t;
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -1,5 +0,0 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_app_hid_basic)

View File

@ -1,3 +0,0 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |

View File

@ -1,6 +0,0 @@
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRC_DIRS "." "../../../"
INCLUDE_DIRS "." "../../../include" ${CMAKE_CURRENT_BINARY_DIR}
REQUIRES usb unity esp_common
WHOLE_ARCHIVE)

View File

@ -1,49 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_err.h"
#include "hid_host.h"
#include "test_hid_basic.h"
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
unity_utils_evaluate_leaks();
}
void app_main(void)
{
// ____ ___ ___________________ __ __
// | | \/ _____/\______ \ _/ |_ ____ _______/ |_
// | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\.
// | | / / \ | | \ | | \ ___/ \___ \ | |
// |______/ /_______ / |______ / |__| \___ >____ > |__|
// \/ \/ \/ \/
printf(" ____ ___ ___________________ __ __ \r\n");
printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n");
printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n");
printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n");
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
printf(" \\/ \\/ \\/ \\/ \r\n");
unity_utils_setup_heap_record(80);
unity_utils_set_leak_level(20);
unity_run_menu();
}

View File

@ -1,379 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "usb/usb_host.h"
#include "hid_host.h"
#include "hid_usage_keyboard.h"
#include "hid_usage_mouse.h"
#include "test_hid_basic.h"
typedef enum {
HOST_NO_CLIENT = 0x1,
HOST_ALL_FREE = 0x2,
DEVICE_CONNECTED = 0x4,
DEVICE_DISCONNECTED = 0x8,
DEVICE_ADDRESS_MASK = 0xFF0,
} test_app_event_t;
static EventGroupHandle_t test_usb_flags;
hid_host_device_handle_t test_hid_device = NULL;
TaskHandle_t test_usb_event_task_handle;
SemaphoreHandle_t test_semaphore;
// ----------------------- Private -------------------------
/**
* @brief USB HID Host event callback. Handle such event as device connection and removing
*
* @param[in] event HID Host device event
* @param[in] arg Pointer to arguments, does not used
*
*/
static void test_hid_host_event_callback(const hid_host_event_t *event, void *arg)
{
if (event->event == HID_DEVICE_CONNECTED) {
// Obtained USB device address is placed after application events
xEventGroupSetBits(test_usb_flags, DEVICE_CONNECTED | (event->device.address << 4));
} else if (event->event == HID_DEVICE_DISCONNECTED) {
xEventGroupSetBits(test_usb_flags, DEVICE_DISCONNECTED);
}
}
/**
* @brief USB HID Host interface callback
*
* @param[in] event HID Host interface event
* @param[in] arg Pointer to arguments, does not used
*/
static void test_hid_host_interface_event_callback(const hid_host_interface_event_t *event, void *arg)
{
switch (event->event) {
case HID_DEVICE_INTERFACE_INIT:
printf("Found Interface number %d, protocol %s\n",
event->interface.num,
(event->interface.proto == HID_PROTOCOL_KEYBOARD)
? "Keyboard"
: "Mouse");
break;
case HID_DEVICE_INTERFACE_TRANSFER_ERROR:
case HID_DEVICE_INTERFACE_CLAIM:
case HID_DEVICE_INTERFACE_RELEASE:
default:
// ... do nothing here for now
break;
}
}
/**
* @brief Handle common USB host library events
*
* @param[in] args Pointer to arguments, does not used
*/
static void test_handle_usb_events(void *args)
{
while (1) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
// Release devices once all clients has deregistered
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
usb_host_device_free_all();
xEventGroupSetBits(test_usb_flags, HOST_NO_CLIENT);
}
// Give ready_to_uninstall_usb semaphore to indicate that USB Host library
// can be deinitialized, and terminate this task.
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
xEventGroupSetBits(test_usb_flags, HOST_ALL_FREE);
}
}
vTaskDelete(NULL);
}
/**
* @brief Create global semaphore
*/
static void test_create_semaphore(void)
{
test_semaphore = xSemaphoreCreateBinary();
}
/**
* @brief Deletes global semaphore
*/
static void test_delete_semaphore(void)
{
vSemaphoreDelete(test_semaphore);
}
/**
* @brief HID Keyboard report callback
*
* Keyboard report length == size of keyboard boot report
*
*/
static void test_hid_host_keyboard_report_callback(const uint8_t *const data, const int length)
{
printf("Keyboard report length %d\n", length);
TEST_ASSERT_EQUAL(sizeof(hid_keyboard_input_report_boot_t), length);
xSemaphoreGive(test_semaphore);
}
/**
* @brief HID Mouse report callback
*
* Mouse report length >= size of mouse boot report
*
*/
static void test_hid_host_mouse_report_callback(const uint8_t *const data, const int length)
{
printf("Mouse report length %d\n", length);
TEST_ASSERT_GREATER_OR_EQUAL(sizeof(hid_mouse_input_report_boot_t), length);
xSemaphoreGive(test_semaphore);
}
/**
* @brief Waits global semaphore for timeout
*/
static bool test_wait_semaphore_timeout(unsigned int ms)
{
return ( xSemaphoreTake(test_semaphore, pdMS_TO_TICKS(ms)) )
? true
: false;
}
/**
* @brief HID Keyboard report length test
*
* - Claim Keyboard interface
* - Wait Keyboard interface report callback for 5 sec
* - Release Keyboard interface
*/
static void test_hid_keyboard_report_length(void)
{
hid_host_interface_handle_t keyboard_handle;
hid_host_interface_config_t keyboard_iface_config = {
.proto = HID_PROTOCOL_KEYBOARD,
.callback = test_hid_host_keyboard_report_callback,
};
// claim keyboard interface
TEST_ASSERT_EQUAL(ESP_OK, hid_host_claim_interface(&keyboard_iface_config, &keyboard_handle) );
// wait report
printf("Please, press any keyboard key ...\n");
fflush(stdout);
test_wait_semaphore_timeout(5000);
// release keyboard interface
TEST_ASSERT_EQUAL(ESP_OK, hid_host_release_interface(keyboard_handle) );
}
/**
* @brief HID Mouse report length test
*
* - Claim Mouse interface
* - Wait Mouse interface report callback for 5 sec
* - Release Mouse interface
*/
static void test_hid_mouse_report_length(void)
{
hid_host_interface_handle_t mouse_handle;
hid_host_interface_config_t mouse_iface_config = {
.proto = HID_PROTOCOL_MOUSE,
.callback = test_hid_host_mouse_report_callback,
};
// claim mouse interface
TEST_ASSERT_EQUAL(ESP_OK, hid_host_claim_interface(&mouse_iface_config, &mouse_handle) );
// wait report
printf("Please, move mouse or press any button ...\n");
fflush(stdout);
test_wait_semaphore_timeout(5000);
// release mouse interface
TEST_ASSERT_EQUAL(ESP_OK, hid_host_release_interface(mouse_handle) );
}
// ----------------------- Public -------------------------
/**
* @brief Setups HID
*
* - Create events handle
* - Create usb_events task
* - Install USB Host driver
* - Install HID Host driver
*/
void test_hid_setup(void)
{
test_usb_flags = xEventGroupCreate();
const usb_host_config_t host_config = { .intr_flags = ESP_INTR_FLAG_LEVEL1 };
TEST_ASSERT_EQUAL(ESP_OK, usb_host_install(&host_config) );
xTaskCreate(test_handle_usb_events, "usb_events", 4096, NULL, 2, &test_usb_event_task_handle);
// hid host driver config
const hid_host_driver_config_t hid_host_config = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 4096,
.callback = test_hid_host_event_callback,
};
TEST_ASSERT_EQUAL(ESP_OK, hid_host_install(&hid_host_config) );
}
/**
* @brief Waits HID connection and install
*
* - Wait events: DEVICE_CONNECTED, DEVICE_ADDRESS_MASK
* - On DEVICE_ADDRESS_MASK install HID Host device driver and return handle
*
* @return hid_host_device_handle_t HID Device handle
*/
hid_host_device_handle_t test_hid_wait_connection_and_install(void)
{
TEST_ASSERT_NULL(test_hid_device);
printf("Please, insert HID device ...\n");
fflush(stdout);
EventBits_t event = xEventGroupWaitBits(test_usb_flags,
DEVICE_CONNECTED | DEVICE_ADDRESS_MASK,
pdTRUE,
pdFALSE,
portMAX_DELAY);
if (event & DEVICE_CONNECTED) {
xEventGroupClearBits(test_usb_flags, DEVICE_CONNECTED);
}
if (event & DEVICE_ADDRESS_MASK) {
xEventGroupClearBits(test_usb_flags, DEVICE_ADDRESS_MASK);
const hid_host_device_config_t hid_host_device_config = {
.dev_addr = (event & DEVICE_ADDRESS_MASK) >> 4,
.iface_event_cb = test_hid_host_interface_event_callback,
.iface_event_arg = NULL,
};
TEST_ASSERT_EQUAL(ESP_OK, hid_host_install_device(&hid_host_device_config, &test_hid_device) );
}
return test_hid_device;
}
/**
* @brief Test HID Wait device removal
*
* - Wait events: DEVICE_DISCONNECTED
* - On DEVICE_DISCONNECTED proceed
*/
void test_hid_wait_for_removal(void)
{
printf("Please, remove HID device ...\n");
fflush(stdout);
EventBits_t event = xEventGroupWaitBits(test_usb_flags,
DEVICE_DISCONNECTED,
pdTRUE,
pdFALSE,
portMAX_DELAY);
if (event & DEVICE_DISCONNECTED) {
xEventGroupClearBits(test_usb_flags, DEVICE_DISCONNECTED);
}
}
/**
* @brief Uninstalls HID device by handle obtained from test_hid_wait_connection_and_install()
*
* - Wait events: DEVICE_DISCONNECTED
* - On DEVICE_DISCONNECTED proceed
*/
void test_hid_uninstall_hid_device(hid_host_device_handle_t hid_device_handle)
{
TEST_ASSERT_EQUAL(ESP_OK, hid_host_uninstall_device(test_hid_device) );
}
/**
* @brief Teardowns HID
*
* - Uninstall HID Host driver
* - Uninstall USB Host driver
* - Delete usb_events task
* - Delete events handle
*/
void test_hid_teardown(void)
{
TEST_ASSERT_EQUAL(ESP_OK, hid_host_uninstall() );
TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall() );
vTaskDelete(test_usb_event_task_handle);
vEventGroupDelete(test_usb_flags);
}
// ------------------------- HID Test ------------------------------------------
static void test_setup_hid_basic(void)
{
test_hid_setup();
test_create_semaphore();
}
static void test_teardown_hid_basic(void)
{
test_delete_semaphore();
test_hid_teardown();
//Short delay to allow task to be cleaned up
vTaskDelay(pdMS_TO_TICKS(10));
test_hid_device = NULL;
}
TEST_CASE("(HID Host) Memory leakage basic", "[auto][hid_host]")
{
test_setup_hid_basic();
test_teardown_hid_basic();
vTaskDelay(20);
}
TEST_CASE("(HID Host) Memory leakage with HID device", "[hid_host]")
{
test_setup_hid_basic();
test_hid_device = test_hid_wait_connection_and_install();
test_hid_wait_for_removal();
test_hid_uninstall_hid_device(test_hid_device);
test_teardown_hid_basic();
vTaskDelay(20);
}
TEST_CASE("(HID Host) HID Keyboard report length", "[hid_host]")
{
test_setup_hid_basic();
test_hid_device = test_hid_wait_connection_and_install();
test_hid_keyboard_report_length();
test_hid_wait_for_removal();
test_hid_uninstall_hid_device(test_hid_device);
test_teardown_hid_basic();
vTaskDelay(20);
}
TEST_CASE("(HID Host) HID Mouse report length", "[hid_host]")
{
test_setup_hid_basic();
test_hid_device = test_hid_wait_connection_and_install();
test_hid_mouse_report_length();
test_hid_wait_for_removal();
test_hid_uninstall_hid_device(test_hid_device);
test_teardown_hid_basic();
vTaskDelay(20);
}

View File

@ -1,31 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "hid_host.h"
#ifdef __cplusplus
extern "C" {
#endif
extern hid_host_device_handle_t test_hid_device;
// ------------------------ HID Test -------------------------------------------
void test_hid_setup(void);
hid_host_device_handle_t test_hid_wait_connection_and_install(void);
void test_hid_wait_for_removal(void);
void test_hid_uninstall_hid_device(hid_host_device_handle_t hid_device_handle);
void test_hid_teardown(void);
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -1,150 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "hid_host.h"
#define TEST_HID_ERR_HANDLING_CASES (3)
static void test_install_hid_driver_without_config(void);
static void test_install_hid_driver_with_wrong_config(void);
static void test_claim_interface_without_driver(void);
void (*test_hid_err_handling_case[TEST_HID_ERR_HANDLING_CASES])(void) = {
test_install_hid_driver_without_config,
test_install_hid_driver_with_wrong_config,
test_claim_interface_without_driver
};
// ----------------------- Private -------------------------
/**
* @brief USB HID Host event callback. Handle such event as device connection and removing
*
* @param[in] event HID Host device event
* @param[in] arg Pointer to arguments, does not used
*
*/
static void test_hid_host_event_callback_stub(const hid_host_event_t *event, void *arg)
{
if (event->event == HID_DEVICE_CONNECTED) {
// Device connected
} else if (event->event == HID_DEVICE_DISCONNECTED) {
// Device disconnected
}
}
// Install HID driver without USB Host and without configuration
static void test_install_hid_driver_without_config(void)
{
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(NULL));
}
// Install HID driver without USB Host and with configuration
static void test_install_hid_driver_with_wrong_config(void)
{
const hid_host_driver_config_t hid_host_config_callback_null = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 4096,
.core_id = 0,
.callback = NULL, /* error expected */
.callback_arg = NULL
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_callback_null));
const hid_host_driver_config_t hid_host_config_stack_size_null = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 0, /* error expected */
.core_id = 0,
.callback = NULL,
.callback_arg = NULL
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_stack_size_null));
const hid_host_driver_config_t hid_host_config_task_priority_null = {
.create_background_task = true,
.task_priority = 0,/* error expected */
.stack_size = 4096,
.core_id = 0,
.callback = NULL,
.callback_arg = NULL
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_task_priority_null));
const hid_host_driver_config_t hid_host_config_correct = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 4096,
.core_id = 0,
.callback = test_hid_host_event_callback_stub,
.callback_arg = NULL
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_install(&hid_host_config_correct));
}
// Open device without installed driver
static void test_claim_interface_without_driver(void)
{
hid_host_interface_handle_t keyboard_handle;
hid_host_interface_config_t keyboard_iface_config_wrong_proto1 = {
.proto = HID_PROTOCOL_NONE,
.callback = NULL,
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG,
hid_host_claim_interface(&keyboard_iface_config_wrong_proto1, &keyboard_handle));
hid_host_interface_config_t keyboard_iface_config_wrong_proto2 = {
.proto = HID_PROTOCOL_MAX,
.callback = NULL,
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG,
hid_host_claim_interface(&keyboard_iface_config_wrong_proto2, &keyboard_handle));
/*
hid_host_interface_config_t keyboard_iface_config = {
.proto = HID_PROTOCOL_KEYBOARD,
.callback = NULL,
};
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE,
hid_host_claim_interface(&keyboard_iface_config, &keyboard_handle));
*/
}
// ----------------------- Public --------------------------
/**
* @brief HID
*
* There are multiple erroneous scenarios checked in this test:
*
* -# Install HID driver without USB Host
* -# Open device without installed driver
* -# Uninstall driver before installing it
* -# Open non-existent device
* -# Open the same device twice
* -# Uninstall driver with open devices
*/
TEST_CASE("(HID Host) Error handling", "[auto][hid_host]")
{
for (int i = 0; i < TEST_HID_ERR_HANDLING_CASES; i++) {
(*test_hid_err_handling_case[i])();
}
}

View File

@ -1,55 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "hid_host.h"
#include "test_hid_basic.h"
// ----------------------- Private -------------------------
/**
* @brief Test HID Uninstall USB driver with device inserted
*
* - Wait events: DEVICE_DISCONNECTED
* - On DEVICE_DISCONNECTED proceed
*/
void test_hid_usb_uninstall(void)
{
printf("HID device remain inserted, uninstall device ... ");
test_hid_uninstall_hid_device(test_hid_device);
}
// ----------------------- Public --------------------------
// ------------------------- USB Test ------------------------------------------
static void test_setup_hid_usb_driver(void)
{
test_hid_setup();
}
static void test_teardown_hid_usb_driver(void)
{
test_hid_teardown();
//Short delay to allow task to be cleaned up
vTaskDelay(pdMS_TO_TICKS(10));
test_hid_device = NULL;
}
TEST_CASE("(HID Host) USB uninstall (dev present)", "[hid_host]")
{
test_setup_hid_usb_driver();
test_hid_device = test_hid_wait_connection_and_install();
test_hid_usb_uninstall();
test_teardown_hid_usb_driver();
vTaskDelay(20);
}

View File

@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded_idf.dut import IdfDut
@pytest.mark.esp32s2
@pytest.mark.esp32s3
def test_hid_basic(dut: IdfDut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('[auto]')
dut.expect_unity_test_output()

View File

@ -1,3 +0,0 @@
CONFIG_ESP_TASK_WDT=n
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y

View File

@ -11,41 +11,46 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "usb/usb_host.h" #include "usb/usb_host.h"
#include "errno.h" #include "errno.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "hid_host.h" #include "usb/hid_host.h"
#include "hid_usage_keyboard.h" #include "usb/hid_usage_keyboard.h"
#include "hid_usage_mouse.h" #include "usb/hid_usage_mouse.h"
/* GPIO Pin number for quit from example logic */
#define APP_QUIT_PIN GPIO_NUM_0 #define APP_QUIT_PIN GPIO_NUM_0
#define APP_QUIT_PIN_POLL_MS 500
#define READY_TO_UNINSTALL (HOST_NO_CLIENT | HOST_ALL_FREE) static const char *TAG = "example";
QueueHandle_t hid_host_event_queue;
/* Main char symbol for ENTER key */ bool user_shutdown = false;
#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 Application Event from USB Host driver * @brief HID Host event
* *
* This event is used for delivering the HID Host event from callback to a task.
*/ */
typedef enum { typedef struct {
HOST_NO_CLIENT = 0x1, hid_host_device_handle_t hid_device_handle;
HOST_ALL_FREE = 0x2, hid_host_driver_event_t event;
DEVICE_CONNECTED = 0x4, void *arg;
DEVICE_DISCONNECTED = 0x8, } hid_host_event_queue_t;
DEVICE_ADDRESS_MASK = 0xFF0,
} app_event_t; /**
* @brief HID Protocol string names
*/
static const char *hid_proto_name_str[] = {
"NONE",
"KEYBOARD",
"MOUSE"
};
/** /**
* @brief Key event * @brief Key event
*
*/ */
typedef struct { typedef struct {
enum key_state { enum key_state {
@ -56,14 +61,10 @@ typedef struct {
uint8_t key_code; uint8_t key_code;
} key_event_t; } key_event_t;
#define USB_EVENTS_TO_WAIT (DEVICE_CONNECTED | DEVICE_ADDRESS_MASK | DEVICE_DISCONNECTED) /* Main char symbol for ENTER key */
#define KEYBOARD_ENTER_MAIN_CHAR '\r'
static const char *TAG = "example"; /* When set to 1 pressing ENTER will be extending with LineFeed during serial debug output */
static EventGroupHandle_t usb_flags; #define KEYBOARD_ENTER_LF_EXTEND 1
static bool hid_device_connected = false;
hid_host_interface_handle_t keyboard_handle = NULL;
hid_host_interface_handle_t mouse_handle = NULL;
/** /**
* @brief Scancode to ascii table * @brief Scancode to ascii table
@ -135,16 +136,17 @@ const uint8_t keycode2ascii [57][2] = {
*/ */
static void hid_print_new_device_report_header(hid_protocol_t proto) static void hid_print_new_device_report_header(hid_protocol_t proto)
{ {
static hid_protocol_t prev_proto_output = HID_PROTOCOL_NONE; static hid_protocol_t prev_proto_output = -1;
if (prev_proto_output != proto) { if (prev_proto_output != proto) {
prev_proto_output = proto; prev_proto_output = proto;
printf("\r\n"); printf("\r\n");
if (proto == HID_PROTOCOL_MOUSE) { if (proto == HID_PROTOCOL_MOUSE) {
printf("Mouse\r\n"); printf("Mouse\r\n");
} } else if (proto == HID_PROTOCOL_KEYBOARD) {
if (proto == HID_PROTOCOL_KEYBOARD) {
printf("Keyboard\r\n"); printf("Keyboard\r\n");
} else {
printf("Generic\r\n");
} }
fflush(stdout); fflush(stdout);
} }
@ -160,8 +162,8 @@ static void hid_print_new_device_report_header(hid_protocol_t proto)
*/ */
static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier) static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier)
{ {
if ((modifier && HID_LEFT_SHIFT) || if (((modifier & HID_LEFT_SHIFT) == HID_LEFT_SHIFT) ||
(modifier && HID_RIGHT_SHIFT)) { ((modifier & HID_RIGHT_SHIFT) == HID_RIGHT_SHIFT)) {
return true; return true;
} }
return false; return false;
@ -324,113 +326,118 @@ static void hid_host_mouse_report_callback(const uint8_t *const data, const int
} }
/** /**
* @brief USB HID Host event callback. Handle such event as device connection and removing * @brief USB HID Host Generic Interface report callback handler
* *
* @param[in] event HID device event * 'generic' means anything else than mouse or keyboard
* @param[in] arg Pointer to arguments, does not used *
* @param[in] data Pointer to input report data buffer
* @param[in] length Length of input report data buffer
*/ */
static void hid_host_event_callback(const hid_host_event_t *event, void *arg) static void hid_host_generic_report_callback(const uint8_t *const data, const int length)
{ {
if (event->event == HID_DEVICE_CONNECTED) { hid_print_new_device_report_header(HID_PROTOCOL_NONE);
// Obtained USB device address is placed after application events for (int i = 0; i < length; i++) {
xEventGroupSetBits(usb_flags, DEVICE_CONNECTED | (event->device.address << 4)); printf("%02X", data[i]);
} else if (event->event == HID_DEVICE_DISCONNECTED) {
xEventGroupSetBits(usb_flags, DEVICE_DISCONNECTED);
} }
putchar('\r');
} }
/** /**
* @brief USB HID Host interface callback * @brief USB HID Host interface callback
* *
* @param[in] event HID interface event * @param[in] hid_device_handle HID Device handle
* @param[in] event HID Host interface event
* @param[in] arg Pointer to arguments, does not used * @param[in] arg Pointer to arguments, does not used
*/ */
static void hid_host_interface_event_callback(const hid_host_interface_event_t *event, void *arg) void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle,
const hid_host_interface_event_t event,
void *arg)
{ {
switch (event->event) { uint8_t data[64] = { 0 };
case HID_DEVICE_INTERFACE_INIT: size_t data_length = 0;
ESP_LOGI(TAG, "Interface number %d, protocol %s", hid_host_dev_params_t dev_params;
event->interface.num, ESP_ERROR_CHECK( hid_host_device_get_params(hid_device_handle, &dev_params));
(event->interface.proto == HID_PROTOCOL_KEYBOARD)
? "Keyboard"
: "Mouse");
if (event->interface.proto == HID_PROTOCOL_KEYBOARD) { switch (event) {
const hid_host_interface_config_t hid_keyboard_config = { case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
.proto = HID_PROTOCOL_KEYBOARD, ESP_ERROR_CHECK( hid_host_device_get_raw_input_report_data(hid_device_handle,
.callback = hid_host_keyboard_report_callback, data,
}; 64,
&data_length));
hid_host_claim_interface(&hid_keyboard_config, &keyboard_handle); if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {
if (HID_PROTOCOL_KEYBOARD == dev_params.proto) {
hid_host_keyboard_report_callback(data, data_length);
} else if (HID_PROTOCOL_MOUSE == dev_params.proto) {
hid_host_mouse_report_callback(data, data_length);
} }
} else {
if (event->interface.proto == HID_PROTOCOL_MOUSE) { hid_host_generic_report_callback(data, data_length);
const hid_host_interface_config_t hid_mouse_config = {
.proto = HID_PROTOCOL_MOUSE,
.callback = hid_host_mouse_report_callback,
};
hid_host_claim_interface(&hid_mouse_config, &mouse_handle);
} }
break; break;
case HID_DEVICE_INTERFACE_TRANSFER_ERROR: case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "Interface number %d, transfer error", ESP_LOGI(TAG, "HID Device, protocol '%s' DISCONNECTED",
event->interface.num); hid_proto_name_str[dev_params.proto]);
ESP_ERROR_CHECK( hid_host_device_close(hid_device_handle) );
break; break;
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
case HID_DEVICE_INTERFACE_CLAIM: ESP_LOGI(TAG, "HID Device, protocol '%s' TRANSFER_ERROR",
case HID_DEVICE_INTERFACE_RELEASE: hid_proto_name_str[dev_params.proto]);
// ... do nothing here for now
break; break;
default: default:
ESP_LOGI(TAG, "%s Unhandled event %X, Interface number %d", ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event",
__FUNCTION__, hid_proto_name_str[dev_params.proto]);
event->event,
event->interface.num);
break; break;
} }
} }
/** /**
* @brief Handle common USB host library events * @brief USB HID Host Device event
* *
* @param[in] args Pointer to arguments, does not used * @param[in] hid_device_handle HID Device handle
* @param[in] event HID Host Device event
* @param[in] arg Pointer to arguments, does not used
*/ */
static void handle_usb_events(void *args) void hid_host_device_event(hid_host_device_handle_t hid_device_handle,
const hid_host_driver_event_t event,
void *arg)
{ {
while (1) { hid_host_dev_params_t dev_params;
uint32_t event_flags; ESP_ERROR_CHECK( hid_host_device_get_params(hid_device_handle, &dev_params));
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
// Release devices once all clients has deregistered switch (event) {
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { case HID_HOST_DRIVER_EVENT_CONNECTED:
usb_host_device_free_all(); ESP_LOGI(TAG, "HID Device, protocol '%s' CONNECTED",
xEventGroupSetBits(usb_flags, HOST_NO_CLIENT); hid_proto_name_str[dev_params.proto]);
}
// Give ready_to_uninstall_usb semaphore to indicate that USB Host library
// can be deinitialized, and terminate this task.
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
xEventGroupSetBits(usb_flags, HOST_ALL_FREE);
}
}
vTaskDelete(NULL); const hid_host_device_config_t dev_config = {
.callback = hid_host_interface_callback,
.callback_arg = NULL
};
ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) );
if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {
ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT));
if (HID_PROTOCOL_KEYBOARD == dev_params.proto) {
ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0));
}
}
ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) );
break;
default:
break;
}
} }
static bool wait_for_event(EventBits_t event, TickType_t timeout) /**
* @brief Start USB Host install and handle common USB host library events while app pin not low
*
* @param[in] arg Not used
*/
static void usb_lib_task(void *arg)
{ {
return xEventGroupWaitBits(usb_flags, event, pdTRUE, pdTRUE, timeout) & event;
}
void app_main(void)
{
TaskHandle_t usb_events_task_handle;
hid_host_device_handle_t hid_device;
BaseType_t task_created;
const gpio_config_t input_pin = { const gpio_config_t input_pin = {
.pin_bit_mask = BIT64(APP_QUIT_PIN), .pin_bit_mask = BIT64(APP_QUIT_PIN),
.mode = GPIO_MODE_INPUT, .mode = GPIO_MODE_INPUT,
@ -438,78 +445,129 @@ void app_main(void)
}; };
ESP_ERROR_CHECK( gpio_config(&input_pin) ); ESP_ERROR_CHECK( gpio_config(&input_pin) );
ESP_LOGI(TAG, "HID HOST example");
usb_flags = xEventGroupCreate();
assert(usb_flags);
const usb_host_config_t host_config = { const usb_host_config_t host_config = {
.skip_phy_setup = false, .skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1 .intr_flags = ESP_INTR_FLAG_LEVEL1,
}; };
ESP_ERROR_CHECK( usb_host_install(&host_config) ); ESP_ERROR_CHECK( usb_host_install(&host_config) );
task_created = xTaskCreate(handle_usb_events, "usb_events", 4096, NULL, 2, &usb_events_task_handle); xTaskNotifyGive(arg);
assert(task_created);
// hid host driver config while (gpio_get_level(APP_QUIT_PIN) != 0) {
const hid_host_driver_config_t hid_host_config = { uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
// Release devices once all clients has deregistered
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
usb_host_device_free_all();
ESP_LOGI(TAG, "USB Event flags: NO_CLIENTS");
}
// All devices were removed
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGI(TAG, "USB Event flags: ALL_FREE");
}
}
// App Button was pressed, trigger the flag
user_shutdown = true;
ESP_LOGI(TAG, "USB shutdown");
// Clean up USB Host
vTaskDelay(10); // Short delay to allow clients clean-up
ESP_ERROR_CHECK( usb_host_uninstall());
vTaskDelete(NULL);
}
/**
* @brief HID Host main task
*
* Creates queue and get new event from the queue
*
* @param[in] pvParameters Not used
*/
void hid_host_task(void *pvParameters)
{
hid_host_event_queue_t evt_queue;
// Create queue
hid_host_event_queue = xQueueCreate(10, sizeof(hid_host_event_queue_t));
// Wait queue
while (!user_shutdown) {
if (xQueueReceive(hid_host_event_queue, &evt_queue, pdMS_TO_TICKS(50))) {
hid_host_device_event(evt_queue.hid_device_handle,
evt_queue.event,
evt_queue.arg);
}
}
xQueueReset(hid_host_event_queue);
vQueueDelete(hid_host_event_queue);
vTaskDelete(NULL);
}
/**
* @brief HID Host Device callback
*
* Puts new HID Device event to the queue
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Device event
* @param[in] arg Not used
*/
void hid_host_device_callback(hid_host_device_handle_t hid_device_handle,
const hid_host_driver_event_t event,
void *arg)
{
const hid_host_event_queue_t evt_queue = {
.hid_device_handle = hid_device_handle,
.event = event,
.arg = arg
};
xQueueSend(hid_host_event_queue, &evt_queue, 0);
}
void app_main(void)
{
BaseType_t task_created;
ESP_LOGI(TAG, "HID Host example");
/*
* Create usb_lib_task to:
* - initialize USB Host library
* - Handle USB Host events while APP pin in in HIGH state
*/
task_created = xTaskCreatePinnedToCore(usb_lib_task,
"usb_events",
4096,
xTaskGetCurrentTaskHandle(),
2, NULL, 0);
assert(task_created == pdTRUE);
// Wait for notification from usb_lib_task to proceed
ulTaskNotifyTake(false, 1000);
/*
* HID host driver configuration
* - create background task for handling low level event inside the HID driver
* - provide the device callback to get new HID Device connection event
*/
const hid_host_driver_config_t hid_host_driver_config = {
.create_background_task = true, .create_background_task = true,
.task_priority = 5, .task_priority = 5,
.stack_size = 4096, .stack_size = 4096,
.core_id = 0, .core_id = 0,
.callback = hid_host_event_callback, .callback = hid_host_device_callback,
.callback_arg = NULL .callback_arg = NULL
}; };
ESP_ERROR_CHECK( hid_host_install(&hid_host_config) ); ESP_ERROR_CHECK( hid_host_install(&hid_host_driver_config) );
do { // Task is working until the devices are gone (while 'user_shutdown' if false)
EventBits_t event = xEventGroupWaitBits(usb_flags, USB_EVENTS_TO_WAIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(APP_QUIT_PIN_POLL_MS)); user_shutdown = false;
if (event & DEVICE_CONNECTED) { /*
xEventGroupClearBits(usb_flags, DEVICE_CONNECTED); * Create HID Host task process for handle events
hid_device_connected = true; * IMPORTANT: Task is necessary here while there is no possibility to interact
} * with USB device from the callback.
*/
if (event & DEVICE_ADDRESS_MASK) { task_created = xTaskCreate(&hid_host_task, "hid_task", 4 * 1024, NULL, 2, NULL);
xEventGroupClearBits(usb_flags, DEVICE_ADDRESS_MASK); assert(task_created == pdTRUE);
const hid_host_device_config_t hid_host_device_config = {
.dev_addr = (event & DEVICE_ADDRESS_MASK) >> 4,
.iface_event_cb = hid_host_interface_event_callback,
.iface_event_arg = NULL,
};
ESP_ERROR_CHECK( hid_host_install_device(&hid_host_device_config, &hid_device) );
}
if (event & DEVICE_DISCONNECTED) {
xEventGroupClearBits(usb_flags, DEVICE_DISCONNECTED);
hid_host_release_interface(keyboard_handle);
hid_host_release_interface(mouse_handle);
ESP_ERROR_CHECK( hid_host_uninstall_device(hid_device) );
hid_device_connected = false;
}
} while (gpio_get_level(APP_QUIT_PIN) != 0);
if (hid_device_connected) {
ESP_LOGI(TAG, "Uninitializing HID Device");
hid_host_release_interface(keyboard_handle);
hid_host_release_interface(mouse_handle);
ESP_ERROR_CHECK( hid_host_uninstall_device(hid_device) );
hid_device_connected = false;
}
ESP_LOGI(TAG, "Uninitializing USB");
ESP_ERROR_CHECK( hid_host_uninstall() );
wait_for_event(READY_TO_UNINSTALL, portMAX_DELAY);
ESP_ERROR_CHECK( usb_host_uninstall() );
vTaskDelete(usb_events_task_handle);
vEventGroupDelete(usb_flags);
ESP_LOGI(TAG, "Done");
} }

View File

@ -0,0 +1,4 @@
## IDF Component Manager Manifest File
dependencies:
idf: ">=4.4"
usb_host_hid: "1.0.0"