hid_host_example.c 18 KB

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