mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-12 10:13:43 +00:00
hids_client: merge states for boot and report mode
This commit is contained in:
parent
84b19b6775
commit
021192e149
@ -328,7 +328,7 @@ hog_mouse_demo: hog_mouse_demo.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SER
|
|||||||
hog_boot_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} hog_boot_host_demo.c
|
hog_boot_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} hog_boot_host_demo.c
|
||||||
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||||
|
|
||||||
hog_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} hog_host_demo.c
|
hog_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} btstack_hid_parser.o hid.o hog_host_demo.c
|
||||||
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||||
|
|
||||||
sm_pairing_peripheral: sm_pairing_peripheral.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${GATT_CLIENT_OBJ} sm_pairing_peripheral.c
|
sm_pairing_peripheral: sm_pairing_peripheral.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${GATT_CLIENT_OBJ} sm_pairing_peripheral.c
|
||||||
|
@ -35,15 +35,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BTSTACK_FILE__ "hog_boot_host_demo.c"
|
#define BTSTACK_FILE__ "hog_host_demo.c"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hog_boot_host_demo.c
|
* hog_host_demo.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* EXAMPLE_START(hog_boot_host_demo): HID Boot Host LE
|
/* EXAMPLE_START(hog_host_demo): HID Host LE
|
||||||
*
|
*
|
||||||
* @text This example implements a minimal HID-over-GATT Boot Host. It scans for LE HID devices, connects to it,
|
* @text This example implements a minimal HID-over-GATT Host. It scans for LE HID devices, connects to it,
|
||||||
* discovers the Characteristics relevant for the HID Service and enables Notifications on them.
|
* discovers the Characteristics relevant for the HID Service and enables Notifications on them.
|
||||||
* It then dumps all Boot Keyboard and Mouse Input Reports
|
* It then dumps all Boot Keyboard and Mouse Input Reports
|
||||||
*/
|
*/
|
||||||
@ -77,6 +77,10 @@ static enum {
|
|||||||
static le_device_addr_t remote_device;
|
static le_device_addr_t remote_device;
|
||||||
static hci_con_handle_t connection_handle;
|
static hci_con_handle_t connection_handle;
|
||||||
static uint16_t hids_cid;
|
static uint16_t hids_cid;
|
||||||
|
static hid_protocol_mode_t protocol_mode = HID_PROTOCOL_MODE_REPORT;
|
||||||
|
|
||||||
|
// SDP
|
||||||
|
static uint8_t hid_descriptor_storage[500];
|
||||||
|
|
||||||
// used to implement connection timeout and reconnect timer
|
// used to implement connection timeout and reconnect timer
|
||||||
static btstack_timer_source_t connection_timer;
|
static btstack_timer_source_t connection_timer;
|
||||||
@ -138,31 +142,56 @@ static const uint8_t keytable_us_shift[] = {
|
|||||||
'6', '7', '8', '9', '0', '.', 0xb1, /* 97-100 */
|
'6', '7', '8', '9', '0', '.', 0xb1, /* 97-100 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @section HOG Boot Keyboard Handler
|
|
||||||
* @text Boot Keyboard Input Report contains a report of format
|
|
||||||
* [ modifier, reserved, 6 x usage for key 1..6 from keyboard usage]
|
|
||||||
* Track new usages, map key usage to actual character and simulate terminal
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* last_keys stores keyboard report to detect new key down events */
|
|
||||||
#define NUM_KEYS 6
|
#define NUM_KEYS 6
|
||||||
static uint8_t last_keys[NUM_KEYS];
|
static uint8_t last_keys[NUM_KEYS];
|
||||||
|
static void hid_handle_input_report(uint8_t service_index, const uint8_t * report, uint16_t report_len){
|
||||||
|
// check if HID Input Report
|
||||||
|
|
||||||
|
if (report_len < 1) return;
|
||||||
|
|
||||||
|
btstack_hid_parser_t parser;
|
||||||
|
|
||||||
|
switch (protocol_mode){
|
||||||
|
case HID_PROTOCOL_MODE_BOOT:
|
||||||
|
btstack_hid_parser_init(&parser,
|
||||||
|
hid_get_boot_descriptor_data(),
|
||||||
|
hid_get_boot_descriptor_len(),
|
||||||
|
HID_REPORT_TYPE_INPUT, report, report_len);
|
||||||
|
break;
|
||||||
|
|
||||||
static void handle_boot_keyboard_event(const uint8_t * report, uint16_t report_len){
|
default:
|
||||||
UNUSED(report_len);
|
btstack_hid_parser_init(&parser,
|
||||||
|
hids_client_descriptor_storage_get_descriptor_data(hids_cid, service_index),
|
||||||
|
hids_client_descriptor_storage_get_descriptor_len(hids_cid, service_index),
|
||||||
|
HID_REPORT_TYPE_INPUT, report, report_len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int shift = 0;
|
||||||
uint8_t new_keys[NUM_KEYS];
|
uint8_t new_keys[NUM_KEYS];
|
||||||
memset(new_keys, 0, sizeof(new_keys));
|
memset(new_keys, 0, sizeof(new_keys));
|
||||||
int new_keys_count = 0;
|
int new_keys_count = 0;
|
||||||
|
while (btstack_hid_parser_has_more(&parser)){
|
||||||
bool shift = (report[0] & 0x22) != 0;
|
uint16_t usage_page;
|
||||||
|
uint16_t usage;
|
||||||
uint8_t key_index;
|
int32_t value;
|
||||||
for (key_index = 0; key_index < NUM_KEYS; key_index++){
|
btstack_hid_parser_get_field(&parser, &usage_page, &usage, &value);
|
||||||
|
if (usage_page != 0x07) continue;
|
||||||
uint16_t usage = report[2 + key_index];
|
switch (usage){
|
||||||
if (usage == 0) continue;
|
case 0xe1:
|
||||||
|
case 0xe6:
|
||||||
|
if (value){
|
||||||
|
shift = 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case 0x00:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (usage >= sizeof(keytable_us_none)) continue;
|
if (usage >= sizeof(keytable_us_none)) continue;
|
||||||
|
|
||||||
// store new keys
|
// store new keys
|
||||||
@ -177,7 +206,6 @@ static void handle_boot_keyboard_event(const uint8_t * report, uint16_t report_l
|
|||||||
}
|
}
|
||||||
if (usage == 0) continue;
|
if (usage == 0) continue;
|
||||||
|
|
||||||
// lookup character based on usage + shift modifier
|
|
||||||
uint8_t key;
|
uint8_t key;
|
||||||
if (shift){
|
if (shift){
|
||||||
key = keytable_us_shift[usage];
|
key = keytable_us_shift[usage];
|
||||||
@ -185,36 +213,15 @@ static void handle_boot_keyboard_event(const uint8_t * report, uint16_t report_l
|
|||||||
key = keytable_us_none[usage];
|
key = keytable_us_none[usage];
|
||||||
}
|
}
|
||||||
if (key == CHAR_ILLEGAL) continue;
|
if (key == CHAR_ILLEGAL) continue;
|
||||||
if (key == CHAR_BACKSPACE){
|
if (key == CHAR_BACKSPACE){
|
||||||
printf("\b \b"); // go back one char, print space, go back one char again
|
printf("\b \b"); // go back one char, print space, go back one char again
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
printf("%c", key);
|
printf("%c", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// store current as last report
|
|
||||||
memcpy(last_keys, new_keys, NUM_KEYS);
|
memcpy(last_keys, new_keys, NUM_KEYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @section HOG Boot Mouse Handler
|
|
||||||
* @text Boot Mouse Input Report contains a report of format
|
|
||||||
* [ buttons, dx, dy, dz = scroll wheel]
|
|
||||||
* Decode packet and print on stdout
|
|
||||||
*
|
|
||||||
* @param report
|
|
||||||
* @param report_len
|
|
||||||
*/
|
|
||||||
static void handle_boot_mouse_event(const uint8_t * report, uint16_t report_len){
|
|
||||||
UNUSED(report_len);
|
|
||||||
|
|
||||||
uint8_t buttons = report[0];
|
|
||||||
int8_t dx = (int8_t) report[1];
|
|
||||||
int8_t dy = (int8_t) report[2];
|
|
||||||
int8_t dwheel = (int8_t) report[3];
|
|
||||||
printf("Mouse: %i, %i - wheel %i - buttons 0x%02x\n", dx, dy, dwheel, buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @section Test if advertisement contains HID UUID
|
* @section Test if advertisement contains HID UUID
|
||||||
* @param packet
|
* @param packet
|
||||||
@ -321,7 +328,6 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
UNUSED(size);
|
UNUSED(size);
|
||||||
|
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
uint8_t report_id;
|
|
||||||
|
|
||||||
if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){
|
if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){
|
||||||
return;
|
return;
|
||||||
@ -349,22 +355,12 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GATTSERVICE_SUBEVENT_HID_REPORT:
|
case GATTSERVICE_SUBEVENT_HID_REPORT:
|
||||||
report_id = gattservice_subevent_hid_report_get_report_id(packet);
|
hid_handle_input_report(
|
||||||
switch (report_id){
|
gattservice_subevent_hid_report_get_service_index(packet),
|
||||||
case HID_BOOT_MODE_MOUSE_ID:
|
gattservice_subevent_hid_report_get_report(packet),
|
||||||
handle_boot_mouse_event(
|
gattservice_subevent_hid_report_get_report_len(packet));
|
||||||
gattservice_subevent_hid_report_get_report(packet),
|
|
||||||
gattservice_subevent_hid_report_get_report_len(packet));
|
|
||||||
break;
|
|
||||||
case HID_BOOT_MODE_KEYBOARD_ID:
|
|
||||||
handle_boot_keyboard_event(
|
|
||||||
gattservice_subevent_hid_report_get_report(packet),
|
|
||||||
gattservice_subevent_hid_report_get_report_len(packet));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -442,7 +438,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
|
|||||||
printf("Search for HID service.\n");
|
printf("Search for HID service.\n");
|
||||||
app_state = W4_HID_CLIENT_CONNECTED;
|
app_state = W4_HID_CLIENT_CONNECTED;
|
||||||
|
|
||||||
status = hids_client_connect(connection_handle, handle_gatt_client_event, HID_PROTOCOL_MODE_REPORT, &hids_cid);
|
status = hids_client_connect(connection_handle, handle_gatt_client_event, protocol_mode, &hids_cid);
|
||||||
if (status != ERROR_CODE_SUCCESS){
|
if (status != ERROR_CODE_SUCCESS){
|
||||||
printf("HID client connection failed, status 0x%02x\n", status);
|
printf("HID client connection failed, status 0x%02x\n", status);
|
||||||
}
|
}
|
||||||
@ -514,7 +510,7 @@ int btstack_main(int argc, const char * argv[]){
|
|||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
|
||||||
/* LISTING_START(HogBootHostSetup): HID-over-GATT Boot Host Setup */
|
/* LISTING_START(HogBootHostSetup): HID-over-GATT Host Setup */
|
||||||
|
|
||||||
// register for events from HCI
|
// register for events from HCI
|
||||||
hci_event_callback_registration.callback = &packet_handler;
|
hci_event_callback_registration.callback = &packet_handler;
|
||||||
@ -531,7 +527,8 @@ int btstack_main(int argc, const char * argv[]){
|
|||||||
l2cap_init();
|
l2cap_init();
|
||||||
sm_init();
|
sm_init();
|
||||||
gatt_client_init();
|
gatt_client_init();
|
||||||
hids_client_init();
|
|
||||||
|
hids_client_init(hid_descriptor_storage, sizeof(hid_descriptor_storage));
|
||||||
|
|
||||||
/* LISTING_END */
|
/* LISTING_END */
|
||||||
|
|
||||||
|
@ -153,7 +153,10 @@ static void hids_client_descriptor_storage_delete(hids_client_t * client){
|
|||||||
|
|
||||||
const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
|
const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
|
||||||
hids_client_t * client = hids_get_client_for_cid(hids_cid);
|
hids_client_t * client = hids_get_client_for_cid(hids_cid);
|
||||||
if (!client){
|
if (client == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (service_index >= client->num_instances){
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
|
return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
|
||||||
@ -161,7 +164,10 @@ const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids
|
|||||||
|
|
||||||
uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
|
uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
|
||||||
hids_client_t * client = hids_get_client_for_cid(hids_cid);
|
hids_client_t * client = hids_get_client_for_cid(hids_cid);
|
||||||
if (!client){
|
if (client == NULL){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (service_index >= client->num_instances){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return client->services[service_index].hid_descriptor_len;
|
return client->services[service_index].hid_descriptor_len;
|
||||||
@ -198,24 +204,8 @@ static uint8_t find_report_index_for_report_id_and_type(hids_client_t * client,
|
|||||||
return HIDS_CLIENT_INVALID_REPORT_INDEX;
|
return HIDS_CLIENT_INVALID_REPORT_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hids_client_report_t * find_report_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
|
static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){
|
||||||
uint8_t i;
|
|
||||||
for (i = 0; i < client->num_reports; i++){
|
|
||||||
if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
|
|
||||||
return &client->reports[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static hids_client_report_t * get_boot_mouse_input_report(hids_client_t * client){
|
|
||||||
return find_report_for_report_id_and_type(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
|
|
||||||
}
|
|
||||||
static hids_client_report_t * get_boot_keyboard_input_report(hids_client_t * client){
|
|
||||||
return find_report_for_report_id_and_type(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type){
|
|
||||||
uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
|
uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
|
||||||
if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
return report_index;
|
return report_index;
|
||||||
@ -230,9 +220,10 @@ static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_clien
|
|||||||
client->reports[report_index].service_index = client->service_index;
|
client->reports[report_index].service_index = client->service_index;
|
||||||
client->reports[report_index].report_id = report_id;
|
client->reports[report_index].report_id = report_id;
|
||||||
client->reports[report_index].report_type = report_type;
|
client->reports[report_index].report_type = report_type;
|
||||||
|
client->reports[report_index].boot_report = boot_report;
|
||||||
|
|
||||||
// log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
|
// log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
|
||||||
log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
|
log_info("add index %d, id %d, type %d, value handle 0x%02x, properties 0x%02x", report_index, report_id, report_type, characteristic->value_handle, characteristic->properties);
|
||||||
client->num_reports++;
|
client->num_reports++;
|
||||||
return report_index;
|
return report_index;
|
||||||
} else {
|
} else {
|
||||||
@ -247,13 +238,24 @@ static void hids_client_get_characteristic_for_report_index(hids_client_t * clie
|
|||||||
characteristic->properties = client->reports[report_index].properties;
|
characteristic->properties = client->reports[report_index].properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t hids_client_get_characteristic(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type, gatt_client_characteristic_t * characteristic){
|
static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
|
||||||
uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type);
|
uint8_t i;
|
||||||
if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
|
for (i = 0; i < client->num_reports; i++){
|
||||||
return report_index;
|
if (!client->reports[i].boot_report){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hids_client_get_characteristic_for_report_index(client, report_index, characteristic);
|
return false;
|
||||||
return report_index;
|
}
|
||||||
|
|
||||||
|
static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < client->num_reports; i++){
|
||||||
|
if (client->reports[i].boot_report){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
|
static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
|
||||||
@ -294,19 +296,19 @@ static uint8_t hids_client_get_next_active_report_map_uuid_index(hids_client_t *
|
|||||||
uint8_t i;
|
uint8_t i;
|
||||||
uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
|
uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
|
||||||
|
|
||||||
for (i = client->active_index; i < client->num_reports; i++){
|
for (i = client->report_index; i < client->num_reports; i++){
|
||||||
hids_client_report_t report = client->reports[i];
|
hids_client_report_t report = client->reports[i];
|
||||||
if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
|
if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client->active_index = index;
|
client->report_index = index;
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
|
static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
|
||||||
client->active_index++;
|
client->report_index++;
|
||||||
if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
|
client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
|
||||||
return true;
|
return true;
|
||||||
@ -317,7 +319,7 @@ static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client
|
|||||||
|
|
||||||
|
|
||||||
static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
|
static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
|
||||||
client->active_index = 0;
|
client->report_index = 0;
|
||||||
|
|
||||||
if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
|
client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
|
||||||
@ -329,19 +331,35 @@ static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
|
|||||||
static uint8_t hids_client_get_next_report_index(hids_client_t * client){
|
static uint8_t hids_client_get_next_report_index(hids_client_t * client){
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
|
uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
|
||||||
for (i = client->active_index; i < client->num_reports; i++){
|
switch (client->protocol_mode){
|
||||||
hids_client_report_t report = client->reports[i];
|
case HID_PROTOCOL_MODE_REPORT:
|
||||||
if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
|
for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
|
||||||
index = i;
|
hids_client_report_t report = client->reports[i];
|
||||||
|
if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
|
||||||
|
index = i;
|
||||||
|
client->service_index = report.service_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HID_PROTOCOL_MODE_BOOT:
|
||||||
|
for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
|
||||||
|
hids_client_report_t report = client->reports[i];
|
||||||
|
if (report.boot_report){
|
||||||
|
index = i;
|
||||||
|
client->service_index = report.service_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
client->active_index = index;
|
|
||||||
|
client->report_index = index;
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hids_client_report_query_next_report(hids_client_t * client){
|
static bool hids_client_report_query_next_report(hids_client_t * client){
|
||||||
client->active_index++;
|
client->report_index++;
|
||||||
if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
|
client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
|
||||||
return true;
|
return true;
|
||||||
@ -350,8 +368,7 @@ static bool hids_client_report_query_next_report(hids_client_t * client){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool hids_client_report_query_init(hids_client_t * client){
|
static bool hids_client_report_query_init(hids_client_t * client){
|
||||||
client->service_index = 0;
|
client->report_index = 0;
|
||||||
client->active_index = 0;
|
|
||||||
|
|
||||||
if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
|
client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
|
||||||
@ -360,6 +377,62 @@ static bool hids_client_report_query_init(hids_client_t * client){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
|
||||||
|
uint8_t i;
|
||||||
|
uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
|
||||||
|
|
||||||
|
switch (client->protocol_mode){
|
||||||
|
case HID_PROTOCOL_MODE_REPORT:
|
||||||
|
for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
|
||||||
|
hids_client_report_t report = client->reports[i];
|
||||||
|
if (report.report_type != HID_REPORT_TYPE_INPUT){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!report.boot_report){
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_PROTOCOL_MODE_BOOT:
|
||||||
|
for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
|
||||||
|
hids_client_report_t report = client->reports[i];
|
||||||
|
if (report.report_type != HID_REPORT_TYPE_INPUT){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (report.boot_report){
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->report_index = index;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hids_client_report_next_notification_report_index(hids_client_t * client){
|
||||||
|
client->report_index++;
|
||||||
|
if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
|
client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hids_client_report_notifications_init(hids_client_t * client){
|
||||||
|
client->report_index = 0;
|
||||||
|
|
||||||
|
if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
|
client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
|
static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
|
||||||
hids_client_t * client = btstack_memory_hids_client_get();
|
hids_client_t * client = btstack_memory_hids_client_get();
|
||||||
if (!client){
|
if (!client){
|
||||||
@ -375,6 +448,12 @@ static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void hids_finalize_client(hids_client_t * client){
|
static void hids_finalize_client(hids_client_t * client){
|
||||||
|
// stop listening
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < client->num_reports; i++){
|
||||||
|
gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
|
||||||
|
}
|
||||||
|
|
||||||
hids_client_descriptor_storage_delete(client);
|
hids_client_descriptor_storage_delete(client);
|
||||||
btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
|
btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
|
||||||
btstack_memory_hids_client_free(client);
|
btstack_memory_hids_client_free(client);
|
||||||
@ -395,12 +474,46 @@ static void hids_emit_connection_established(hids_client_t * client, uint8_t sta
|
|||||||
(*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
(*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
|
||||||
|
uint16_t pos = 0;
|
||||||
|
buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
|
||||||
|
pos++; // skip len
|
||||||
|
buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
|
||||||
|
little_endian_store_16(buffer, pos, client->cid);
|
||||||
|
pos += 2;
|
||||||
|
buffer[pos++] = client->reports[report_index].service_index;
|
||||||
|
buffer[pos++] = client->reports[report_index].report_id;
|
||||||
|
little_endian_store_16(buffer, pos, report_len + 1);
|
||||||
|
pos += 2;
|
||||||
|
buffer[pos++] = client->reports[report_index].report_id;
|
||||||
|
buffer[1] = pos + report_len + 1 - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||||
|
UNUSED(packet_type);
|
||||||
|
UNUSED(channel);
|
||||||
|
UNUSED(size);
|
||||||
|
if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
|
||||||
|
|
||||||
|
hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
|
||||||
|
btstack_assert(client != NULL);
|
||||||
|
|
||||||
|
uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
|
||||||
|
if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * in_place_event = &packet[-1];
|
||||||
|
hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
|
||||||
|
|
||||||
|
(*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void hids_run_for_client(hids_client_t * client){
|
static void hids_run_for_client(hids_client_t * client){
|
||||||
uint8_t att_status;
|
uint8_t att_status;
|
||||||
gatt_client_service_t service;
|
gatt_client_service_t service;
|
||||||
gatt_client_characteristic_t characteristic;
|
gatt_client_characteristic_t characteristic;
|
||||||
uint8_t report_index;
|
|
||||||
|
|
||||||
switch (client->state){
|
switch (client->state){
|
||||||
case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
|
case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
|
||||||
@ -420,51 +533,24 @@ static void hids_run_for_client(hids_client_t * client){
|
|||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD:
|
case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
|
||||||
client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
|
|
||||||
|
|
||||||
report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic);
|
|
||||||
if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
|
||||||
att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
|
|
||||||
client->reports[report_index].value_handle = 0;
|
|
||||||
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE:
|
|
||||||
client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
|
|
||||||
|
|
||||||
report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic);
|
|
||||||
|
|
||||||
if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
|
||||||
att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
|
|
||||||
client->reports[report_index].value_handle = 0;
|
|
||||||
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE:
|
|
||||||
client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
|
|
||||||
att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
|
att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
|
||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
|
|
||||||
client->protocol_mode = client->required_protocol_mode;
|
client->protocol_mode = client->required_protocol_mode;
|
||||||
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
if (hids_client_report_query_init(client)){
|
||||||
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
||||||
|
hids_finalize_client(client);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HIDS_CLIENT_W2_SEND_REPORT:{
|
case HIDS_CLIENT_W2_SEND_REPORT:{
|
||||||
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
||||||
|
|
||||||
att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
|
att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
|
||||||
client->reports[client->active_index].value_handle,
|
client->reports[client->report_index].value_handle,
|
||||||
client->report_len, (uint8_t *)client->report);
|
client->report_len, (uint8_t *)client->report);
|
||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
break;
|
break;
|
||||||
@ -472,7 +558,7 @@ static void hids_run_for_client(hids_client_t * client){
|
|||||||
|
|
||||||
case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
|
case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
|
||||||
client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
|
client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
|
||||||
att_status = gatt_client_read_long_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
|
att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
|
||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -489,7 +575,7 @@ static void hids_run_for_client(hids_client_t * client){
|
|||||||
case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
|
case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
|
||||||
client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
|
client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
|
||||||
|
|
||||||
att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_index].value_handle);
|
att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->report_index].value_handle);
|
||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -505,7 +591,7 @@ static void hids_run_for_client(hids_client_t * client){
|
|||||||
case HIDS_CLIENT_STATE_W2_FIND_REPORT:
|
case HIDS_CLIENT_STATE_W2_FIND_REPORT:
|
||||||
client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
|
client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
|
||||||
client->descriptor_handle = 0;
|
client->descriptor_handle = 0;
|
||||||
hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
|
hids_client_get_characteristic_for_report_index(client, client->report_index, &characteristic);
|
||||||
att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
|
att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
|
||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
break;
|
break;
|
||||||
@ -517,55 +603,39 @@ static void hids_run_for_client(hids_client_t * client){
|
|||||||
client->descriptor_handle = 0;
|
client->descriptor_handle = 0;
|
||||||
UNUSED(att_status);
|
UNUSED(att_status);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
|
||||||
|
client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
|
||||||
|
|
||||||
|
characteristic.value_handle = client->reports[client->report_index].value_handle;
|
||||||
|
characteristic.end_handle = client->reports[client->report_index].end_handle;
|
||||||
|
characteristic.properties = client->reports[client->report_index].properties;
|
||||||
|
|
||||||
|
att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
|
||||||
|
|
||||||
|
if (att_status != ERROR_CODE_SUCCESS){
|
||||||
|
if (hids_client_report_next_notification_report_index(client)){
|
||||||
|
hids_run_for_client(client);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
||||||
|
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||||
|
} else {
|
||||||
|
gatt_client_listen_for_characteristic_value_updates(
|
||||||
|
&client->reports[client->report_index].notification_listener,
|
||||||
|
&handle_report_hid_event, client->con_handle, &characteristic);
|
||||||
|
|
||||||
|
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
||||||
|
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||||
|
}
|
||||||
|
UNUSED(att_status);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){
|
|
||||||
uint16_t pos = 0;
|
|
||||||
buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
|
|
||||||
pos++; // skip len
|
|
||||||
buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
|
|
||||||
little_endian_store_16(buffer, pos, client->cid);
|
|
||||||
pos += 2;
|
|
||||||
buffer[pos++] = report_id;
|
|
||||||
little_endian_store_16(buffer, pos, report_len);
|
|
||||||
pos += 2;
|
|
||||||
buffer[1] = pos + report_len - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
|
||||||
UNUSED(packet_type);
|
|
||||||
UNUSED(channel);
|
|
||||||
UNUSED(size);
|
|
||||||
|
|
||||||
if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
|
|
||||||
|
|
||||||
hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
|
|
||||||
btstack_assert(client != NULL);
|
|
||||||
|
|
||||||
uint8_t * in_place_event = packet;
|
|
||||||
hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet));
|
|
||||||
(*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
|
||||||
UNUSED(packet_type);
|
|
||||||
UNUSED(channel);
|
|
||||||
UNUSED(size);
|
|
||||||
|
|
||||||
if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
|
|
||||||
|
|
||||||
hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
|
|
||||||
btstack_assert(client != NULL);
|
|
||||||
|
|
||||||
uint8_t * in_place_event = packet;
|
|
||||||
hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet));
|
|
||||||
(*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||||
UNUSED(packet_type);
|
UNUSED(packet_type);
|
||||||
@ -578,12 +648,11 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
gatt_client_characteristic_t characteristic;
|
gatt_client_characteristic_t characteristic;
|
||||||
gatt_client_characteristic_descriptor_t characteristic_descriptor;
|
gatt_client_characteristic_descriptor_t characteristic_descriptor;
|
||||||
|
|
||||||
hids_client_report_t * boot_keyboard_report;
|
// hids_client_report_t * boot_keyboard_report;
|
||||||
hids_client_report_t * boot_mouse_report;
|
// hids_client_report_t * boot_mouse_report;
|
||||||
const uint8_t * characteristic_descriptor_value;
|
const uint8_t * characteristic_descriptor_value;
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
uint8_t report_index;
|
uint8_t report_index;
|
||||||
uint8_t next_service_index;
|
|
||||||
|
|
||||||
const uint8_t * descriptor;
|
const uint8_t * descriptor;
|
||||||
uint16_t descriptor_len;
|
uint16_t descriptor_len;
|
||||||
@ -599,15 +668,17 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_service_index = client->num_instances;
|
if (client->num_instances < MAX_NUM_HID_SERVICES){
|
||||||
if (next_service_index < MAX_NUM_HID_SERVICES){
|
uint8_t index = client->num_instances;
|
||||||
gatt_event_service_query_result_get_service(packet, &service);
|
gatt_event_service_query_result_get_service(packet, &service);
|
||||||
client->services[next_service_index].start_handle = service.start_group_handle;
|
client->services[index].start_handle = service.start_group_handle;
|
||||||
client->services[next_service_index].end_handle = service.end_group_handle;
|
client->services[index].end_handle = service.end_group_handle;
|
||||||
|
|
||||||
hids_client_descriptor_storage_init(client, next_service_index);
|
|
||||||
client->num_instances++;
|
client->num_instances++;
|
||||||
}
|
|
||||||
|
hids_client_descriptor_storage_init(client, index);
|
||||||
|
} else {
|
||||||
|
log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances + 1, MAX_NUM_HID_SERVICES);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
|
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
|
||||||
@ -636,19 +707,19 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
|
case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
|
||||||
hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
|
hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
|
case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
|
||||||
hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
|
hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
|
case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
|
||||||
hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT);
|
hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
|
case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
|
||||||
hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
|
hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
|
case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
|
||||||
@ -658,12 +729,10 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
|
case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
|
||||||
// printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
|
// printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
|
||||||
hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
|
case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
|
||||||
// printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
|
// printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
|
||||||
hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -677,10 +746,9 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
|
client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
|
||||||
btstack_assert(client != NULL);
|
btstack_assert(client != NULL);
|
||||||
|
|
||||||
// printf("HID descriptor found\n");
|
|
||||||
descriptor_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
|
descriptor_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
|
||||||
descriptor = gatt_event_characteristic_value_query_result_get_value(packet);
|
descriptor = gatt_event_characteristic_value_query_result_get_value(packet);
|
||||||
|
|
||||||
for (i = 0; i < descriptor_len; i++){
|
for (i = 0; i < descriptor_len; i++){
|
||||||
bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor[i]);
|
bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor[i]);
|
||||||
if (!stored){
|
if (!stored){
|
||||||
@ -699,7 +767,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
|
case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
|
||||||
// setup for descriptor value query
|
// setup for descriptor value query
|
||||||
if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
|
if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
|
||||||
report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED);
|
report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED, false);
|
||||||
|
|
||||||
if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
|
||||||
client->reports[report_index].value_handle = characteristic_descriptor.handle;
|
client->reports[report_index].value_handle = characteristic_descriptor.handle;
|
||||||
@ -738,13 +806,13 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
|
case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
|
||||||
client->reports[client->active_index].report_id = characteristic_descriptor_value[0];
|
client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
|
||||||
client->reports[client->active_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
|
client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
|
||||||
log_info("update index %d, id %d, type %d, value handle 0x%02x",
|
log_info("update index %d, id %d, type %d, value handle 0x%02x",
|
||||||
client->active_index,
|
client->report_index,
|
||||||
client->reports[client->active_index].report_id,
|
client->reports[client->report_index].report_id,
|
||||||
client->reports[client->active_index].report_type,
|
client->reports[client->report_index].report_type,
|
||||||
client->reports[client->active_index].value_handle);
|
client->reports[client->report_index].value_handle);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -773,11 +841,6 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->num_instances > MAX_NUM_HID_SERVICES) {
|
|
||||||
log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES);
|
|
||||||
client->num_instances = MAX_NUM_HID_SERVICES;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
|
client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
|
||||||
client->service_index = 0;
|
client->service_index = 0;
|
||||||
break;
|
break;
|
||||||
@ -788,43 +851,67 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
hids_finalize_client(client);
|
hids_finalize_client(client);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((client->service_index + 1) < client->num_instances){
|
||||||
|
// discover characteristics of next service
|
||||||
|
client->service_index++;
|
||||||
|
client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (client->required_protocol_mode){
|
switch (client->required_protocol_mode){
|
||||||
case HID_PROTOCOL_MODE_BOOT:
|
case HID_PROTOCOL_MODE_REPORT:
|
||||||
if (get_boot_keyboard_input_report(client) != NULL){
|
client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
|
||||||
client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD;
|
if (hid_clients_has_reports_in_report_mode(client)){
|
||||||
break;
|
client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
|
||||||
}
|
|
||||||
if (get_boot_mouse_input_report(client) != NULL){
|
|
||||||
client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
hids_emit_connection_established(client, att_status);
|
||||||
hids_finalize_client(client);
|
hids_finalize_client(client);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case HID_PROTOCOL_MODE_REPORT:
|
|
||||||
if ((client->service_index + 1) < client->num_instances){
|
|
||||||
// discover characteristics of next service
|
|
||||||
client->service_index++;
|
|
||||||
client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// 1. we need to get HID Descriptor and
|
case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
|
||||||
// 2. get external Report characteristics if referenced from Report Map
|
if (hid_clients_has_reports_in_report_mode(client)){
|
||||||
if (hids_client_report_map_query_init(client)){
|
client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (hid_clients_has_reports_in_boot_mode(client)){
|
||||||
|
if (client->protocol_mode_value_handle != 0){
|
||||||
|
client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
hids_emit_connection_established(client, att_status);
|
||||||
hids_finalize_client(client);
|
hids_finalize_client(client);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (hid_clients_has_reports_in_boot_mode(client)){
|
||||||
|
if (client->protocol_mode_value_handle != 0){
|
||||||
|
client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hids_emit_connection_established(client, att_status);
|
||||||
|
hids_finalize_client(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. we need to get HID Descriptor and
|
||||||
|
// 2. get external Report characteristics if referenced from Report Map
|
||||||
|
if (hids_client_report_map_query_init(client)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
||||||
|
hids_finalize_client(client);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
// HID descriptor found
|
// HID descriptor found
|
||||||
case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
|
case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
|
||||||
if (att_status != ATT_ERROR_SUCCESS){
|
if (att_status != ATT_ERROR_SUCCESS){
|
||||||
@ -868,7 +955,6 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
|
case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
|
||||||
|
|
||||||
// discover characteristic descriptor for all Report characteristics,
|
// discover characteristic descriptor for all Report characteristics,
|
||||||
// then read value of characteristic descriptor to get Report ID
|
// then read value of characteristic descriptor to get Report ID
|
||||||
if (hids_client_report_query_init(client)){
|
if (hids_client_report_query_init(client)){
|
||||||
@ -901,66 +987,19 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO continue with report mode
|
if (hids_client_report_notifications_init(client)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
|
case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
|
||||||
boot_keyboard_report = get_boot_keyboard_input_report(client);
|
if (hids_client_report_next_notification_report_index(client)){
|
||||||
if (boot_keyboard_report != NULL){
|
|
||||||
// setup listener
|
|
||||||
characteristic.value_handle = boot_keyboard_report->value_handle;
|
|
||||||
gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if there is mouse input report
|
|
||||||
boot_mouse_report = get_boot_mouse_input_report(client);
|
|
||||||
if (boot_mouse_report != NULL){
|
|
||||||
client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if none of the reports is found bail out
|
|
||||||
if (!boot_mouse_report && !boot_keyboard_report){
|
|
||||||
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|
||||||
hids_finalize_client(client);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||||
// set protocol
|
|
||||||
if (client->protocol_mode_value_handle != 0){
|
|
||||||
client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
|
|
||||||
} else {
|
|
||||||
client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
|
|
||||||
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
|
||||||
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
|
||||||
}
|
|
||||||
hids_run_for_client(client);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
|
|
||||||
boot_mouse_report = get_boot_mouse_input_report(client);
|
|
||||||
if (boot_mouse_report != NULL){
|
|
||||||
// setup listener
|
|
||||||
characteristic.value_handle = boot_mouse_report->value_handle;
|
|
||||||
gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic);
|
|
||||||
}
|
|
||||||
|
|
||||||
boot_keyboard_report = get_boot_keyboard_input_report(client);
|
|
||||||
if (!boot_mouse_report && !boot_keyboard_report){
|
|
||||||
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|
||||||
hids_finalize_client(client);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->protocol_mode_value_handle != 0){
|
|
||||||
client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
|
|
||||||
} else {
|
|
||||||
client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
|
|
||||||
client->state = HIDS_CLIENT_STATE_CONNECTED;
|
|
||||||
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1038,7 +1077,7 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
client->state = HIDS_CLIENT_W2_SEND_REPORT;
|
client->state = HIDS_CLIENT_W2_SEND_REPORT;
|
||||||
client->active_index = report_index;
|
client->report_index = report_index;
|
||||||
client->report = report;
|
client->report = report;
|
||||||
client->report_len = report_len;
|
client->report_len = report_len;
|
||||||
|
|
||||||
@ -1046,6 +1085,9 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint
|
|||||||
return ERROR_CODE_SUCCESS;
|
return ERROR_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hids_client_init(void){}
|
void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
|
||||||
|
hids_client_descriptor_storage = hid_descriptor_storage;
|
||||||
|
hids_client_descriptor_storage_len = hid_descriptor_storage_len;
|
||||||
|
}
|
||||||
|
|
||||||
void hids_client_deinit(void){}
|
void hids_client_deinit(void){}
|
||||||
|
@ -67,6 +67,8 @@ typedef enum {
|
|||||||
HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC,
|
HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC,
|
||||||
HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT,
|
HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT,
|
||||||
|
|
||||||
|
HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE,
|
||||||
|
|
||||||
// for each REPORT_MAP characteristic, read HID Descriptor (Report Map Characteristic Value)
|
// for each REPORT_MAP characteristic, read HID Descriptor (Report Map Characteristic Value)
|
||||||
HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR,
|
HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR,
|
||||||
HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR,
|
HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR,
|
||||||
@ -91,16 +93,9 @@ typedef enum {
|
|||||||
HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE,
|
HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE,
|
||||||
HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE,
|
HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE,
|
||||||
|
|
||||||
// Boot Mode
|
HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS,
|
||||||
HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD,
|
HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED,
|
||||||
HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED,
|
|
||||||
HIDS_CLIENT_STATE_W2_ENABLE_MOUSE,
|
|
||||||
HIDS_CLIENT_STATE_W4_MOUSE_ENABLED,
|
|
||||||
|
|
||||||
|
|
||||||
HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE,
|
|
||||||
HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE,
|
|
||||||
|
|
||||||
HIDS_CLIENT_STATE_CONNECTED,
|
HIDS_CLIENT_STATE_CONNECTED,
|
||||||
HIDS_CLIENT_W2_SEND_REPORT
|
HIDS_CLIENT_W2_SEND_REPORT
|
||||||
} hid_service_client_state_t;
|
} hid_service_client_state_t;
|
||||||
@ -121,7 +116,8 @@ typedef struct {
|
|||||||
uint8_t service_index;
|
uint8_t service_index;
|
||||||
uint8_t report_id;
|
uint8_t report_id;
|
||||||
hid_report_type_t report_type;
|
hid_report_type_t report_type;
|
||||||
gatt_client_notification_t notifications;
|
uint8_t boot_report;
|
||||||
|
gatt_client_notification_t notification_listener;
|
||||||
} hids_client_report_t;
|
} hids_client_report_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -163,7 +159,7 @@ typedef struct {
|
|||||||
uint8_t num_reports;
|
uint8_t num_reports;
|
||||||
|
|
||||||
// index used for report and report map search
|
// index used for report and report map search
|
||||||
uint8_t active_index;
|
uint8_t report_index;
|
||||||
uint16_t descriptor_handle;
|
uint16_t descriptor_handle;
|
||||||
uint16_t report_len;
|
uint16_t report_len;
|
||||||
const uint8_t * report;
|
const uint8_t * report;
|
||||||
@ -174,8 +170,10 @@ typedef struct {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize Battery Service.
|
* @brief Initialize Battery Service.
|
||||||
|
* @param hid_descriptor_storage
|
||||||
|
* @param hid_descriptor_storage_len
|
||||||
*/
|
*/
|
||||||
void hids_client_init(void);
|
void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len);
|
||||||
|
|
||||||
/* @brief Connect to HID Services of remote device.
|
/* @brief Connect to HID Services of remote device.
|
||||||
*
|
*
|
||||||
@ -204,8 +202,18 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint
|
|||||||
*/
|
*/
|
||||||
uint8_t hids_client_disconnect(uint16_t hids_cid);
|
uint8_t hids_client_disconnect(uint16_t hids_cid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get descriptor data
|
||||||
|
* @param hid_cid
|
||||||
|
* @result data
|
||||||
|
*/
|
||||||
const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index);
|
const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get descriptor length
|
||||||
|
* @param hid_cid
|
||||||
|
* @result length
|
||||||
|
*/
|
||||||
uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index);
|
uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
141
src/hid.c
Normal file
141
src/hid.c
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 BlueKitchen GmbH
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holders nor the names of
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
* 4. Any redistribution, use, or modification is done solely for
|
||||||
|
* personal benefit and not for any commercial purpose or for
|
||||||
|
* monetary gain.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||||
|
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||||
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Please inquire about commercial licensing options at
|
||||||
|
* contact@bluekitchen-gmbh.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hid.h"
|
||||||
|
|
||||||
|
|
||||||
|
// from USB HID Specification 1.1, Appendix B.1
|
||||||
|
const uint8_t hid_descriptor_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
|
||||||
|
|
||||||
|
// Mouse
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x02, // USAGE (Mouse)
|
||||||
|
0xa1, 0x01, // COLLECTION (Application)
|
||||||
|
|
||||||
|
0x85, 0x01, // Report ID 1
|
||||||
|
|
||||||
|
0x09, 0x01, // USAGE (Pointer)
|
||||||
|
|
||||||
|
0xa1, 0x00, // COLLECTION (Physical)
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
0x05, 0x09, // USAGE_PAGE (Button)
|
||||||
|
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||||
|
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
0x95, 0x03, // REPORT_COUNT (3)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x75, 0x05, // REPORT_SIZE (5)
|
||||||
|
0x81, 0x03, // INPUT (Cnst,Var,Abs)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x30, // USAGE (X)
|
||||||
|
0x09, 0x31, // USAGE (Y)
|
||||||
|
0x15, 0x81, // LOGICAL_MINIMUM (-127)
|
||||||
|
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x95, 0x02, // REPORT_COUNT (2)
|
||||||
|
0x81, 0x06, // INPUT (Data,Var,Rel)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
0xc0, // END_COLLECTION
|
||||||
|
0xc0 // END_COLLECTION
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t * hid_get_boot_descriptor_data(void){
|
||||||
|
return &hid_descriptor_boot_mode[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hid_get_boot_descriptor_len(void){
|
||||||
|
return sizeof(hid_descriptor_boot_mode);
|
||||||
|
}
|
||||||
|
|
19
src/hid.h
19
src/hid.h
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 BlueKitchen GmbH
|
* Copyright (C) 2021 BlueKitchen GmbH
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@ -42,7 +42,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* API_START */
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#define HID_BOOT_MODE_KEYBOARD_ID 1
|
#define HID_BOOT_MODE_KEYBOARD_ID 1
|
||||||
#define HID_BOOT_MODE_MOUSE_ID 2
|
#define HID_BOOT_MODE_MOUSE_ID 2
|
||||||
@ -105,6 +106,20 @@ typedef enum {
|
|||||||
HID_REPORT_ID_INVALID
|
HID_REPORT_ID_INVALID
|
||||||
} hid_report_id_status_t;
|
} hid_report_id_status_t;
|
||||||
|
|
||||||
|
/* API_START */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get boot descriptor data
|
||||||
|
* @result data
|
||||||
|
*/
|
||||||
|
const uint8_t * hid_get_boot_descriptor_data(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get boot descriptor length
|
||||||
|
* @result length
|
||||||
|
*/
|
||||||
|
uint16_t hid_get_boot_descriptor_len(void);
|
||||||
|
|
||||||
/* API_END */
|
/* API_END */
|
||||||
|
|
||||||
#if defined __cplusplus
|
#if defined __cplusplus
|
||||||
|
Loading…
x
Reference in New Issue
Block a user