hid_host_example.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. */
  6. #include <stdio.h>
  7. #include <stdbool.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #include "freertos/FreeRTOS.h"
  11. #include "freertos/task.h"
  12. #include "freertos/event_groups.h"
  13. #include "esp_err.h"
  14. #include "esp_log.h"
  15. #include "usb/usb_host.h"
  16. #include "errno.h"
  17. #include "driver/gpio.h"
  18. #include "hid_host.h"
  19. #include "hid_usage_keyboard.h"
  20. #include "hid_usage_mouse.h"
  21. #define APP_QUIT_PIN GPIO_NUM_0
  22. #define APP_QUIT_PIN_POLL_MS 500
  23. #define READY_TO_UNINSTALL (HOST_NO_CLIENT | HOST_ALL_FREE)
  24. /* Main char symbol for ENTER key */
  25. #define KEYBOARD_ENTER_MAIN_CHAR '\r'
  26. /* When set to 1 pressing ENTER will be extending with LineFeed during serial debug output */
  27. #define KEYBOARD_ENTER_LF_EXTEND 1
  28. /**
  29. * @brief Application Event from USB Host driver
  30. *
  31. */
  32. typedef enum {
  33. HOST_NO_CLIENT = 0x1,
  34. HOST_ALL_FREE = 0x2,
  35. DEVICE_CONNECTED = 0x4,
  36. DEVICE_DISCONNECTED = 0x8,
  37. DEVICE_ADDRESS_MASK = 0xFF0,
  38. } app_event_t;
  39. /**
  40. * @brief Key event
  41. *
  42. */
  43. typedef struct {
  44. enum key_state {
  45. KEY_STATE_PRESSED = 0x00,
  46. KEY_STATE_RELEASED = 0x01
  47. } state;
  48. uint8_t modifier;
  49. uint8_t key_code;
  50. } key_event_t;
  51. #define USB_EVENTS_TO_WAIT (DEVICE_CONNECTED | DEVICE_ADDRESS_MASK | DEVICE_DISCONNECTED)
  52. static const char *TAG = "example";
  53. static EventGroupHandle_t usb_flags;
  54. static bool hid_device_connected = false;
  55. hid_host_interface_handle_t keyboard_handle = NULL;
  56. hid_host_interface_handle_t mouse_handle = NULL;
  57. /**
  58. * @brief Scancode to ascii table
  59. */
  60. const uint8_t keycode2ascii [57][2] = {
  61. {0, 0}, /* HID_KEY_NO_PRESS */
  62. {0, 0}, /* HID_KEY_ROLLOVER */
  63. {0, 0}, /* HID_KEY_POST_FAIL */
  64. {0, 0}, /* HID_KEY_ERROR_UNDEFINED */
  65. {'a', 'A'}, /* HID_KEY_A */
  66. {'b', 'B'}, /* HID_KEY_B */
  67. {'c', 'C'}, /* HID_KEY_C */
  68. {'d', 'D'}, /* HID_KEY_D */
  69. {'e', 'E'}, /* HID_KEY_E */
  70. {'f', 'F'}, /* HID_KEY_F */
  71. {'g', 'G'}, /* HID_KEY_G */
  72. {'h', 'H'}, /* HID_KEY_H */
  73. {'i', 'I'}, /* HID_KEY_I */
  74. {'j', 'J'}, /* HID_KEY_J */
  75. {'k', 'K'}, /* HID_KEY_K */
  76. {'l', 'L'}, /* HID_KEY_L */
  77. {'m', 'M'}, /* HID_KEY_M */
  78. {'n', 'N'}, /* HID_KEY_N */
  79. {'o', 'O'}, /* HID_KEY_O */
  80. {'p', 'P'}, /* HID_KEY_P */
  81. {'q', 'Q'}, /* HID_KEY_Q */
  82. {'r', 'R'}, /* HID_KEY_R */
  83. {'s', 'S'}, /* HID_KEY_S */
  84. {'t', 'T'}, /* HID_KEY_T */
  85. {'u', 'U'}, /* HID_KEY_U */
  86. {'v', 'V'}, /* HID_KEY_V */
  87. {'w', 'W'}, /* HID_KEY_W */
  88. {'x', 'X'}, /* HID_KEY_X */
  89. {'y', 'Y'}, /* HID_KEY_Y */
  90. {'z', 'Z'}, /* HID_KEY_Z */
  91. {'1', '!'}, /* HID_KEY_1 */
  92. {'2', '@'}, /* HID_KEY_2 */
  93. {'3', '#'}, /* HID_KEY_3 */
  94. {'4', '$'}, /* HID_KEY_4 */
  95. {'5', '%'}, /* HID_KEY_5 */
  96. {'6', '^'}, /* HID_KEY_6 */
  97. {'7', '&'}, /* HID_KEY_7 */
  98. {'8', '*'}, /* HID_KEY_8 */
  99. {'9', '('}, /* HID_KEY_9 */
  100. {'0', ')'}, /* HID_KEY_0 */
  101. {KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER */
  102. {0, 0}, /* HID_KEY_ESC */
  103. {'\b', 0}, /* HID_KEY_DEL */
  104. {0, 0}, /* HID_KEY_TAB */
  105. {' ', ' '}, /* HID_KEY_SPACE */
  106. {'-', '_'}, /* HID_KEY_MINUS */
  107. {'=', '+'}, /* HID_KEY_EQUAL */
  108. {'[', '{'}, /* HID_KEY_OPEN_BRACKET */
  109. {']', '}'}, /* HID_KEY_CLOSE_BRACKET */
  110. {'\\', '|'}, /* HID_KEY_BACK_SLASH */
  111. {'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
  112. {';', ':'}, /* HID_KEY_COLON */
  113. {'\'', '"'}, /* HID_KEY_QUOTE */
  114. {'`', '~'}, /* HID_KEY_TILDE */
  115. {',', '<'}, /* HID_KEY_LESS */
  116. {'.', '>'}, /* HID_KEY_GREATER */
  117. {'/', '?'} /* HID_KEY_SLASH */
  118. };
  119. /**
  120. * @brief Makes new line depending on report output protocol type
  121. *
  122. * @param[in] proto Current protocol to output
  123. */
  124. static void hid_print_new_device_report_header(hid_protocol_t proto)
  125. {
  126. static hid_protocol_t prev_proto_output = HID_PROTOCOL_NONE;
  127. if (prev_proto_output != proto) {
  128. prev_proto_output = proto;
  129. printf("\r\n");
  130. if (proto == HID_PROTOCOL_MOUSE) {
  131. printf("Mouse\r\n");
  132. }
  133. if (proto == HID_PROTOCOL_KEYBOARD) {
  134. printf("Keyboard\r\n");
  135. }
  136. fflush(stdout);
  137. }
  138. }
  139. /**
  140. * @brief HID Keyboard modifier verification for capitalization application (right or left shift)
  141. *
  142. * @param[in] modifier
  143. * @return true Modifier was pressed (left or right shift)
  144. * @return false Modifier was not pressed (left or right shift)
  145. *
  146. */
  147. static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier)
  148. {
  149. if ((modifier && HID_LEFT_SHIFT) ||
  150. (modifier && HID_RIGHT_SHIFT)) {
  151. return true;
  152. }
  153. return false;
  154. }
  155. /**
  156. * @brief HID Keyboard get char symbol from key code
  157. *
  158. * @param[in] modifier Keyboard modifier data
  159. * @param[in] key_code Keyboard key code
  160. * @param[in] key_char Pointer to key char data
  161. *
  162. * @return true Key scancode converted successfully
  163. * @return false Key scancode unknown
  164. */
  165. static inline bool hid_keyboard_get_char(uint8_t modifier,
  166. uint8_t key_code,
  167. unsigned char *key_char)
  168. {
  169. uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0;
  170. if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_SLASH)) {
  171. *key_char = keycode2ascii[key_code][mod];
  172. } else {
  173. // All other key pressed
  174. return false;
  175. }
  176. return true;
  177. }
  178. /**
  179. * @brief HID Keyboard print char symbol
  180. *
  181. * @param[in] key_char Keyboard char to stdout
  182. */
  183. static inline void hid_keyboard_print_char(unsigned int key_char)
  184. {
  185. if (!!key_char) {
  186. putchar(key_char);
  187. #if (KEYBOARD_ENTER_LF_EXTEND)
  188. if (KEYBOARD_ENTER_MAIN_CHAR == key_char) {
  189. putchar('\n');
  190. }
  191. #endif // KEYBOARD_ENTER_LF_EXTEND
  192. fflush(stdout);
  193. }
  194. }
  195. /**
  196. * @brief Key Event. Key event with the key code, state and modifier.
  197. *
  198. * @param[in] key_event Pointer to Key Event structure
  199. *
  200. */
  201. static void key_event_callback(key_event_t *key_event)
  202. {
  203. unsigned char key_char;
  204. hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD);
  205. if (KEY_STATE_PRESSED == key_event->state) {
  206. if (hid_keyboard_get_char(key_event->modifier,
  207. key_event->key_code, &key_char)) {
  208. hid_keyboard_print_char(key_char);
  209. }
  210. }
  211. }
  212. /**
  213. * @brief Key buffer scan code search.
  214. *
  215. * @param[in] src Pointer to source buffer where to search
  216. * @param[in] key Key scancode to search
  217. * @param[in] length Size of the source buffer
  218. */
  219. static inline bool key_found(const uint8_t *const src,
  220. uint8_t key,
  221. unsigned int length)
  222. {
  223. for (unsigned int i = 0; i < length; i++) {
  224. if (src[i] == key) {
  225. return true;
  226. }
  227. }
  228. return false;
  229. }
  230. /**
  231. * @brief USB HID Host Keyboard Interface report callback handler
  232. *
  233. * @param[in] data Pointer to input report data buffer
  234. * @param[in] length Length of input report data buffer
  235. */
  236. static void hid_host_keyboard_report_callback(const uint8_t *const data, const int length)
  237. {
  238. hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data;
  239. if (length < sizeof(hid_keyboard_input_report_boot_t)) {
  240. return;
  241. }
  242. static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = { 0 };
  243. key_event_t key_event;
  244. for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
  245. // key has been released verification
  246. if (prev_keys[i] > HID_KEY_ERROR_UNDEFINED &&
  247. !key_found(kb_report->key, prev_keys[i], HID_KEYBOARD_KEY_MAX)) {
  248. key_event.key_code = prev_keys[i];
  249. key_event.modifier = 0;
  250. key_event.state = KEY_STATE_RELEASED;
  251. key_event_callback(&key_event);
  252. }
  253. // key has been pressed verification
  254. if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED &&
  255. !key_found(prev_keys, kb_report->key[i], HID_KEYBOARD_KEY_MAX)) {
  256. key_event.key_code = kb_report->key[i];
  257. key_event.modifier = kb_report->modifier.val;
  258. key_event.state = KEY_STATE_PRESSED;
  259. key_event_callback(&key_event);
  260. }
  261. }
  262. memcpy(prev_keys, &kb_report->key, HID_KEYBOARD_KEY_MAX);
  263. }
  264. /**
  265. * @brief USB HID Host Mouse Interface report callback handler
  266. *
  267. * @param[in] data Pointer to input report data buffer
  268. * @param[in] length Length of input report data buffer
  269. */
  270. static void hid_host_mouse_report_callback(const uint8_t *const data, const int length)
  271. {
  272. hid_mouse_input_report_boot_t *mouse_report = (hid_mouse_input_report_boot_t *)data;
  273. if (length < sizeof(hid_mouse_input_report_boot_t)) {
  274. return;
  275. }
  276. static int x_pos = 0;
  277. static int y_pos = 0;
  278. // Calculate absolute position from displacement
  279. x_pos += mouse_report->x_displacement;
  280. y_pos += mouse_report->y_displacement;
  281. hid_print_new_device_report_header(HID_PROTOCOL_MOUSE);
  282. printf("X: %06d\tY: %06d\t|%c|%c|\r",
  283. x_pos, y_pos,
  284. (mouse_report->buttons.button1 ? 'o' : ' '),
  285. (mouse_report->buttons.button2 ? 'o' : ' '));
  286. fflush(stdout);
  287. }
  288. /**
  289. * @brief USB HID Host event callback. Handle such event as device connection and removing
  290. *
  291. * @param[in] event HID device event
  292. * @param[in] arg Pointer to arguments, does not used
  293. */
  294. static void hid_host_event_callback(const hid_host_event_t *event, void *arg)
  295. {
  296. if (event->event == HID_DEVICE_CONNECTED) {
  297. // Obtained USB device address is placed after application events
  298. xEventGroupSetBits(usb_flags, DEVICE_CONNECTED | (event->device.address << 4));
  299. } else if (event->event == HID_DEVICE_DISCONNECTED) {
  300. xEventGroupSetBits(usb_flags, DEVICE_DISCONNECTED);
  301. }
  302. }
  303. /**
  304. * @brief USB HID Host interface callback
  305. *
  306. * @param[in] event HID interface event
  307. * @param[in] arg Pointer to arguments, does not used
  308. */
  309. static void hid_host_interface_event_callback(const hid_host_interface_event_t *event, void *arg)
  310. {
  311. switch (event->event) {
  312. case HID_DEVICE_INTERFACE_INIT:
  313. ESP_LOGI(TAG, "Interface number %d, protocol %s",
  314. event->interface.num,
  315. (event->interface.proto == HID_PROTOCOL_KEYBOARD)
  316. ? "Keyboard"
  317. : "Mouse");
  318. if (event->interface.proto == HID_PROTOCOL_KEYBOARD) {
  319. const hid_host_interface_config_t hid_keyboard_config = {
  320. .proto = HID_PROTOCOL_KEYBOARD,
  321. .callback = hid_host_keyboard_report_callback,
  322. };
  323. hid_host_claim_interface(&hid_keyboard_config, &keyboard_handle);
  324. }
  325. if (event->interface.proto == HID_PROTOCOL_MOUSE) {
  326. const hid_host_interface_config_t hid_mouse_config = {
  327. .proto = HID_PROTOCOL_MOUSE,
  328. .callback = hid_host_mouse_report_callback,
  329. };
  330. hid_host_claim_interface(&hid_mouse_config, &mouse_handle);
  331. }
  332. break;
  333. case HID_DEVICE_INTERFACE_TRANSFER_ERROR:
  334. ESP_LOGD(TAG, "Interface number %d, transfer error",
  335. event->interface.num);
  336. break;
  337. case HID_DEVICE_INTERFACE_CLAIM:
  338. case HID_DEVICE_INTERFACE_RELEASE:
  339. // ... do nothing here for now
  340. break;
  341. default:
  342. ESP_LOGI(TAG, "%s Unhandled event %X, Interface number %d",
  343. __FUNCTION__,
  344. event->event,
  345. event->interface.num);
  346. break;
  347. }
  348. }
  349. /**
  350. * @brief Handle common USB host library events
  351. *
  352. * @param[in] args Pointer to arguments, does not used
  353. */
  354. static void handle_usb_events(void *args)
  355. {
  356. while (1) {
  357. uint32_t event_flags;
  358. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  359. // Release devices once all clients has deregistered
  360. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  361. usb_host_device_free_all();
  362. xEventGroupSetBits(usb_flags, HOST_NO_CLIENT);
  363. }
  364. // Give ready_to_uninstall_usb semaphore to indicate that USB Host library
  365. // can be deinitialized, and terminate this task.
  366. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
  367. xEventGroupSetBits(usb_flags, HOST_ALL_FREE);
  368. }
  369. }
  370. vTaskDelete(NULL);
  371. }
  372. static bool wait_for_event(EventBits_t event, TickType_t timeout)
  373. {
  374. return xEventGroupWaitBits(usb_flags, event, pdTRUE, pdTRUE, timeout) & event;
  375. }
  376. void app_main(void)
  377. {
  378. TaskHandle_t usb_events_task_handle;
  379. hid_host_device_handle_t hid_device;
  380. BaseType_t task_created;
  381. const gpio_config_t input_pin = {
  382. .pin_bit_mask = BIT64(APP_QUIT_PIN),
  383. .mode = GPIO_MODE_INPUT,
  384. .pull_up_en = GPIO_PULLUP_ENABLE,
  385. };
  386. ESP_ERROR_CHECK( gpio_config(&input_pin) );
  387. ESP_LOGI(TAG, "HID HOST example");
  388. usb_flags = xEventGroupCreate();
  389. assert(usb_flags);
  390. const usb_host_config_t host_config = {
  391. .skip_phy_setup = false,
  392. .intr_flags = ESP_INTR_FLAG_LEVEL1
  393. };
  394. ESP_ERROR_CHECK( usb_host_install(&host_config) );
  395. task_created = xTaskCreate(handle_usb_events, "usb_events", 4096, NULL, 2, &usb_events_task_handle);
  396. assert(task_created);
  397. // hid host driver config
  398. const hid_host_driver_config_t hid_host_config = {
  399. .create_background_task = true,
  400. .task_priority = 5,
  401. .stack_size = 4096,
  402. .core_id = 0,
  403. .callback = hid_host_event_callback,
  404. .callback_arg = NULL
  405. };
  406. ESP_ERROR_CHECK( hid_host_install(&hid_host_config) );
  407. do {
  408. EventBits_t event = xEventGroupWaitBits(usb_flags, USB_EVENTS_TO_WAIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(APP_QUIT_PIN_POLL_MS));
  409. if (event & DEVICE_CONNECTED) {
  410. xEventGroupClearBits(usb_flags, DEVICE_CONNECTED);
  411. hid_device_connected = true;
  412. }
  413. if (event & DEVICE_ADDRESS_MASK) {
  414. xEventGroupClearBits(usb_flags, DEVICE_ADDRESS_MASK);
  415. const hid_host_device_config_t hid_host_device_config = {
  416. .dev_addr = (event & DEVICE_ADDRESS_MASK) >> 4,
  417. .iface_event_cb = hid_host_interface_event_callback,
  418. .iface_event_arg = NULL,
  419. };
  420. ESP_ERROR_CHECK( hid_host_install_device(&hid_host_device_config, &hid_device) );
  421. }
  422. if (event & DEVICE_DISCONNECTED) {
  423. xEventGroupClearBits(usb_flags, DEVICE_DISCONNECTED);
  424. hid_host_release_interface(keyboard_handle);
  425. hid_host_release_interface(mouse_handle);
  426. ESP_ERROR_CHECK( hid_host_uninstall_device(hid_device) );
  427. hid_device_connected = false;
  428. }
  429. } while (gpio_get_level(APP_QUIT_PIN) != 0);
  430. if (hid_device_connected) {
  431. ESP_LOGI(TAG, "Uninitializing HID Device");
  432. hid_host_release_interface(keyboard_handle);
  433. hid_host_release_interface(mouse_handle);
  434. ESP_ERROR_CHECK( hid_host_uninstall_device(hid_device) );
  435. hid_device_connected = false;
  436. }
  437. ESP_LOGI(TAG, "Uninitializing USB");
  438. ESP_ERROR_CHECK( hid_host_uninstall() );
  439. wait_for_event(READY_TO_UNINSTALL, portMAX_DELAY);
  440. ESP_ERROR_CHECK( usb_host_uninstall() );
  441. vTaskDelete(usb_events_task_handle);
  442. vEventGroupDelete(usb_flags);
  443. ESP_LOGI(TAG, "Done");
  444. }