hids_client: fix report lookup, implement hids_client_send_get_report

This commit is contained in:
Milanka Ringwald 2021-03-25 16:05:32 +01:00
parent 1914678990
commit 83d7ed1cf6
3 changed files with 212 additions and 37 deletions

View File

@ -250,14 +250,59 @@ static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16
return false;
}
static uint8_t find_input_report_index_for_report_id(hids_client_t * client, uint8_t report_id){
uint8_t i;
switch (client->protocol_mode){
case HID_PROTOCOL_MODE_BOOT:
for (i = 0; i < client->num_reports; i++){
if (!client->reports[i].boot_report){
continue;
}
if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type != HID_REPORT_TYPE_OUTPUT)){
return i;
}
}
break;
default:
for (i = 0; i < client->num_reports; i++){
if (client->reports[i].boot_report){
continue;
}
if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type != HID_REPORT_TYPE_OUTPUT)){
return i;
}
}
break;
}
return HIDS_CLIENT_INVALID_REPORT_INDEX;
}
static uint8_t find_report_index_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
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 i;
}
switch (client->protocol_mode){
case HID_PROTOCOL_MODE_BOOT:
for (i = 0; i < client->num_reports; i++){
if (!client->reports[i].boot_report){
continue;
}
if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
return i;
}
}
break;
default:
for (i = 0; i < client->num_reports; i++){
if (client->reports[i].boot_report){
continue;
}
if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
return i;
}
}
break;
}
return HIDS_CLIENT_INVALID_REPORT_INDEX;
}
@ -532,13 +577,14 @@ static void hids_client_setup_report_event(hids_client_t * client, uint8_t repor
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;
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) {
static void handle_notification_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));
@ -549,12 +595,34 @@ static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8
return;
}
uint8_t * in_place_event = &packet[-1];
uint8_t * in_place_event = &packet[-2];
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);
(*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
}
static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(packet_type);
UNUSED(channel);
if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
btstack_assert(client != NULL);
if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
return;
}
client->state = HIDS_CLIENT_STATE_CONNECTED;
uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
return;
}
uint8_t * in_place_event = &packet[-2];
hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
(*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
}
static void hids_run_for_client(hids_client_t * client){
uint8_t att_status;
@ -600,16 +668,6 @@ static void hids_run_for_client(hids_client_t * client){
hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
hids_finalize_client(client);
break;
case HIDS_CLIENT_W2_SEND_REPORT:{
client->state = HIDS_CLIENT_STATE_CONNECTED;
att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
client->reports[client->report_index].value_handle,
client->report_len, (uint8_t *)client->report);
UNUSED(att_status);
break;
}
case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
#ifdef ENABLE_TESTING_SUPPORT
@ -663,10 +721,10 @@ static void hids_run_for_client(hids_client_t * client){
case HIDS_CLIENT_STATE_W2_FIND_REPORT:
#ifdef ENABLE_TESTING_SUPPORT
printf("\nQuery Report (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
client->reports[client->report_index].value_handle,
printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
client->report_index,
client->reports[client->report_index].service_index);
client->reports[client->report_index].service_index,
client->reports[client->report_index].value_handle);
#endif
client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
client->descriptor_handle = 0;
@ -681,12 +739,6 @@ static void hids_run_for_client(hids_client_t * client){
break;
case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
#ifdef ENABLE_TESTING_SUPPORT
printf("\nQuery Report ID and Type (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
client->reports[client->report_index].value_handle,
client->report_index,
client->reports[client->report_index].service_index);
#endif
client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
// result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
@ -715,14 +767,60 @@ static void hids_run_for_client(hids_client_t * client){
} else {
gatt_client_listen_for_characteristic_value_updates(
&client->reports[client->report_index].notification_listener,
&handle_report_hid_event, client->con_handle, &characteristic);
&handle_notification_event, client->con_handle, &characteristic);
client->state = HIDS_CLIENT_STATE_CONNECTED;
hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
}
UNUSED(att_status);
break;
case HIDS_CLIENT_W2_SEND_REPORT:
#ifdef ENABLE_TESTING_SUPPORT
printf(" Send report [%d, %d, 0x%04X]:\n",
client->report_index,
client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
#endif
client->state = HIDS_CLIENT_STATE_CONNECTED;
att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
client->reports[client->report_index].value_handle,
client->report_len, (uint8_t *)client->report);
UNUSED(att_status);
break;
case HIDS_CLIENT_W2_SEND_GET_REPORT:
#ifdef ENABLE_TESTING_SUPPORT
printf(" Get report [ID %d, Service %d, handle 0x%04X]:\n",
client->reports[client->report_index].report_id,
client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
#endif
client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
att_status = gatt_client_read_value_of_characteristic_using_value_handle(
&handle_report_event,
client->con_handle,
client->reports[client->report_index].value_handle);
UNUSED(att_status);
break;
#ifdef ENABLE_TESTING_SUPPORT
case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
// end of write marked in GATT_EVENT_QUERY_COMPLETE
att_status = gatt_client_read_value_of_characteristic_using_value_handle(
&handle_gatt_client_event,
client->con_handle,
client->reports[client->report_index].ccc_handle);
break;
#endif
default:
break;
}
@ -896,6 +994,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
#ifdef ENABLE_TESTING_SUPPORT
if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
printf(" Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
characteristic_descriptor.handle,
characteristic_descriptor.uuid16);
@ -908,6 +1007,17 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
}
break;
#ifdef ENABLE_TESTING_SUPPORT
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
btstack_assert(client != NULL);
printf(" Received CCC value: ");
printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet));
client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
break;
#endif
case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
btstack_assert(client != NULL);
@ -936,17 +1046,15 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
#ifdef ENABLE_TESTING_SUPPORT
printf(" Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
printf(" Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
client->reports[client->report_index].report_id,
client->reports[client->report_index].report_type,
client->report_index, client->service_index,
client->external_reports[client->report_index].external_report_reference_uuid);
client->report_index, client->service_index);
#endif
}
break;
default:
printf("GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT 3");
break;
}
break;
@ -1097,7 +1205,6 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
if (client->descriptor_handle != 0){
printf("descriptor found\n");
client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
break;
}
@ -1190,6 +1297,7 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint
}
uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
@ -1214,6 +1322,33 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint
return ERROR_CODE_SUCCESS;
}
uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){
hids_client_t * client = hids_get_client_for_cid(hids_cid);
if (client == NULL){
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
return ERROR_CODE_COMMAND_DISALLOWED;
}
uint8_t report_index = find_input_report_index_for_report_id(client, report_id);
if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
client->report_index = report_index;
#ifdef ENABLE_TESTING_SUPPORT
client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
#else
client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
#endif
hids_run_for_client(client);
return ERROR_CODE_SUCCESS;
}
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;

View File

@ -97,7 +97,15 @@ typedef enum {
HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED,
HIDS_CLIENT_STATE_CONNECTED,
HIDS_CLIENT_W2_SEND_REPORT
HIDS_CLIENT_W2_SEND_REPORT,
HIDS_CLIENT_W2_SEND_GET_REPORT,
HIDS_CLIENT_W4_GET_REPORT_RESULT,
#ifdef ENABLE_TESTING_SUPPORT
HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION,
HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT,
#endif
} hid_service_client_state_t;
@ -110,7 +118,8 @@ typedef struct {
uint16_t properties;
// UUID of external Report characteristic, stored in Report Map descriptor EXTERNAL_REPORT_REFERENCE
uint16_t external_report_reference_uuid;
uint16_t external_report_reference_uuid;
uint16_t ccc_handle;
// service mapping
uint8_t service_index;
@ -198,6 +207,8 @@ uint8_t hids_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_
*/
uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len);
uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id);
/**
* @brief Disconnect from Battery Service.
* @param hids_cid

View File

@ -389,6 +389,14 @@ static void hid_service_gatt_client_event_handler(uint8_t packet_type, uint16_t
break;
case GATTSERVICE_SUBEVENT_HID_REPORT:
printf(" Received report [ID %d, Service %d]: ",
gattservice_subevent_hid_report_get_report_id(packet),
gattservice_subevent_hid_report_get_service_index(packet));
// first byte id report ID
printf_hexdump(gattservice_subevent_hid_report_get_report(packet) + 1,
gattservice_subevent_hid_report_get_report_len(packet) - 1);
hid_handle_input_report(
gattservice_subevent_hid_report_get_service_index(packet),
gattservice_subevent_hid_report_get_report(packet),
@ -491,6 +499,8 @@ static void show_usage(void){
printf("h - connect to HID Service client\n");
printf("b - connect to Battery Service Client\n");
printf("d - connect to Device Information Service Client\n");
printf("Ctrl-c - exit\n");
printf("---\n");
}
@ -544,6 +554,25 @@ static void stdin_process(char character){
printf("Connect to Device Information Service\n");
hog_host_connect_device_information_client();
break;
case '1':
printf("Get report with ID 1\n");
hids_client_send_get_report(hids_cid, 1);
break;
case '2':
printf("Get report with ID 4\n");
hids_client_send_get_report(hids_cid, 4);
break;
case 'r':{
uint8_t report[] = {0, 0, 0, 0, 0, 0, 0, 0};
printf("Send output report with id 0x01\n");
hids_client_send_report(hids_cid, 0x01, report, sizeof(report));
break;
}
case '\n':
case '\r':
break;