hid_host_example.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  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 "freertos/queue.h"
  14. #include "esp_err.h"
  15. #include "esp_log.h"
  16. #include "usb/usb_host.h"
  17. #include "errno.h"
  18. #include "driver/gpio.h"
  19. #include "usb/hid_host.h"
  20. #include "usb/hid_usage_keyboard.h"
  21. #include "usb/hid_usage_mouse.h"
  22. /* GPIO Pin number for quit from example logic */
  23. #define APP_QUIT_PIN GPIO_NUM_0
  24. static const char *TAG = "example";
  25. QueueHandle_t app_event_queue = NULL;
  26. /**
  27. * @brief APP event group
  28. *
  29. * Application logic can be different. There is a one among other ways to distingiush the
  30. * event by application event group.
  31. * In this example we have two event groups:
  32. * APP_EVENT - General event, which is APP_QUIT_PIN press event (Generally, it is IO0).
  33. * APP_EVENT_HID_HOST - HID Host Driver event, such as device connection/disconnection or input report.
  34. */
  35. typedef enum {
  36. APP_EVENT = 0,
  37. APP_EVENT_HID_HOST
  38. } app_event_group_t;
  39. /**
  40. * @brief APP event queue
  41. *
  42. * This event is used for delivering the HID Host event from callback to a task.
  43. */
  44. typedef struct {
  45. app_event_group_t event_group;
  46. /* HID Host - Device related info */
  47. struct {
  48. hid_host_device_handle_t handle;
  49. hid_host_driver_event_t event;
  50. void *arg;
  51. } hid_host_device;
  52. } app_event_queue_t;
  53. /**
  54. * @brief HID Protocol string names
  55. */
  56. static const char *hid_proto_name_str[] = {
  57. "NONE",
  58. "KEYBOARD",
  59. "MOUSE"
  60. };
  61. /**
  62. * @brief Key event
  63. */
  64. typedef struct {
  65. enum key_state {
  66. KEY_STATE_PRESSED = 0x00,
  67. KEY_STATE_RELEASED = 0x01
  68. } state;
  69. uint8_t modifier;
  70. uint8_t key_code;
  71. } key_event_t;
  72. /* Main char symbol for ENTER key */
  73. #define KEYBOARD_ENTER_MAIN_CHAR '\r'
  74. /* When set to 1 pressing ENTER will be extending with LineFeed during serial debug output */
  75. #define KEYBOARD_ENTER_LF_EXTEND 1
  76. /**
  77. * @brief Scancode to ascii table
  78. */
  79. const uint8_t keycode2ascii [57][2] = {
  80. {0, 0}, /* HID_KEY_NO_PRESS */
  81. {0, 0}, /* HID_KEY_ROLLOVER */
  82. {0, 0}, /* HID_KEY_POST_FAIL */
  83. {0, 0}, /* HID_KEY_ERROR_UNDEFINED */
  84. {'a', 'A'}, /* HID_KEY_A */
  85. {'b', 'B'}, /* HID_KEY_B */
  86. {'c', 'C'}, /* HID_KEY_C */
  87. {'d', 'D'}, /* HID_KEY_D */
  88. {'e', 'E'}, /* HID_KEY_E */
  89. {'f', 'F'}, /* HID_KEY_F */
  90. {'g', 'G'}, /* HID_KEY_G */
  91. {'h', 'H'}, /* HID_KEY_H */
  92. {'i', 'I'}, /* HID_KEY_I */
  93. {'j', 'J'}, /* HID_KEY_J */
  94. {'k', 'K'}, /* HID_KEY_K */
  95. {'l', 'L'}, /* HID_KEY_L */
  96. {'m', 'M'}, /* HID_KEY_M */
  97. {'n', 'N'}, /* HID_KEY_N */
  98. {'o', 'O'}, /* HID_KEY_O */
  99. {'p', 'P'}, /* HID_KEY_P */
  100. {'q', 'Q'}, /* HID_KEY_Q */
  101. {'r', 'R'}, /* HID_KEY_R */
  102. {'s', 'S'}, /* HID_KEY_S */
  103. {'t', 'T'}, /* HID_KEY_T */
  104. {'u', 'U'}, /* HID_KEY_U */
  105. {'v', 'V'}, /* HID_KEY_V */
  106. {'w', 'W'}, /* HID_KEY_W */
  107. {'x', 'X'}, /* HID_KEY_X */
  108. {'y', 'Y'}, /* HID_KEY_Y */
  109. {'z', 'Z'}, /* HID_KEY_Z */
  110. {'1', '!'}, /* HID_KEY_1 */
  111. {'2', '@'}, /* HID_KEY_2 */
  112. {'3', '#'}, /* HID_KEY_3 */
  113. {'4', '$'}, /* HID_KEY_4 */
  114. {'5', '%'}, /* HID_KEY_5 */
  115. {'6', '^'}, /* HID_KEY_6 */
  116. {'7', '&'}, /* HID_KEY_7 */
  117. {'8', '*'}, /* HID_KEY_8 */
  118. {'9', '('}, /* HID_KEY_9 */
  119. {'0', ')'}, /* HID_KEY_0 */
  120. {KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER */
  121. {0, 0}, /* HID_KEY_ESC */
  122. {'\b', 0}, /* HID_KEY_DEL */
  123. {0, 0}, /* HID_KEY_TAB */
  124. {' ', ' '}, /* HID_KEY_SPACE */
  125. {'-', '_'}, /* HID_KEY_MINUS */
  126. {'=', '+'}, /* HID_KEY_EQUAL */
  127. {'[', '{'}, /* HID_KEY_OPEN_BRACKET */
  128. {']', '}'}, /* HID_KEY_CLOSE_BRACKET */
  129. {'\\', '|'}, /* HID_KEY_BACK_SLASH */
  130. {'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
  131. {';', ':'}, /* HID_KEY_COLON */
  132. {'\'', '"'}, /* HID_KEY_QUOTE */
  133. {'`', '~'}, /* HID_KEY_TILDE */
  134. {',', '<'}, /* HID_KEY_LESS */
  135. {'.', '>'}, /* HID_KEY_GREATER */
  136. {'/', '?'} /* HID_KEY_SLASH */
  137. };
  138. /**
  139. * @brief Makes new line depending on report output protocol type
  140. *
  141. * @param[in] proto Current protocol to output
  142. */
  143. static void hid_print_new_device_report_header(hid_protocol_t proto)
  144. {
  145. static hid_protocol_t prev_proto_output = -1;
  146. if (prev_proto_output != proto) {
  147. prev_proto_output = proto;
  148. printf("\r\n");
  149. if (proto == HID_PROTOCOL_MOUSE) {
  150. printf("Mouse\r\n");
  151. } else if (proto == HID_PROTOCOL_KEYBOARD) {
  152. printf("Keyboard\r\n");
  153. } else {
  154. printf("Generic\r\n");
  155. }
  156. fflush(stdout);
  157. }
  158. }
  159. /**
  160. * @brief HID Keyboard modifier verification for capitalization application (right or left shift)
  161. *
  162. * @param[in] modifier
  163. * @return true Modifier was pressed (left or right shift)
  164. * @return false Modifier was not pressed (left or right shift)
  165. *
  166. */
  167. static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier)
  168. {
  169. if (((modifier & HID_LEFT_SHIFT) == HID_LEFT_SHIFT) ||
  170. ((modifier & HID_RIGHT_SHIFT) == HID_RIGHT_SHIFT)) {
  171. return true;
  172. }
  173. return false;
  174. }
  175. /**
  176. * @brief HID Keyboard get char symbol from key code
  177. *
  178. * @param[in] modifier Keyboard modifier data
  179. * @param[in] key_code Keyboard key code
  180. * @param[in] key_char Pointer to key char data
  181. *
  182. * @return true Key scancode converted successfully
  183. * @return false Key scancode unknown
  184. */
  185. static inline bool hid_keyboard_get_char(uint8_t modifier,
  186. uint8_t key_code,
  187. unsigned char *key_char)
  188. {
  189. uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0;
  190. if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_SLASH)) {
  191. *key_char = keycode2ascii[key_code][mod];
  192. } else {
  193. // All other key pressed
  194. return false;
  195. }
  196. return true;
  197. }
  198. /**
  199. * @brief HID Keyboard print char symbol
  200. *
  201. * @param[in] key_char Keyboard char to stdout
  202. */
  203. static inline void hid_keyboard_print_char(unsigned int key_char)
  204. {
  205. if (!!key_char) {
  206. putchar(key_char);
  207. #if (KEYBOARD_ENTER_LF_EXTEND)
  208. if (KEYBOARD_ENTER_MAIN_CHAR == key_char) {
  209. putchar('\n');
  210. }
  211. #endif // KEYBOARD_ENTER_LF_EXTEND
  212. fflush(stdout);
  213. }
  214. }
  215. /**
  216. * @brief Key Event. Key event with the key code, state and modifier.
  217. *
  218. * @param[in] key_event Pointer to Key Event structure
  219. *
  220. */
  221. static void key_event_callback(key_event_t *key_event)
  222. {
  223. unsigned char key_char;
  224. hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD);
  225. if (KEY_STATE_PRESSED == key_event->state) {
  226. if (hid_keyboard_get_char(key_event->modifier,
  227. key_event->key_code, &key_char)) {
  228. hid_keyboard_print_char(key_char);
  229. }
  230. }
  231. }
  232. /**
  233. * @brief Key buffer scan code search.
  234. *
  235. * @param[in] src Pointer to source buffer where to search
  236. * @param[in] key Key scancode to search
  237. * @param[in] length Size of the source buffer
  238. */
  239. static inline bool key_found(const uint8_t *const src,
  240. uint8_t key,
  241. unsigned int length)
  242. {
  243. for (unsigned int i = 0; i < length; i++) {
  244. if (src[i] == key) {
  245. return true;
  246. }
  247. }
  248. return false;
  249. }
  250. /**
  251. * @brief USB HID Host Keyboard Interface report callback handler
  252. *
  253. * @param[in] data Pointer to input report data buffer
  254. * @param[in] length Length of input report data buffer
  255. */
  256. static void hid_host_keyboard_report_callback(const uint8_t *const data, const int length)
  257. {
  258. hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data;
  259. if (length < sizeof(hid_keyboard_input_report_boot_t)) {
  260. return;
  261. }
  262. static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = { 0 };
  263. key_event_t key_event;
  264. for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
  265. // key has been released verification
  266. if (prev_keys[i] > HID_KEY_ERROR_UNDEFINED &&
  267. !key_found(kb_report->key, prev_keys[i], HID_KEYBOARD_KEY_MAX)) {
  268. key_event.key_code = prev_keys[i];
  269. key_event.modifier = 0;
  270. key_event.state = KEY_STATE_RELEASED;
  271. key_event_callback(&key_event);
  272. }
  273. // key has been pressed verification
  274. if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED &&
  275. !key_found(prev_keys, kb_report->key[i], HID_KEYBOARD_KEY_MAX)) {
  276. key_event.key_code = kb_report->key[i];
  277. key_event.modifier = kb_report->modifier.val;
  278. key_event.state = KEY_STATE_PRESSED;
  279. key_event_callback(&key_event);
  280. }
  281. }
  282. memcpy(prev_keys, &kb_report->key, HID_KEYBOARD_KEY_MAX);
  283. }
  284. /**
  285. * @brief USB HID Host Mouse Interface report callback handler
  286. *
  287. * @param[in] data Pointer to input report data buffer
  288. * @param[in] length Length of input report data buffer
  289. */
  290. static void hid_host_mouse_report_callback(const uint8_t *const data, const int length)
  291. {
  292. hid_mouse_input_report_boot_t *mouse_report = (hid_mouse_input_report_boot_t *)data;
  293. if (length < sizeof(hid_mouse_input_report_boot_t)) {
  294. return;
  295. }
  296. static int x_pos = 0;
  297. static int y_pos = 0;
  298. // Calculate absolute position from displacement
  299. x_pos += mouse_report->x_displacement;
  300. y_pos += mouse_report->y_displacement;
  301. hid_print_new_device_report_header(HID_PROTOCOL_MOUSE);
  302. printf("X: %06d\tY: %06d\t|%c|%c|\r",
  303. x_pos, y_pos,
  304. (mouse_report->buttons.button1 ? 'o' : ' '),
  305. (mouse_report->buttons.button2 ? 'o' : ' '));
  306. fflush(stdout);
  307. }
  308. /**
  309. * @brief USB HID Host Generic Interface report callback handler
  310. *
  311. * 'generic' means anything else than mouse or keyboard
  312. *
  313. * @param[in] data Pointer to input report data buffer
  314. * @param[in] length Length of input report data buffer
  315. */
  316. static void hid_host_generic_report_callback(const uint8_t *const data, const int length)
  317. {
  318. hid_print_new_device_report_header(HID_PROTOCOL_NONE);
  319. for (int i = 0; i < length; i++) {
  320. printf("%02X", data[i]);
  321. }
  322. putchar('\r');
  323. }
  324. /**
  325. * @brief USB HID Host interface callback
  326. *
  327. * @param[in] hid_device_handle HID Device handle
  328. * @param[in] event HID Host interface event
  329. * @param[in] arg Pointer to arguments, does not used
  330. */
  331. void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle,
  332. const hid_host_interface_event_t event,
  333. void *arg)
  334. {
  335. uint8_t data[64] = { 0 };
  336. size_t data_length = 0;
  337. hid_host_dev_params_t dev_params;
  338. ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));
  339. switch (event) {
  340. case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
  341. ESP_ERROR_CHECK(hid_host_device_get_raw_input_report_data(hid_device_handle,
  342. data,
  343. 64,
  344. &data_length));
  345. if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {
  346. if (HID_PROTOCOL_KEYBOARD == dev_params.proto) {
  347. hid_host_keyboard_report_callback(data, data_length);
  348. } else if (HID_PROTOCOL_MOUSE == dev_params.proto) {
  349. hid_host_mouse_report_callback(data, data_length);
  350. }
  351. } else {
  352. hid_host_generic_report_callback(data, data_length);
  353. }
  354. break;
  355. case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
  356. ESP_LOGI(TAG, "HID Device, protocol '%s' DISCONNECTED",
  357. hid_proto_name_str[dev_params.proto]);
  358. ESP_ERROR_CHECK(hid_host_device_close(hid_device_handle));
  359. break;
  360. case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
  361. ESP_LOGI(TAG, "HID Device, protocol '%s' TRANSFER_ERROR",
  362. hid_proto_name_str[dev_params.proto]);
  363. break;
  364. default:
  365. ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event",
  366. hid_proto_name_str[dev_params.proto]);
  367. break;
  368. }
  369. }
  370. /**
  371. * @brief USB HID Host Device event
  372. *
  373. * @param[in] hid_device_handle HID Device handle
  374. * @param[in] event HID Host Device event
  375. * @param[in] arg Pointer to arguments, does not used
  376. */
  377. void hid_host_device_event(hid_host_device_handle_t hid_device_handle,
  378. const hid_host_driver_event_t event,
  379. void *arg)
  380. {
  381. hid_host_dev_params_t dev_params;
  382. ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));
  383. switch (event) {
  384. case HID_HOST_DRIVER_EVENT_CONNECTED:
  385. ESP_LOGI(TAG, "HID Device, protocol '%s' CONNECTED",
  386. hid_proto_name_str[dev_params.proto]);
  387. const hid_host_device_config_t dev_config = {
  388. .callback = hid_host_interface_callback,
  389. .callback_arg = NULL
  390. };
  391. ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config));
  392. if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {
  393. ESP_ERROR_CHECK(hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT));
  394. if (HID_PROTOCOL_KEYBOARD == dev_params.proto) {
  395. ESP_ERROR_CHECK(hid_class_request_set_idle(hid_device_handle, 0, 0));
  396. }
  397. }
  398. ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle));
  399. break;
  400. default:
  401. break;
  402. }
  403. }
  404. /**
  405. * @brief Start USB Host install and handle common USB host library events while app pin not low
  406. *
  407. * @param[in] arg Not used
  408. */
  409. static void usb_lib_task(void *arg)
  410. {
  411. const usb_host_config_t host_config = {
  412. .skip_phy_setup = false,
  413. .intr_flags = ESP_INTR_FLAG_LEVEL1,
  414. };
  415. ESP_ERROR_CHECK(usb_host_install(&host_config));
  416. xTaskNotifyGive(arg);
  417. while (true) {
  418. uint32_t event_flags;
  419. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  420. // In this example, there is only one client registered
  421. // So, once we deregister the client, this call must succeed with ESP_OK
  422. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  423. ESP_ERROR_CHECK(usb_host_device_free_all());
  424. break;
  425. }
  426. }
  427. ESP_LOGI(TAG, "USB shutdown");
  428. // Clean up USB Host
  429. vTaskDelay(10); // Short delay to allow clients clean-up
  430. ESP_ERROR_CHECK(usb_host_uninstall());
  431. vTaskDelete(NULL);
  432. }
  433. /**
  434. * @brief BOOT button pressed callback
  435. *
  436. * Signal application to exit the HID Host task
  437. *
  438. * @param[in] arg Unused
  439. */
  440. static void gpio_isr_cb(void *arg)
  441. {
  442. BaseType_t xTaskWoken = pdFALSE;
  443. const app_event_queue_t evt_queue = {
  444. .event_group = APP_EVENT,
  445. };
  446. if (app_event_queue) {
  447. xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken);
  448. }
  449. if (xTaskWoken == pdTRUE) {
  450. portYIELD_FROM_ISR();
  451. }
  452. }
  453. /**
  454. * @brief HID Host Device callback
  455. *
  456. * Puts new HID Device event to the queue
  457. *
  458. * @param[in] hid_device_handle HID Device handle
  459. * @param[in] event HID Device event
  460. * @param[in] arg Not used
  461. */
  462. void hid_host_device_callback(hid_host_device_handle_t hid_device_handle,
  463. const hid_host_driver_event_t event,
  464. void *arg)
  465. {
  466. const app_event_queue_t evt_queue = {
  467. .event_group = APP_EVENT_HID_HOST,
  468. // HID Host Device related info
  469. .hid_host_device.handle = hid_device_handle,
  470. .hid_host_device.event = event,
  471. .hid_host_device.arg = arg
  472. };
  473. if (app_event_queue) {
  474. xQueueSend(app_event_queue, &evt_queue, 0);
  475. }
  476. }
  477. void app_main(void)
  478. {
  479. BaseType_t task_created;
  480. app_event_queue_t evt_queue;
  481. ESP_LOGI(TAG, "HID Host example");
  482. // Init BOOT button: Pressing the button simulates app request to exit
  483. // It will disconnect the USB device and uninstall the HID driver and USB Host Lib
  484. const gpio_config_t input_pin = {
  485. .pin_bit_mask = BIT64(APP_QUIT_PIN),
  486. .mode = GPIO_MODE_INPUT,
  487. .pull_up_en = GPIO_PULLUP_ENABLE,
  488. .intr_type = GPIO_INTR_NEGEDGE,
  489. };
  490. ESP_ERROR_CHECK(gpio_config(&input_pin));
  491. ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1));
  492. ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_isr_cb, NULL));
  493. /*
  494. * Create usb_lib_task to:
  495. * - initialize USB Host library
  496. * - Handle USB Host events while APP pin in in HIGH state
  497. */
  498. task_created = xTaskCreatePinnedToCore(usb_lib_task,
  499. "usb_events",
  500. 4096,
  501. xTaskGetCurrentTaskHandle(),
  502. 2, NULL, 0);
  503. assert(task_created == pdTRUE);
  504. // Wait for notification from usb_lib_task to proceed
  505. ulTaskNotifyTake(false, 1000);
  506. /*
  507. * HID host driver configuration
  508. * - create background task for handling low level event inside the HID driver
  509. * - provide the device callback to get new HID Device connection event
  510. */
  511. const hid_host_driver_config_t hid_host_driver_config = {
  512. .create_background_task = true,
  513. .task_priority = 5,
  514. .stack_size = 4096,
  515. .core_id = 0,
  516. .callback = hid_host_device_callback,
  517. .callback_arg = NULL
  518. };
  519. ESP_ERROR_CHECK(hid_host_install(&hid_host_driver_config));
  520. // Create queue
  521. app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t));
  522. ESP_LOGI(TAG, "Waiting for HID Device to be connected");
  523. while (1) {
  524. // Wait queue
  525. if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) {
  526. if (APP_EVENT == evt_queue.event_group) {
  527. // User pressed button
  528. usb_host_lib_info_t lib_info;
  529. ESP_ERROR_CHECK(usb_host_lib_info(&lib_info));
  530. if (lib_info.num_devices == 0) {
  531. // End while cycle
  532. break;
  533. } else {
  534. ESP_LOGW(TAG, "To shutdown example, remove all USB devices and press button again.");
  535. // Keep polling
  536. }
  537. }
  538. if (APP_EVENT_HID_HOST == evt_queue.event_group) {
  539. hid_host_device_event(evt_queue.hid_host_device.handle,
  540. evt_queue.hid_host_device.event,
  541. evt_queue.hid_host_device.arg);
  542. }
  543. }
  544. }
  545. ESP_LOGI(TAG, "HID Driver uninstall");
  546. ESP_ERROR_CHECK(hid_host_uninstall());
  547. gpio_isr_handler_remove(APP_QUIT_PIN);
  548. xQueueReset(app_event_queue);
  549. vQueueDelete(app_event_queue);
  550. }