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:
Connecting and Disconnecting: Including functions to connect/disconnect HID.
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. |
|
bt_hid_device.h |
Connect HID, send and receive data etc. |
Functions
HID Host API
This section introduces the APIs used by HID host.
HID Host Init
bt_hid_host_init() - Initialize HID profile. Decide whether to support boot mode.
bt_hid_host_descriptor_set() - Set an HID report descriptor after get the HID report descriptor over SDP discovery.
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 = ¶m->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
bt_hid_host_connect_req() - Initiate an HID connection to a remote HID device, as shown below:
bt_hid_host_connect_cfm() - Accept or reject the incoming connection from the remote HID device, as shown below:
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
bt_hid_host_disconnect_req() - Request a hid disconnection, as shown below:
HID Host Protocol
bt_hid_host_get_protocol_req() - Request to retrieve the protocol mode of the HID device, as shown below:
bt_hid_host_set_protocol_req() - Request to set the protocol mode of the HID device, as shown below:
HID Host Report
bt_hid_host_get_report_req() - Request the transfer of a HID report from the HID device, as shown below:
bt_hid_host_set_report_req() - Request to initiate the transfer of a report to HID device, as shown below:
bt_hid_host_interrupt_data_send() - Send HID report data to the HID device over interrupt channel, as shown below:
HID Device API
This section introduces the APIs used by HID device.
HID Device Init
bt_hid_device_init() - Initialize HID profile. Configure the maximum link number of HID and decide whether to support boot mode.
bt_hid_device_descriptor_set() - Set an HID report descriptor.
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
bt_hid_device_connect_cfm() - Accept or reject the incoming connection from the remote HID host.
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;
}
bt_hid_device_connect_req() - Initiate an HID connection to a remote HID host, as shown below:
HID Device Disconnect
bt_hid_device_disconnect_req() - Request a hid disconnection, as shown below:
HID Device Report
bt_hid_device_get_report_rsp() - Send an HID get report response over control channel after receiving get report command.
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;
}
bt_hid_device_interrupt_data_send() - Send HID report data to the HID host over interrupt channel, as shown below:
There is an example of sending volume up report.
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.