Просмотр исходного кода

usbh: hid change key event handler

Roman Leonov 3 лет назад
Родитель
Сommit
bb1e1737d3

+ 19 - 17
examples/peripherals/usb/host/hid/README.md

@@ -36,31 +36,33 @@ idf.py -p PORT flash monitor
 The example serial output will be the following:
 
 ```
-I (195) example: HID HOST example
-I (35955) example: Interface number 0, protocol Mouse
-I (35955) example: Interface number 1, protocol Keyboard
-X: 000016       Y: -00083       | |o|
-|Q|T| | | | |
-X: 000016       Y: -00083       |o| |
-| |1|3|5| | |
+I (198) example: HID HOST example
+I (598) example: Interface number 0, protocol Mouse
+I (598) example: Interface number 1, protocol Keyboard
+
+Mouse
+X: 000883       Y: 000058       |o| |
+Keyboard
+qwertyuiop[]\asdfghjkl;'zxcvbnm,./
+Mouse
+X: 000883       Y: 000058       | |o|
 ```
 
 Where every keyboard key printed as char symbol if it is possible and a Hex value for any other key. 
 
-#### Keyboard report description
+#### Keyboard input data
+Keyboard input data starts with the word "Keyboard" and every pressed key is printed to the serial debug.
+Left or right Shift modifier is also supported. 
+
 ```
-|Q|T| | | | |
- | | | | | |
- | | | | | +----------------- Key 5 Char symbol
- | | | | +------------------- Key 4 Char symbol 
- | | | +--------------------- Key 3 Char symbol
- | | +----------------------- Key 2 Char symbol
- | +------------------------- Key 1 Char symbol
- +--------------------------- Key 0 Char symbol
+Keyboard
+Hello, ESP32 USB HID Keyboard is here!
 ```
 
-#### Mouse report description
+#### Mouse input data 
+Mouse input data starts with the word "Mouse" and has the following structure. 
 ```
+Mouse
 X: -00343   Y: 000183   | |o|
      |            |      | |
      |            |      | +- Right mouse button pressed status ("o" - pressed, " " - not pressed)

+ 9 - 5
examples/peripherals/usb/host/hid/components/hid/hid_host.c

@@ -982,16 +982,20 @@ esp_err_t hid_host_claim_interface(const hid_host_interface_config_t *iface_conf
 
     HID_RETURN_ON_ERROR( hid_host_interface_prepare_transfer(iface) );
 
+    /* Set infinity duration for output report = 0. Only reporting when a
+     * change is detected in the report data.
+     */
     HID_RETURN_ON_ERROR( hid_class_request_set_idle(iface, 0, 0) );
 
-    if (iface->proto == HID_PROTOCOL_MOUSE) {
-        HID_RETURN_ON_ERROR (hid_class_request_get_protocol(iface, &report_protocol) );
-        if (report_protocol != HID_REPORT_PROTOCOL_BOOT) {
-            HID_RETURN_ON_ERROR( hid_class_request_set_protocol(iface, HID_REPORT_PROTOCOL_BOOT) );
-        }
+    // Set BOOT protocol for interface
+    HID_RETURN_ON_ERROR (hid_class_request_get_protocol(iface, &report_protocol) );
+
+    if (report_protocol != HID_REPORT_PROTOCOL_BOOT) {
+        HID_RETURN_ON_ERROR( hid_class_request_set_protocol(iface, HID_REPORT_PROTOCOL_BOOT) );
         // verify that protocol has been successfully changed
         report_protocol = HID_REPORT_PROTOCOL_MAX;
         HID_RETURN_ON_ERROR (hid_class_request_get_protocol(iface, &report_protocol) );
+
         if (report_protocol != HID_REPORT_PROTOCOL_BOOT) {
             ESP_LOGE(TAG, "Interface %d Set BOOT Protocol Failure, protocol=%d",
                      iface->num,

+ 190 - 66
examples/peripherals/usb/host/hid/main/hid_host_example.c

@@ -6,6 +6,7 @@
 
 #include <stdio.h>
 #include <stdbool.h>
+#include <string.h>
 #include <unistd.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
@@ -20,11 +21,20 @@
 #include "hid_usage_keyboard.h"
 #include "hid_usage_mouse.h"
 
-#define APP_QUIT_PIN            GPIO_NUM_0
-#define APP_QUIT_PIN_POLL_MS    500
+#define APP_QUIT_PIN                GPIO_NUM_0
+#define APP_QUIT_PIN_POLL_MS        500
 
-#define READY_TO_UNINSTALL      (HOST_NO_CLIENT | HOST_ALL_FREE)
+#define READY_TO_UNINSTALL          (HOST_NO_CLIENT | HOST_ALL_FREE)
 
+/* Main char symbol for ENTER key */
+#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
+ *
+ */
 typedef enum {
     HOST_NO_CLIENT = 0x1,
     HOST_ALL_FREE = 0x2,
@@ -33,6 +43,19 @@ typedef enum {
     DEVICE_ADDRESS_MASK = 0xFF0,
 } app_event_t;
 
+/**
+ * @brief Key event
+ *
+ */
+typedef struct {
+    enum key_state {
+        KEY_STATE_PRESSED = 0x00,
+        KEY_STATE_RELEASED = 0x01
+    } state;
+    uint8_t modifier;
+    uint8_t key_code;
+} key_event_t;
+
 #define USB_EVENTS_TO_WAIT      (DEVICE_CONNECTED | DEVICE_ADDRESS_MASK | DEVICE_DISCONNECTED)
 
 static const char *TAG = "example";
@@ -42,16 +65,67 @@ static bool hid_device_connected = false;
 hid_host_interface_handle_t keyboard_handle = NULL;
 hid_host_interface_handle_t mouse_handle = NULL;
 
-
-const char *modifier_char_name[8] = {
-    "LEFT_CONTROL",
-    "LEFT_SHIFT",
-    "LEFT_ALT",
-    "LEFT_GUI",
-    "RIGHT_CONTROL",
-    "RIGHT_SHIFT",
-    "RIGHT_ALT",
-    "RIGHT_GUI"
+/**
+ * @brief Scancode to ascii table
+ */
+const uint8_t keycode2ascii [57][2] = {
+    {0, 0}, /* HID_KEY_NO_PRESS        */
+    {0, 0}, /* HID_KEY_ROLLOVER        */
+    {0, 0}, /* HID_KEY_POST_FAIL       */
+    {0, 0}, /* HID_KEY_ERROR_UNDEFINED */
+    {'a', 'A'}, /* HID_KEY_A               */
+    {'b', 'B'}, /* HID_KEY_B               */
+    {'c', 'C'}, /* HID_KEY_C               */
+    {'d', 'D'}, /* HID_KEY_D               */
+    {'e', 'E'}, /* HID_KEY_E               */
+    {'f', 'F'}, /* HID_KEY_F               */
+    {'g', 'G'}, /* HID_KEY_G               */
+    {'h', 'H'}, /* HID_KEY_H               */
+    {'i', 'I'}, /* HID_KEY_I               */
+    {'j', 'J'}, /* HID_KEY_J               */
+    {'k', 'K'}, /* HID_KEY_K               */
+    {'l', 'L'}, /* HID_KEY_L               */
+    {'m', 'M'}, /* HID_KEY_M               */
+    {'n', 'N'}, /* HID_KEY_N               */
+    {'o', 'O'}, /* HID_KEY_O               */
+    {'p', 'P'}, /* HID_KEY_P               */
+    {'q', 'Q'}, /* HID_KEY_Q               */
+    {'r', 'R'}, /* HID_KEY_R               */
+    {'s', 'S'}, /* HID_KEY_S               */
+    {'t', 'T'}, /* HID_KEY_T               */
+    {'u', 'U'}, /* HID_KEY_U               */
+    {'v', 'V'}, /* HID_KEY_V               */
+    {'w', 'W'}, /* HID_KEY_W               */
+    {'x', 'X'}, /* HID_KEY_X               */
+    {'y', 'Y'}, /* HID_KEY_Y               */
+    {'z', 'Z'}, /* HID_KEY_Z               */
+    {'1', '!'}, /* HID_KEY_1               */
+    {'2', '@'}, /* HID_KEY_2               */
+    {'3', '#'}, /* HID_KEY_3               */
+    {'4', '$'}, /* HID_KEY_4               */
+    {'5', '%'}, /* HID_KEY_5               */
+    {'6', '^'}, /* HID_KEY_6               */
+    {'7', '&'}, /* HID_KEY_7               */
+    {'8', '*'}, /* HID_KEY_8               */
+    {'9', '('}, /* HID_KEY_9               */
+    {'0', ')'}, /* HID_KEY_0               */
+    {KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER           */
+    {0, 0}, /* HID_KEY_ESC             */
+    {'\b', 0}, /* HID_KEY_DEL             */
+    {0, 0}, /* HID_KEY_TAB             */
+    {' ', ' '}, /* HID_KEY_SPACE           */
+    {'-', '_'}, /* HID_KEY_MINUS           */
+    {'=', '+'}, /* HID_KEY_EQUAL           */
+    {'[', '{'}, /* HID_KEY_OPEN_BRACKET    */
+    {']', '}'}, /* HID_KEY_CLOSE_BRACKET   */
+    {'\\', '|'}, /* HID_KEY_BACK_SLASH      */
+    {'\\', '|'}, /* HID_KEY_SHARP           */  // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
+    {';', ':'}, /* HID_KEY_COLON           */
+    {'\'', '"'}, /* HID_KEY_QUOTE           */
+    {'`', '~'}, /* HID_KEY_TILDE           */
+    {',', '<'}, /* HID_KEY_LESS            */
+    {'.', '>'}, /* HID_KEY_GREATER         */
+    {'/', '?'} /* HID_KEY_SLASH           */
 };
 
 /**
@@ -59,29 +133,20 @@ const char *modifier_char_name[8] = {
  *
  * @param[in] proto Current protocol to output
  */
-static void hid_trigger_new_line_output(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;
 
     if (prev_proto_output != proto) {
         prev_proto_output = proto;
         printf("\r\n");
-        fflush(stdout);
-    }
-}
-
-/**
- * @brief HID Keyboard modifier verification function. Verify and print debug information about modifier has been pressed
- *
- * @param[in] modifier
- */
-static inline void hid_keyboard_modifier_pressed(uint8_t modifier)
-{
-    // verify bit mask
-    for (uint8_t i = 0; i < (sizeof(uint8_t) << 3); i++) {
-        if ((modifier >> i) & 0x01) {
-            ESP_LOGD(TAG, "Modifier Pressed: %s", modifier_char_name[i]);
+        if (proto == HID_PROTOCOL_MOUSE) {
+            printf("Mouse\r\n");
         }
+        if (proto == HID_PROTOCOL_KEYBOARD) {
+            printf("Keyboard\r\n");
+        }
+        fflush(stdout);
     }
 }
 
@@ -93,7 +158,7 @@ static inline void hid_keyboard_modifier_pressed(uint8_t modifier)
  * @return false Modifier was not pressed (left or right shift)
  *
  */
-static inline bool hid_keyboard_is_modifier_capital(uint8_t modifier)
+static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier)
 {
     if ((modifier && HID_LEFT_SHIFT) ||
             (modifier && HID_RIGHT_SHIFT)) {
@@ -107,23 +172,84 @@ static inline bool hid_keyboard_is_modifier_capital(uint8_t modifier)
  *
  * @param[in] modifier  Keyboard modifier data
  * @param[in] key_code  Keyboard key code
+ * @param[in] key_char  Pointer to key char data
+ *
+ * @return true  Key scancode converted successfully
+ * @return false Key scancode unknown
  */
-static inline char hid_keyboard_get_char(uint8_t modifier, uint8_t key_code)
+static inline bool hid_keyboard_get_char(uint8_t modifier,
+        uint8_t key_code,
+        unsigned char *key_char)
 {
-    uint8_t key_char = (hid_keyboard_is_modifier_capital(modifier)) ? 'A' : 'a';
-    // Handle only char key pressed
-    if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_Z)) {
-        key_char += (key_code - HID_KEY_A);
-    } else if ((key_code >= HID_KEY_1) && (key_code <= HID_KEY_9)) {
-        key_char = '1' + (key_code - HID_KEY_1);
-    } else if (key_code == HID_KEY_0) {
-        key_char = '0';
+    uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0;
+
+    if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_SLASH)) {
+        *key_char = keycode2ascii[key_code][mod];
     } else {
         // All other key pressed
-        key_char = 0x00;
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @brief HID Keyboard print char symbol
+ *
+ * @param[in] key_char  Keyboard char to stdout
+ */
+static inline void hid_keyboard_print_char(unsigned int key_char)
+{
+    if (!!key_char) {
+        putchar(key_char);
+#if (KEYBOARD_ENTER_LF_EXTEND)
+        if (KEYBOARD_ENTER_MAIN_CHAR == key_char) {
+            putchar('\n');
+        }
+#endif // KEYBOARD_ENTER_LF_EXTEND
+        fflush(stdout);
     }
+}
+
+/**
+ * @brief Key Event. Key event with the key code, state and modifier.
+ *
+ * @param[in] key_event Pointer to Key Event structure
+ *
+ */
+static void key_event_callback(key_event_t *key_event)
+{
+    unsigned char key_char;
+
+    hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD);
+
+    if (KEY_STATE_PRESSED == key_event->state) {
+        if (hid_keyboard_get_char(key_event->modifier,
+                                  key_event->key_code, &key_char)) {
 
-    return key_char;
+            hid_keyboard_print_char(key_char);
+
+        }
+    }
+}
+
+/**
+ * @brief Key buffer scan code search.
+ *
+ * @param[in] src       Pointer to source buffer where to search
+ * @param[in] key       Key scancode to search
+ * @param[in] length    Size of the source buffer
+ */
+static inline bool key_found(const uint8_t *const src,
+                             uint8_t key,
+                             unsigned int length)
+{
+    for (unsigned int i = 0; i < length; i++) {
+        if (src[i] == key) {
+            return true;
+        }
+    }
+    return false;
 }
 
 /**
@@ -134,37 +260,37 @@ static inline char hid_keyboard_get_char(uint8_t modifier, uint8_t key_code)
  */
 static void hid_host_keyboard_report_callback(const uint8_t *const data, const int length)
 {
-    bool keys_state_changed = false;
     hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data;
 
-    if (kb_report->modifier.val != 0) {
-        hid_keyboard_modifier_pressed(kb_report->modifier.val);
+    if (length < sizeof(hid_keyboard_input_report_boot_t)) {
+        return;
     }
 
-    static uint8_t keys[HID_KEYBOARD_KEY_MAX] = { 0 };
+    static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = { 0 };
+    key_event_t key_event;
 
     for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
-        if (kb_report->key[i] != keys[i]) {
-            keys_state_changed = true;
-            if (kb_report->key[i] != 0) {
-                keys[i] = hid_keyboard_get_char(kb_report->modifier.val, kb_report->key[i]);
-            } else {
-                keys[i] = 0x00;
-            }
-        }
-    }
-
-    if (keys_state_changed) {
 
-        hid_trigger_new_line_output(HID_PROTOCOL_KEYBOARD);
+        // key has been released verification
+        if (prev_keys[i] > HID_KEY_ERROR_UNDEFINED &&
+                !key_found(kb_report->key, prev_keys[i], HID_KEYBOARD_KEY_MAX)) {
+            key_event.key_code = prev_keys[i];
+            key_event.modifier = 0;
+            key_event.state = KEY_STATE_RELEASED;
+            key_event_callback(&key_event);
+        }
 
-        printf("|");
-        for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
-            printf("%c|", keys[i] ? keys[i] : ' ');
+        // key has been pressed verification
+        if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED &&
+                !key_found(prev_keys, kb_report->key[i], HID_KEYBOARD_KEY_MAX)) {
+            key_event.key_code = kb_report->key[i];
+            key_event.modifier = kb_report->modifier.val;
+            key_event.state = KEY_STATE_PRESSED;
+            key_event_callback(&key_event);
         }
-        printf("\r");
-        fflush(stdout);
     }
+
+    memcpy(prev_keys, &kb_report->key, HID_KEYBOARD_KEY_MAX);
 }
 
 /**
@@ -177,9 +303,7 @@ static void hid_host_mouse_report_callback(const uint8_t *const data, const int
 {
     hid_mouse_input_report_boot_t *mouse_report = (hid_mouse_input_report_boot_t *)data;
 
-    // First 3 bytes are mandated by HID specification
     if (length < sizeof(hid_mouse_input_report_boot_t)) {
-        ESP_LOGE(TAG, "Mouse Boot report length (%d) error", length);
         return;
     }
 
@@ -190,7 +314,7 @@ static void hid_host_mouse_report_callback(const uint8_t *const data, const int
     x_pos += mouse_report->x_displacement;
     y_pos += mouse_report->y_displacement;
 
-    hid_trigger_new_line_output(HID_PROTOCOL_MOUSE);
+    hid_print_new_device_report_header(HID_PROTOCOL_MOUSE);
 
     printf("X: %06d\tY: %06d\t|%c|%c|\r",
            x_pos, y_pos,
@@ -251,7 +375,7 @@ static void hid_host_interface_event_callback(const hid_host_interface_event_t *
 
         break;
     case HID_DEVICE_INTERFACE_TRANSFER_ERROR:
-        ESP_LOGI(TAG, "Interface number %d, transfer error",
+        ESP_LOGD(TAG, "Interface number %d, transfer error",
                  event->interface.num);
         break;