hid_host_example.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdio.h>
  7. #include <stdbool.h>
  8. #include <unistd.h>
  9. #include "freertos/FreeRTOS.h"
  10. #include "freertos/task.h"
  11. #include "freertos/event_groups.h"
  12. #include "esp_err.h"
  13. #include "esp_log.h"
  14. #include "usb/usb_host.h"
  15. #include "errno.h"
  16. #include "driver/gpio.h"
  17. #include "hid_host.h"
  18. #include "hid_usage_keyboard.h"
  19. #include "hid_usage_mouse.h"
  20. #define APP_QUIT_PIN GPIO_NUM_0
  21. #define APP_QUIT_PIN_POLL_MS 500
  22. #define READY_TO_UNINSTALL (HOST_NO_CLIENT | HOST_ALL_FREE)
  23. typedef enum {
  24. HOST_NO_CLIENT = 0x1,
  25. HOST_ALL_FREE = 0x2,
  26. DEVICE_CONNECTED = 0x4,
  27. DEVICE_DISCONNECTED = 0x8,
  28. DEVICE_ADDRESS_MASK = 0xFF0,
  29. } app_event_t;
  30. #define USB_EVENTS_TO_WAIT (DEVICE_CONNECTED | DEVICE_ADDRESS_MASK | DEVICE_DISCONNECTED)
  31. static const char *TAG = "example";
  32. static EventGroupHandle_t usb_flags;
  33. static bool hid_device_connected = false;
  34. hid_host_interface_handle_t keyboard_handle = NULL;
  35. hid_host_interface_handle_t mouse_handle = NULL;
  36. const char *modifier_char_name[8] = {
  37. "LEFT_CONTROL",
  38. "LEFT_SHIFT",
  39. "LEFT_ALT",
  40. "LEFT_GUI",
  41. "RIGHT_CONTROL",
  42. "RIGHT_SHIFT",
  43. "RIGHT_ALT",
  44. "RIGHT_GUI"
  45. };
  46. /**
  47. * @brief Makes new line depending on report output protocol type
  48. *
  49. * @param[in] proto Current protocol to output
  50. */
  51. static void hid_trigger_new_line_output(hid_protocol_t proto)
  52. {
  53. static hid_protocol_t prev_proto_output = HID_PROTOCOL_NONE;
  54. if (prev_proto_output != proto) {
  55. prev_proto_output = proto;
  56. printf("\r\n");
  57. fflush(stdout);
  58. }
  59. }
  60. /**
  61. * @brief HID Keyboard modifier verification function. Verify and print debug information about modifier has been pressed
  62. *
  63. * @param[in] modifier
  64. */
  65. static inline void hid_keyboard_modifier_pressed(uint8_t modifier)
  66. {
  67. // verify bit mask
  68. for (uint8_t i = 0; i < (sizeof(uint8_t) << 3); i++) {
  69. if ((modifier >> i) & 0x01) {
  70. ESP_LOGD(TAG, "Modifier Pressed: %s", modifier_char_name[i]);
  71. }
  72. }
  73. }
  74. /**
  75. * @brief HID Keyboard modifier verification for capitalization application (right or left shift)
  76. *
  77. * @param[in] modifier
  78. * @return true Modifier was pressed (left or right shift)
  79. * @return false Modifier was not pressed (left or right shift)
  80. *
  81. */
  82. static inline bool hid_keyboard_is_modifier_capital(uint8_t modifier)
  83. {
  84. if ((modifier && HID_LEFT_SHIFT) ||
  85. (modifier && HID_RIGHT_SHIFT)) {
  86. return true;
  87. }
  88. return false;
  89. }
  90. /**
  91. * @brief HID Keyboard get char symbol from key code
  92. *
  93. * @param[in] modifier Keyboard modifier data
  94. * @param[in] key_code Keyboard key code
  95. */
  96. static inline char hid_keyboard_get_char(uint8_t modifier, uint8_t key_code)
  97. {
  98. uint8_t key_char = (hid_keyboard_is_modifier_capital(modifier)) ? 'A' : 'a';
  99. // Handle only char key pressed
  100. if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_Z)) {
  101. key_char += (key_code - HID_KEY_A);
  102. } else if ((key_code >= HID_KEY_1) && (key_code <= HID_KEY_9)) {
  103. key_char = '1' + (key_code - HID_KEY_1);
  104. } else if (key_code == HID_KEY_0) {
  105. key_char = '0';
  106. } else {
  107. // All other key pressed
  108. key_char = 0x00;
  109. }
  110. return key_char;
  111. }
  112. /**
  113. * @brief USB HID Host Keyboard Interface report callback handler
  114. *
  115. * @param[in] data Pointer to input report data buffer
  116. * @param[in] length Length of input report data buffer
  117. */
  118. static void hid_host_keyboard_report_callback(const uint8_t *const data, const int length)
  119. {
  120. bool keys_state_changed = false;
  121. hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data;
  122. if (kb_report->modifier.val != 0) {
  123. hid_keyboard_modifier_pressed(kb_report->modifier.val);
  124. }
  125. static uint8_t keys[HID_KEYBOARD_KEY_MAX] = { 0 };
  126. for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
  127. if (kb_report->key[i] != keys[i]) {
  128. keys_state_changed = true;
  129. if (kb_report->key[i] != 0) {
  130. keys[i] = hid_keyboard_get_char(kb_report->modifier.val, kb_report->key[i]);
  131. } else {
  132. keys[i] = 0x00;
  133. }
  134. }
  135. }
  136. if (keys_state_changed) {
  137. hid_trigger_new_line_output(HID_PROTOCOL_KEYBOARD);
  138. printf("|");
  139. for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
  140. printf("%c|", keys[i] ? keys[i] : ' ');
  141. }
  142. printf("\r");
  143. fflush(stdout);
  144. }
  145. }
  146. /**
  147. * @brief USB HID Host Mouse Interface report callback handler
  148. *
  149. * @param[in] data Pointer to input report data buffer
  150. * @param[in] length Length of input report data buffer
  151. */
  152. static void hid_host_mouse_report_callback(const uint8_t *const data, const int length)
  153. {
  154. hid_mouse_input_report_boot_t *mouse_report = (hid_mouse_input_report_boot_t *)data;
  155. // First 3 bytes are mandated by HID specification
  156. if (length < sizeof(hid_mouse_input_report_boot_t)) {
  157. ESP_LOGE(TAG, "Mouse Boot report length (%d) error", length);
  158. return;
  159. }
  160. static int x_pos = 0;
  161. static int y_pos = 0;
  162. // Calculate absolute position from displacement
  163. x_pos += mouse_report->x_displacement;
  164. y_pos += mouse_report->y_displacement;
  165. hid_trigger_new_line_output(HID_PROTOCOL_MOUSE);
  166. printf("X: %06d\tY: %06d\t|%c|%c|\r",
  167. x_pos, y_pos,
  168. (mouse_report->buttons.button1 ? 'o' : ' '),
  169. (mouse_report->buttons.button2 ? 'o' : ' '));
  170. fflush(stdout);
  171. }
  172. /**
  173. * @brief USB HID Host event callback. Handle such event as device connection and removing
  174. *
  175. * @param[in] event HID device event
  176. * @param[in] arg Pointer to arguments, does not used
  177. */
  178. static void hid_host_event_callback(const hid_host_event_t *event, void *arg)
  179. {
  180. if (event->event == HID_DEVICE_CONNECTED) {
  181. // Obtained USB device address is placed after application events
  182. xEventGroupSetBits(usb_flags, DEVICE_CONNECTED | (event->device.address << 4));
  183. } else if (event->event == HID_DEVICE_DISCONNECTED) {
  184. xEventGroupSetBits(usb_flags, DEVICE_DISCONNECTED);
  185. }
  186. }
  187. /**
  188. * @brief USB HID Host interface callback
  189. *
  190. * @param[in] event HID interface event
  191. * @param[in] arg Pointer to arguments, does not used
  192. */
  193. static void hid_host_interface_event_callback(const hid_host_interface_event_t *event, void *arg)
  194. {
  195. switch (event->event) {
  196. case HID_DEVICE_INTERFACE_INIT:
  197. ESP_LOGI(TAG, "Interface number %d, protocol %s",
  198. event->interface.num,
  199. (event->interface.proto == HID_PROTOCOL_KEYBOARD)
  200. ? "Keyboard"
  201. : "Mouse");
  202. if (event->interface.proto == HID_PROTOCOL_KEYBOARD) {
  203. const hid_host_interface_config_t hid_keyboard_config = {
  204. .proto = HID_PROTOCOL_KEYBOARD,
  205. .callback = hid_host_keyboard_report_callback,
  206. };
  207. hid_host_claim_interface(&hid_keyboard_config, &keyboard_handle);
  208. }
  209. if (event->interface.proto == HID_PROTOCOL_MOUSE) {
  210. const hid_host_interface_config_t hid_mouse_config = {
  211. .proto = HID_PROTOCOL_MOUSE,
  212. .callback = hid_host_mouse_report_callback,
  213. };
  214. hid_host_claim_interface(&hid_mouse_config, &mouse_handle);
  215. }
  216. break;
  217. case HID_DEVICE_INTERFACE_TRANSFER_ERROR:
  218. ESP_LOGI(TAG, "Interface number %d, transfer error",
  219. event->interface.num);
  220. break;
  221. case HID_DEVICE_INTERFACE_CLAIM:
  222. case HID_DEVICE_INTERFACE_RELEASE:
  223. // ... do nothing here for now
  224. break;
  225. default:
  226. ESP_LOGI(TAG, "%s Unhandled event %X, Interface number %d",
  227. __FUNCTION__,
  228. event->event,
  229. event->interface.num);
  230. break;
  231. }
  232. }
  233. /**
  234. * @brief Handle common USB host library events
  235. *
  236. * @param[in] args Pointer to arguments, does not used
  237. */
  238. static void handle_usb_events(void *args)
  239. {
  240. while (1) {
  241. uint32_t event_flags;
  242. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  243. // Release devices once all clients has deregistered
  244. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  245. usb_host_device_free_all();
  246. xEventGroupSetBits(usb_flags, HOST_NO_CLIENT);
  247. }
  248. // Give ready_to_uninstall_usb semaphore to indicate that USB Host library
  249. // can be deinitialized, and terminate this task.
  250. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
  251. xEventGroupSetBits(usb_flags, HOST_ALL_FREE);
  252. }
  253. }
  254. vTaskDelete(NULL);
  255. }
  256. static bool wait_for_event(EventBits_t event, TickType_t timeout)
  257. {
  258. return xEventGroupWaitBits(usb_flags, event, pdTRUE, pdTRUE, timeout) & event;
  259. }
  260. void app_main(void)
  261. {
  262. TaskHandle_t usb_events_task_handle;
  263. hid_host_device_handle_t hid_device;
  264. BaseType_t task_created;
  265. const gpio_config_t input_pin = {
  266. .pin_bit_mask = BIT64(APP_QUIT_PIN),
  267. .mode = GPIO_MODE_INPUT,
  268. .pull_up_en = GPIO_PULLUP_ENABLE,
  269. };
  270. ESP_ERROR_CHECK( gpio_config(&input_pin) );
  271. ESP_LOGI(TAG, "HID HOST example");
  272. usb_flags = xEventGroupCreate();
  273. assert(usb_flags);
  274. const usb_host_config_t host_config = {
  275. .skip_phy_setup = false,
  276. .intr_flags = ESP_INTR_FLAG_LEVEL1
  277. };
  278. ESP_ERROR_CHECK( usb_host_install(&host_config) );
  279. task_created = xTaskCreate(handle_usb_events, "usb_events", 4096, NULL, 2, &usb_events_task_handle);
  280. assert(task_created);
  281. // hid host driver config
  282. const hid_host_driver_config_t hid_host_config = {
  283. .create_background_task = true,
  284. .task_priority = 5,
  285. .stack_size = 4096,
  286. .core_id = 0,
  287. .callback = hid_host_event_callback,
  288. .callback_arg = NULL
  289. };
  290. ESP_ERROR_CHECK( hid_host_install(&hid_host_config) );
  291. do {
  292. EventBits_t event = xEventGroupWaitBits(usb_flags, USB_EVENTS_TO_WAIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(APP_QUIT_PIN_POLL_MS));
  293. if (event & DEVICE_CONNECTED) {
  294. xEventGroupClearBits(usb_flags, DEVICE_CONNECTED);
  295. hid_device_connected = true;
  296. }
  297. if (event & DEVICE_ADDRESS_MASK) {
  298. xEventGroupClearBits(usb_flags, DEVICE_ADDRESS_MASK);
  299. const hid_host_device_config_t hid_host_device_config = {
  300. .dev_addr = (event & DEVICE_ADDRESS_MASK) >> 4,
  301. .iface_event_cb = hid_host_interface_event_callback,
  302. .iface_event_arg = NULL,
  303. };
  304. ESP_ERROR_CHECK( hid_host_install_device(&hid_host_device_config, &hid_device) );
  305. }
  306. if (event & DEVICE_DISCONNECTED) {
  307. xEventGroupClearBits(usb_flags, DEVICE_DISCONNECTED);
  308. hid_host_release_interface(keyboard_handle);
  309. hid_host_release_interface(mouse_handle);
  310. ESP_ERROR_CHECK( hid_host_uninstall_device(hid_device) );
  311. hid_device_connected = false;
  312. }
  313. } while (gpio_get_level(APP_QUIT_PIN) != 0);
  314. if (hid_device_connected) {
  315. ESP_LOGI(TAG, "Uninitializing HID Device");
  316. hid_host_release_interface(keyboard_handle);
  317. hid_host_release_interface(mouse_handle);
  318. ESP_ERROR_CHECK( hid_host_uninstall_device(hid_device) );
  319. hid_device_connected = false;
  320. }
  321. ESP_LOGI(TAG, "Uninitializing USB");
  322. ESP_ERROR_CHECK( hid_host_uninstall() );
  323. wait_for_event(READY_TO_UNINSTALL, portMAX_DELAY);
  324. ESP_ERROR_CHECK( usb_host_uninstall() );
  325. vTaskDelete(usb_events_task_handle);
  326. vEventGroupDelete(usb_flags);
  327. ESP_LOGI(TAG, "Done");
  328. }