在描述符中组合键盘和用户控件

时间:2019-03-14 15:17:50

标签: bluetooth-lowenergy hid bluetooth-gatt

我有一个看起来像这样的hid_discriptor:

// from USB HID Specification 1.1, Appendix B.1
const uint8_t hid_descriptor_keyboard_boot_mode[] = {
    /*
       Keyboard
     */
    0x05, 0x01,                    // Usage Page (Generic Desktop)
    0x09, 0x06,                    // Usage (Keyboard)
    0xa1, 0x01,                    // Collection (Application)

    0x85,  0x01,                   // Report ID 1

    // Modifier byte

    0x75, 0x01,                    //   Report Size (1)
    0x95, 0x08,                    //   Report Count (8)
    0x05, 0x07,                    //   Usage Page (Key codes)
    0x19, 0xe0,                    //   Usage Minimum (Keyboard LeftControl)
    0x29, 0xe7,                    //   Usage Maxium (Keyboard Right GUI)
    0x15, 0x00,                    //   Logical Minimum (0)
    0x25, 0x01,                    //   Logical Maximum (1)
    0x81, 0x02,                    //   Input (Data, Variable, Absolute)

    // Reserved byte

    0x75, 0x01,                    //   Report Size (1)
    0x95, 0x08,                    //   Report Count (8)
    0x81, 0x03,                    //   Input (Constant, Variable, Absolute)

    // LED report + padding

    0x95, 0x05,                    //   Report Count (5)
    0x75, 0x01,                    //   Report Size (1)
    0x05, 0x08,                    //   Usage Page (LEDs)
    0x19, 0x01,                    //   Usage Minimum (Num Lock)
    0x29, 0x05,                    //   Usage Maxium (Kana)
    0x91, 0x02,                    //   Output (Data, Variable, Absolute)

    0x95, 0x01,                    //   Report Count (1)
    0x75, 0x03,                    //   Report Size (3)
    0x91, 0x03,                    //   Output (Constant, Variable, Absolute)

    // Keycodes

    0x95, 0x06,                    //   Report Count (6)
    0x75, 0x08,                    //   Report Size (8)
    0x15, 0x00,                    //   Logical Minimum (0)
    0x25, 0xff,                    //   Logical Maximum (1)
    0x05, 0x07,                    //   Usage Page (Key codes)
    0x19, 0x00,                    //   Usage Minimum (Reserved (no event indicated))
    0x29, 0xff,                    //   Usage Maxium (Reserved)
    0x81, 0x00,                    //   Input (Data, Array)
    0xc0,                          // End collection
};

哪个适用于键盘代码;

但是,我也想在样本中添加消费者控制,因此我也可以向上/向下发送音量等。

    /*
       Consumer Control
     */
    0x05, 0x0C,                     // Usage Page (Consumer Devices)
    0x09, 0x01,                     // Usage (Consumer Control)
    0xA1, 0x01,                     // Collection (Application)
    0x85, 0x02,                     //      Report ID
    0x75, 0x09, 0x01,               //      Report Size
    0x95, 0x09, 0x01,               //     Report Count
    0x15, 0x00,                     //      Logical Minimum (0)
    0x26, 0xFF, 0x07,               //      Logical Maximum (2047)
    0x19, 0x00,                     //      Usage Minimum (0)
    0x2A, 0xFF, 0x07,               //      Usage Maximum (2047)
    0x81, 0x00,                     //      Input (Data, Ary, Abs)
    0xC0,

问题是,in the provided demo code;发送报告的代码是这样的:

static void send_report(int modifier, int keycode){
    uint8_t report[] = { /* 0xa1, */ modifier, 0, 0, keycode, 0, 0, 0, 0, 0};
    hids_device_send_input_report(con_handle, report, sizeof(report));
}

这在发送键击方面有效;当我在源代码中跟踪该调用时,它将转到to this function;

void hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
    hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
    if (!instance){
        log_error("no instance for handle 0x%02x", con_handle);
        return;
    }
    att_server_notify(con_handle, instance->hid_report_input_value_handle, report, report_len);
}

实际上使用instance->hid_report_input_value_handle代替hid_descriptor_keyboard_boot_mode;我也知道为什么(如果人们想知道的话); The spec mentions;

  

HID子类1为启动设备定义了两个描述符。设备可能   将其他数据添加到这些引导报告中,但是键盘报告的前8个字节和鼠标报告的前3个字节必须符合引导报告描述符所定义的格式,以便BIOS可以正确解释数据。

... snip ...

  

在加载HID类驱动程序时,它将发出更改协议,   在读取引导界面的报告描述符后,从引导协议更改为报告协议。

因此,在调用更改协议之后;该报告已加载到hid_report_input_value_handle中。所以到目前为止,一切都还很清楚。

然后主要问题来了;如何将消费者控制发送到配对设备?仅仅是添加reportId的问题吗?例如

对于键盘按键作为击键事件:

uint8_t report[] = { 0x01, modifier, 0, 0, keycode, 0, 0, 0, 0, 0};
hids_device_send_input_report(con_handle, report, sizeof(report));

对于击键事件,用于消费者控制(播放/暂停):

uint8_t report[] = { 0x02, 0xCD, 0x00};
hids_device_send_input_report(con_handle, report, sizeof(report));

那是正确的吗?任何指导都将受到欢迎,BLE对我来说是新的,因此是一个问题。

1 个答案:

答案 0 :(得分:0)

对于键盘HID描述符,假设它表示以下C结构:

This user lacks sufficient permissions to access this resource

...然后我希望send函数看起来像这样:

//--------------------------------------------------------------------------------
// Keyboard/Keypad Page inputReport 01 (Device --> Host)
//--------------------------------------------------------------------------------

typedef struct
{
  uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                     // Collection: CA:Keyboard
  uint8_t  KB_KeyboardKeyboardLeftControl : 1;       // Usage 0x000700E0: Keyboard Left Control, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardLeftShift : 1;         // Usage 0x000700E1: Keyboard Left Shift, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardLeftAlt : 1;           // Usage 0x000700E2: Keyboard Left Alt, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardLeftGui : 1;           // Usage 0x000700E3: Keyboard Left GUI, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardRightControl : 1;      // Usage 0x000700E4: Keyboard Right Control, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardRightShift : 1;        // Usage 0x000700E5: Keyboard Right Shift, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardRightAlt : 1;          // Usage 0x000700E6: Keyboard Right Alt, Value = 0 to 1
  uint8_t  KB_KeyboardKeyboardRightGui : 1;          // Usage 0x000700E7: Keyboard Right GUI, Value = 0 to 1
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  Keyboard[6];                              // Value = 0 to 255
} inputReport01_t;

至于“消费类设备”报告,HID报告描述符似乎非常错误。应该是这样的(假设它已连接到您的键盘报告描述符):

uint8_t report[] = { 0x01, modifier, 0, keycode, 0, 0, 0, 0, 0}; // <-- i.e. one less byte
hids_device_send_input_report(con_handle, report, sizeof(report));

...代表以下C结构:

/*
   Consumer Control
 */
0x05, 0x0C,                     // Usage Page (Consumer Devices)
0x09, 0x01,                     // Usage (Consumer Control)
0xA1, 0x01,                     // Collection (Application)
0x85, 0x02,                     //      Report ID
0x75, 0x10,                     //      Report Size (16)
0x95, 0x01,                     //     Report Count (1)
0x26, 0xFF, 0x07,               //      Logical Maximum (2047)
0x19, 0x00,                     //      Usage Minimum (0)
0x2A, 0xFF, 0x07,               //      Usage Maximum (2047)
0x81, 0x00,                     //      Input (Data, Ary, Abs)
0xC0,

...在这种情况下,您发送消费者设备请求的功能应该无需修改即可工作:

//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------

/*
05 0C        (GLOBAL) USAGE_PAGE         0x000C Consumer Device Page 
09 01        (LOCAL)  USAGE              0x000C0001 Consumer Control (Application Collection)  
A1 01        (MAIN)   COLLECTION         0x01 Application (Usage=0x000C0001: Page=Consumer Device Page, Usage=Consumer Control, Type=Application Collection)
85 02          (GLOBAL) REPORT_ID          0x02 (2)  
75 10          (GLOBAL) REPORT_SIZE        0x10 (16) Number of bits per field  
95 01          (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
26 FF07        (GLOBAL) LOGICAL_MAXIMUM    0x07FF (2047)  
19 00          (LOCAL)  USAGE_MINIMUM      0x000C0000 Unassigned  <-- Info: Consider replacing 19 00 with 18
2A FF07        (LOCAL)  USAGE_MAXIMUM      0x000C07FF 
81 00          (MAIN)   INPUT              0x00000000 (1 field x 16 bits) 0=Data 0=Array 0=Absolute 
C0           (MAIN)   END_COLLECTION     Application 
*/
//--------------------------------------------------------------------------------
// Consumer Device Page inputReport 02 (Device --> Host)
//--------------------------------------------------------------------------------

typedef struct
{
  uint8_t  reportId;                                 // Report ID = 0x02 (2)
                                                     // Collection: CA:ConsumerControl
  uint16_t ConsumerControl;                          // Value = 0 to 2047
} inputReport02_t;

...发送播放/暂停请求。

不要忘了在每次发送后都跟上“没有按键被按下”的信号(对于键盘):

uint8_t report[] = { 0x02, 0xCD, 0x00};
hids_device_send_input_report(con_handle, report, sizeof(report));

这等效于传统的“按键”通知。