HID

The purpose of this document is to provide an overview of the HID application based on HID profile. The Human Interface Device (HID) Profile Specification defines the protocols, procedures, and features to be used by Bluetooth HID devices and Bluetooth HID hosts.

The most common examples of such devices are keyboards and mice used together with computers.

Terminology and Concepts

HID Role

  • HID host: This is a device using or requesting the services of a Bluetooth HID device. Examples would be a personal computer or gaming console.

  • HID device: This is a device providing the service of human or other data input and output to and from a Bluetooth HID host. Examples of Bluetooth HID devices are keyboards, mice, and joysticks.

Feature Set

The functions provided by HID API are as below:

  1. Connecting and Disconnecting: Including functions to connect/disconnect HID.

  2. Sending and Receiving Data: Including functions to send data through HID between devices.

Provided APIs

This figure shows the relationship between applications and HID and Bluetooth Protocol Stack. Above the horizon line is developed by the applications. Below the horizon line is developed by Realtek:

The table below shows a simple description of HID APIs. HID related APIs are provided in the sdk\inc\framework\bt\bt_hid_host.h and sdk\inc\framework\bt\bt_hid_device.h.

Header file

Description

API Reference

bt_hid_host.h

Connect HID, send and receive data etc.

Bluetooth HID Host

bt_hid_device.h

Connect HID, send and receive data etc.

Bluetooth HID Device

Functions

HID Host API

This section introduces the APIs used by HID host.

HID Host Init

static void app_hid_host_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
    T_BT_EVENT_PARAM *param = event_buf;
    T_APP_BR_LINK *p_link;
    bool handle = true;

    switch (event_type)
    {
    case BT_EVENT_SDP_ATTR_INFO:
        {
            T_APP_BR_LINK *p_link = app_link_find_br_link(param->sdp_attr_info.bd_addr);

            if (p_link != NULL)
            {
                T_BT_SDP_ATTR_INFO *sdp_info = &param->sdp_attr_info.info;

                if (sdp_info->srv_class_uuid_data.uuid_16 == UUID_HUMAN_INTERFACE_DEVICE_SERVICE)
                {
                    if (app_hid_host_descriptor_find(param->sdp_attr_info.bd_addr) == NULL)
                    {
                        uint8_t  *p_attr_start = sdp_info->p_attr;
                        uint8_t  *p_attr_end = p_attr_start + sdp_info->attr_len;
                        uint8_t  *p_attr = NULL;
                        uint8_t  *p_attr_param = NULL;
                        uint8_t  *p_elem = NULL;
                        uint8_t  *descriptor = NULL;
                        uint32_t  descriptor_len = 0;
                        uint8_t   type = 0;

                        p_attr = bt_sdp_attr_find(p_attr_start, p_attr_end - p_attr_start, SDP_ATTR_HID_DESC_LIST);
                        if (p_attr)
                        {
                            uint8_t loop = 1;

                            while ((p_elem = bt_sdp_elem_access(p_attr, p_attr_end - p_attr, loop)) != NULL)
                            {
                                p_attr_param = bt_sdp_elem_access(p_elem, p_attr_end - p_elem, 2);

                                if (p_attr_param)
                                {
                                    descriptor = bt_sdp_elem_decode(p_attr_param, p_attr_end - p_attr_param, &descriptor_len, &type);
                                    break;
                                }
                                loop++;
                            }
                        }
                        if (descriptor)
                        {
                            T_HID_HOST_DESCRIPTOR *hid_descriptor;

                            hid_descriptor = app_hid_host_alloc_descriptor(param->sdp_attr_info.bd_addr,
                                                                           descriptor,
                                                                           descriptor_len);
                            if (HID_PROFILE_MASK & p_link->connected_profile)
                            {
                                bt_hid_host_descriptor_set(param->sdp_attr_info.bd_addr,
                                                           hid_descriptor->descriptor,
                                                           hid_descriptor->descriptor_len);
                            }
                        }
                    }
                }
            }
        }
        break;

    case BT_EVENT_HID_HOST_CONN_CMPL:
        {
            T_HID_HOST_DESCRIPTOR *hid_descriptor;

            hid_descriptor = app_hid_host_descriptor_find(param->hid_host_conn_cmpl.bd_addr);
            if (hid_descriptor != NULL)
            {
                bt_hid_host_descriptor_set(param->hid_host_conn_cmpl.bd_addr,
                                           hid_descriptor->descriptor,
                                           hid_descriptor->descriptor_len);
            }
            else
            {
                bt_sdp_discov_start(param->hid_host_conn_cmpl.bd_addr, BT_SDP_UUID16, hid_service_uuid16);
            }
        }
        break;
    
    }

    if (handle == true)
    {
        APP_PRINT_INFO1("app_hid_host_bt_cback: event_type 0x%04x", event_type);
    }
}

void app_hid_host_init(void)
{
    if (app_cfg_const.supported_profile_mask & HID_PROFILE_MASK)
    {
        bt_hid_host_init(true);
        bt_mgr_cback_register(app_hid_host_bt_cback);
    }
}

HID Host Connect

static void app_hid_host_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
    T_BT_EVENT_PARAM *param = event_buf;
    T_APP_BR_LINK *p_link;
    bool handle = true;

    switch (event_type)
    {
    case BT_EVENT_HID_HOST_CONN_IND:
        {
            p_link = app_find_br_link(param->bt_hid_host_conn_ind.bd_addr);
            if (p_link != NULL)
            {
                bt_hid_host_connect_cfm(p_link->bd_addr, true);
            }
        }
        break;
}

HID Host Disconnect

HID Host Protocol

HID Host Report

HID Device API

This section introduces the APIs used by HID device.

HID Device Init

const uint8_t hid_descriptor_keyboard_boot_mode[] =
{
    0x05, 0x0c,             /* USAGE_PAGE (Consumer) */
    0x09, 0x01,             /* USAGE (Consumer Control) */
    0xA1, 0x01,             /* COLLECTION (Application) */
    0x85, 0x02,             /*     REPORT_ID         (2)           */
    0x95, 0x18,             /*     REPORT_COUNT (24) */
    0x75, 0x01,             /*     REPORT_SIZE (1) */
    0x15, 0x00,             /*     LOGICAL_MINIMUM (0) */
    0x25, 0x01,             /*     LOGICAL_MAXIMUM (1) */
    0x0a, 0x23, 0x02,       /*     USAGE (AL Home) */
    0x0a, 0x21, 0x02,       /*     USAGE (AC Search) */
    0x0a, 0x8a, 0x01,       /*     USAGE (AL Email Reader) */
    0x0a, 0x96, 0x01,       /*     USAGE (AL Internet Browser) */
    0x0a, 0x9e, 0x01,       /*     USAGE (AL Terminal Lock/Screensaver) */
    0x0a, 0x01, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x02, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x05, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x06, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x07, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x08, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x0a, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x0b, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0xae, 0x01,       /*     USAGE (AL Keyboard Layboard) */
    0x0a, 0xb1, 0x01,       /*     USAGE (AL Screen Saver) */
    0x09, 0x40,             /*     USAGE (Menu) */
    0x09, 0x30,             /*     USAGE (Power) */
    0x09, 0xb7,             /*     USAGE (Stop) */
    0x09, 0xb6,             /*     USAGE (Scan Previous Track) */
    0x09, 0xcd,             /*     USAGE (Play/Pause) */
    0x09, 0xb5,             /*     USAGE (Scan Next Track) */
    0x09, 0xe2,             /*     USAGE (Mute) */
    0x09, 0xea,             /*     USAGE (Volume Down) */
    0x09, 0xe9,             /*     USAGE (Volume Up) */
    0x81, 0x02,             /*     INPUT (Data,Var,Abs) */
    0xC0,                   /* END_COLLECTION */
};

void app_hid_init(void)
{
    if (app_cfg_const.supported_profile_mask & HID_PROFILE_MASK)
    {
        bt_hid_device_init(true);
        bt_hid_device_descriptor_set(hid_descriptor_keyboard_boot_mode, sizeof(hid_descriptor_keyboard_boot_mode));
        bt_mgr_cback_register(app_hid_bt_cback);
    }
}

Application should register HID SDP table when initializing HID. Application can call bt_sdp_record_add() to register HID SDP table. An example of HID SDP record is shown below:

static const uint8_t hid_keyboard_sdp_record[] =
{
    //Total length
    SDP_DATA_ELEM_SEQ_HDR_2BYTE,
    0x01,
    0x12,

    //Attribute SDP_ATTR_SRV_CLASS_ID_LIST
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
    (uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
    SDP_DATA_ELEM_SEQ_HDR,
    0x03,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_HUMAN_INTERFACE_DEVICE_SERVICE >> 8),
    (uint8_t)(UUID_HUMAN_INTERFACE_DEVICE_SERVICE),

    //Attribute SDP_ATTR_PROTO_DESC_LIST
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
    (uint8_t)SDP_ATTR_PROTO_DESC_LIST,
    SDP_DATA_ELEM_SEQ_HDR,
    0x0D,
    SDP_DATA_ELEM_SEQ_HDR,
    0x06,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_L2CAP >> 8),
    (uint8_t)(UUID_L2CAP),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(PSM_HID_CONTROL >> 8),
    (uint8_t)(PSM_HID_CONTROL),
    SDP_DATA_ELEM_SEQ_HDR,
    0x03,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_HIDP >> 8),
    (uint8_t)(UUID_HIDP),

    //Attribute SDP_ATTR_LANG_BASE_ATTR_ID_LIST
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_LANG_BASE_ATTR_ID_LIST >> 8),
    (uint8_t)SDP_ATTR_LANG_BASE_ATTR_ID_LIST,
    SDP_DATA_ELEM_SEQ_HDR,
    0x09,
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_LANG_ENGLISH >> 8),
    (uint8_t)(SDP_LANG_ENGLISH),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_CHARACTER_UTF8 >> 8),
    (uint8_t)(SDP_CHARACTER_UTF8),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_BASE_LANG_OFFSET >> 8),
    (uint8_t)(SDP_BASE_LANG_OFFSET),

    //Attribute SDP_ATTR_PROFILE_DESC_LIST
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_PROFILE_DESC_LIST >> 8),
    (uint8_t)(SDP_ATTR_PROFILE_DESC_LIST),
    SDP_DATA_ELEM_SEQ_HDR,
    0x08,
    SDP_DATA_ELEM_SEQ_HDR,
    0x06,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_HUMAN_INTERFACE_DEVICE_SERVICE >> 8),
    (uint8_t)(UUID_HUMAN_INTERFACE_DEVICE_SERVICE),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0101 >> 8),
    (uint8_t)(0x0101),

    //Attribute SDP_ATTR_ADD_PROTO_DESC_LIST
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_ADD_PROTO_DESC_LIST >> 8),
    (uint8_t)SDP_ATTR_ADD_PROTO_DESC_LIST,
    SDP_DATA_ELEM_SEQ_HDR,
    0x0F,
    SDP_DATA_ELEM_SEQ_HDR,
    0x0D,
    SDP_DATA_ELEM_SEQ_HDR,
    0x06,
    SDP_UUID16_HDR,
    (uint8_t)(0x0100 >> 8),
    (uint8_t)(0x0100),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(PSM_HID_INTERRUPT >> 8),
    (uint8_t)(PSM_HID_INTERRUPT),
    SDP_DATA_ELEM_SEQ_HDR,
    0x03,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_HIDP >> 8),
    (uint8_t)(UUID_HIDP),

    //Attribute SDP_ATTR_SRV_NAME
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)((SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET) >> 8),
    (uint8_t)(SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET),
    SDP_STRING_HDR,
    0x0B,
    'R', 'e', 'a', 'l', 't', 'e', 'k', ' ', 'H', 'I', 'D',

    //Attribute SDP_ATTR_SRV_DESC
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)((SDP_ATTR_SRV_DESC + SDP_BASE_LANG_OFFSET) >> 8),
    (uint8_t)(SDP_ATTR_SRV_DESC + SDP_BASE_LANG_OFFSET),
    SDP_STRING_HDR,
    0x10,
    '(', '3', ')', ' ', 'B', 'u', 't', 't', 'o', 'n', ' ', 'M', 'o', 'u', 's', 'e',

    //Attribute SDP_ATTR_PROVIDER_NAME
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)((SDP_ATTR_PROVIDER_NAME + SDP_BASE_LANG_OFFSET) >> 8),
    (uint8_t)(SDP_ATTR_PROVIDER_NAME + SDP_BASE_LANG_OFFSET),
    SDP_STRING_HDR,
    0x07,
    'R', 'e', 'a', 'l', 't', 'e', 'k',

    //Attribute HIDParserVersion
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0201 >> 8),
    (uint8_t)(0x0201),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0111 >> 8),
    (uint8_t)(0x0111),

    //Attribute HIDDeviceSubclass
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0202 >> 8),
    (uint8_t)(0x0202),
    SDP_UNSIGNED_ONE_BYTE,
    0x80,

    //Attribute HIDCountryCode
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0203 >> 8),
    (uint8_t)(0x0203),
    SDP_UNSIGNED_ONE_BYTE,
    0x21,

    //Attribute HIDVirtualCable
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0204 >> 8),
    (uint8_t)(0x0204),
    SDP_BOOL_ONE_BYTE,
    0x01,

    //Attribute HIDReconnectInitiate
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0205 >> 8),
    (uint8_t)(0x0205),
    SDP_BOOL_ONE_BYTE,
    0x01,

    //Attribute HIDDescriptorList
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0206 >> 8),
    (uint8_t)(0x0206),
    SDP_DATA_ELEM_SEQ_HDR,
    //0x47,
    0x58,
    SDP_DATA_ELEM_SEQ_HDR,
    //0x45,
    0x56,
    SDP_UNSIGNED_ONE_BYTE,
    0x22,                   /* Type = Report Descriptor */
    SDP_STRING_HDR,
    0x52,

    0x05, 0x0c,             /* USAGE_PAGE (Consumer) */
    0x09, 0x01,             /* USAGE (Consumer Control) */
    0xA1, 0x01,             /* COLLECTION (Application) */
    0x85, 0x02,             /*     REPORT_ID         (2)           */
    0x95, 0x18,             /*     REPORT_COUNT (24) */
    0x75, 0x01,             /*     REPORT_SIZE (1) */
    0x15, 0x00,             /*     LOGICAL_MINIMUM (0) */
    0x25, 0x01,             /*     LOGICAL_MAXIMUM (1) */
    0x0a, 0x23, 0x02,       /*     USAGE (AL Home) */
    0x0a, 0x21, 0x02,       /*     USAGE (AC Search) */
    0x0a, 0x8a, 0x01,       /*     USAGE (AL Email Reader) */
    0x0a, 0x96, 0x01,       /*     USAGE (AL Internet Browser) */
    0x0a, 0x9e, 0x01,       /*     USAGE (AL Terminal Lock/Screensaver) */
    0x0a, 0x01, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x02, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x05, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x06, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x07, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x08, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x0a, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0x0b, 0x03,       /*     USAGE (Reserved) */
    0x0a, 0xae, 0x01,       /*     USAGE (AL Keyboard Layboard) */
    0x0a, 0xb1, 0x01,       /*     USAGE (AL Screen Saver) */
    0x09, 0x40,             /*     USAGE (Menu) */
    0x09, 0x30,             /*     USAGE (Power) */
    0x09, 0xb7,             /*     USAGE (Stop) */
    0x09, 0xb6,             /*     USAGE (Scan Previous Track) */
    0x09, 0xcd,             /*     USAGE (Play/Pause) */
    0x09, 0xb5,             /*     USAGE (Scan Next Track) */
    0x09, 0xe2,             /*     USAGE (Mute) */
    0x09, 0xea,             /*     USAGE (Volume Down) */
    0x09, 0xe9,             /*     USAGE (Volume Up) */
    0x81, 0x02,             /*     INPUT (Data,Var,Abs) */
    0xC0,                   /* END_COLLECTION */

    //Attribute HIDLANGIDBaseList
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0207 >> 8),
    (uint8_t)(0x0207),
    SDP_DATA_ELEM_SEQ_HDR,
    0x08,
    SDP_DATA_ELEM_SEQ_HDR,
    0x06,
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0409 >> 8),
    (uint8_t)(0x0409),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0100 >> 8),
    (uint8_t)(0x0100),

    //Attribute HIDBatteryPower
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x0209 >> 8),
    (uint8_t)(0x0209),
    SDP_BOOL_ONE_BYTE,
    0x01,

    //Attribute HIDRemoteWake
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x020A >> 8),
    (uint8_t)(0x020A),
    SDP_BOOL_ONE_BYTE,
    0x01,

    //Attribute HIDNormallyConnectable
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x020D >> 8),
    (uint8_t)(0x020D),
    SDP_BOOL_ONE_BYTE,
    0x01,

    //Attribute HIDBootDevice
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(0x020E >> 8),
    (uint8_t)(0x020E),
    SDP_BOOL_ONE_BYTE,
    0x01,
};

The following table describes meanings of each bit in HID report. The bits for selected usage are set to 1. Others are set to 0. Please refer to https://usb.org/document-library/hid-usage-tables-122/.

Bit

Description

0

AL Home

1

AC Search

2

AL Email Reader

3

AL Internet Browser

4

AL Terminal Lock/Screensaver

5

Reserved

6

Reserved

7

Reserved

8

Reserved

9

Reserved

10

Reserved

11

Reserved

12

Reserved

13

AL Keyboard Layboard

14

AL Screen Saver

15

Menu

16

Power

17

Stop

18

Scan Previous Track

19

Play/Pause

20

Scan Next Track

21

Mute

22

Volume Down

23

Volume Up

HID Device Connect

static void app_hid_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
    T_BT_EVENT_PARAM *param = event_buf;
    T_APP_BR_LINK *p_link;
    bool handle = true;

    switch (event_type)
    {
    case BT_EVENT_HID_DEVICE_CONN_IND:
        {
            p_link = app_find_br_link(param->bt_hid_device_conn_ind.bd_addr);
            if (p_link != NULL)
            {
                bt_hid_device_connect_cfm(p_link->bd_addr, true);
            }
        }
        break;
}

HID Device Disconnect

HID Device Report

static void app_hid_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
    switch (event_type)
    {

    case BT_EVENT_HID_DEVICE_GET_REPORT_IND:
        {
            bt_hid_device_get_report_rsp(p_link->bd_addr, report_type, buf, len);
        }
        break;
}
void app_mmi_handle_action(uint8_t action)
{
    switch (action)
    {
    case MMI_TAKE_PICTURE:
        {
            uint8_t app_idx = app_a2dp_get_active_idx();
            uint8_t keyboard_vol_up[5] = {0x02, 0, 0, 0x80, 0};
            uint8_t keyboard_release[5] = {0x02, 0, 0, 0, 0};

            bt_hid_device_interrupt_data_send(app_db.br_link[app_idx].bd_addr, BT_HID_REPORT_TYPE_INPUT,
                                              keyboard_vol_up, sizeof(keyboard_vol_up));
            bt_hid_device_interrupt_data_send(app_db.br_link[app_idx].bd_addr, BT_HID_REPORT_TYPE_INPUT,
                                              keyboard_release, sizeof(keyboard_release));
        }
        break;
        
    default:
        break;
    }

Sample Projects

SDK provides a corresponding demo application for developers’ reference in development.The BR/EDR HID gives a simple example on how to use HID.