diff --git a/src/ble/gatt-service/hids_client.c b/src/ble/gatt-service/hids_client.c index 7be61122e..d0806f96e 100644 --- a/src/ble/gatt-service/hids_client.c +++ b/src/ble/gatt-service/hids_client.c @@ -582,6 +582,27 @@ static void hids_client_setup_report_event(hids_client_t * client, uint8_t repor } +static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){ + if (value_len != 4) return; + + uint8_t event[11]; + int pos = 0; + event[pos++] = HCI_EVENT_GATTSERVICE_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION; + little_endian_store_16(event, pos, client->cid); + pos += 2; + event[pos++] = client->service_index; + + memcpy(event+pos, value, 3); + pos += 3; + event[pos++] = (value[3] & 0x02) >> 1; + event[pos++] = value[3] & 0x01; + + (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + + static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { UNUSED(packet_type); UNUSED(channel); @@ -795,13 +816,14 @@ static void hids_run_for_client(hids_client_t * client){ case HIDS_CLIENT_W2_SEND_GET_REPORT: #ifdef ENABLE_TESTING_SUPPORT - printf(" Get report [ID %d, Service %d, handle 0x%04X]:\n", + printf(" Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n", + client->report_index, 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; - + // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT att_status = gatt_client_read_value_of_characteristic_using_value_handle( &handle_report_event, client->con_handle, @@ -813,7 +835,7 @@ static void hids_run_for_client(hids_client_t * client){ case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION: client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT; - // end of write marked in GATT_EVENT_QUERY_COMPLETE + // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT att_status = gatt_client_read_value_of_characteristic_using_value_handle( &handle_gatt_client_event, client->con_handle, @@ -821,7 +843,15 @@ static void hids_run_for_client(hids_client_t * client){ break; #endif + case HIDS_CLIENT_W2_SEND_GET_HID_INFORMATION: + client->state = HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT; + // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT + att_status = gatt_client_read_value_of_characteristic_using_value_handle( + &handle_gatt_client_event, + client->con_handle, + client->services[client->service_index].hid_information_value_handle); + break; default: break; } @@ -844,8 +874,8 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint uint8_t i; uint8_t report_index; - const uint8_t * descriptor_value; - uint16_t descriptor_value_len; + const uint8_t * value; + uint16_t value_len; switch(hci_event_packet_get_type(packet)){ case GATT_EVENT_SERVICE_QUERY_RESULT: @@ -900,7 +930,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint break; case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT: - report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false); + report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true); break; case ORG_BLUETOOTH_CHARACTERISTIC_REPORT: @@ -913,9 +943,13 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint break; case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION: + client->services[client->service_index].hid_information_value_handle = characteristic.value_handle; + client->services[client->service_index].hid_information_end_handle = characteristic.end_handle; break; case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT: + client->services[client->service_index].control_point_value_handle = characteristic.value_handle; + client->services[client->service_index].control_point_end_handle = characteristic.end_handle; break; default: @@ -945,15 +979,15 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet)); btstack_assert(client != NULL); - descriptor_value = gatt_event_long_characteristic_value_query_result_get_value(packet); - descriptor_value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet); + value = gatt_event_long_characteristic_value_query_result_get_value(packet); + value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet); #ifdef ENABLE_TESTING_SUPPORT // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index); - printf_hexdump(descriptor_value, descriptor_value_len); + printf_hexdump(value, value_len); #endif - for (i = 0; i < descriptor_value_len; i++){ - bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor_value[i]); + for (i = 0; i < value_len; i++){ + bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]); if (!stored){ client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; break; @@ -1008,15 +1042,29 @@ 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)); - break; + value = gatt_event_characteristic_value_query_result_get_value(packet); + value_len = gatt_event_characteristic_value_query_result_get_value_length(packet); + + switch (client->state){ +#ifdef ENABLE_TESTING_SUPPORT + case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT: + printf(" Received CCC value: "); + printf_hexdump(value, value_len); + break; #endif + case HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT: + hids_client_emit_hid_information_event(client, value, value_len); + break; + + default: + break; + } + + break; case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT: client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet)); @@ -1236,11 +1284,17 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint client->state = HIDS_CLIENT_STATE_CONNECTED; hids_emit_connection_established(client, ERROR_CODE_SUCCESS); break; + #ifdef ENABLE_TESTING_SUPPORT case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT: client->state = HIDS_CLIENT_W2_SEND_GET_REPORT; break; #endif + + case HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT: + client->state = HIDS_CLIENT_STATE_CONNECTED; + break; + default: break; } @@ -1354,6 +1408,26 @@ uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){ } +uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){ + 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; + } + + if (service_index >= client->num_instances){ + return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + client->service_index = service_index; + client->state = HIDS_CLIENT_W2_SEND_GET_HID_INFORMATION; + 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; diff --git a/src/ble/gatt-service/hids_client.h b/src/ble/gatt-service/hids_client.h index f26009a4d..f2a42aa37 100644 --- a/src/ble/gatt-service/hids_client.h +++ b/src/ble/gatt-service/hids_client.h @@ -98,9 +98,13 @@ typedef enum { HIDS_CLIENT_STATE_CONNECTED, HIDS_CLIENT_W2_SEND_REPORT, + HIDS_CLIENT_W2_SEND_GET_REPORT, HIDS_CLIENT_W4_GET_REPORT_RESULT, + HIDS_CLIENT_W2_SEND_GET_HID_INFORMATION, + HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT, + #ifdef ENABLE_TESTING_SUPPORT HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION, HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT, @@ -119,7 +123,10 @@ typedef struct { // UUID of external Report characteristic, stored in Report Map descriptor EXTERNAL_REPORT_REFERENCE uint16_t external_report_reference_uuid; + +#ifdef ENABLE_TESTING_SUPPORT uint16_t ccc_handle; +#endif // service mapping uint8_t service_index; @@ -136,6 +143,12 @@ typedef struct { uint16_t report_map_value_handle; uint16_t report_map_end_handle; + uint16_t hid_information_value_handle; + uint16_t hid_information_end_handle; + + uint16_t control_point_value_handle; + uint16_t control_point_end_handle; + // descriptor storage uint16_t hid_descriptor_offset; uint16_t hid_descriptor_len; @@ -209,6 +222,9 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id); +uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index); + + /** * @brief Disconnect from Battery Service. * @param hids_cid diff --git a/src/btstack_defines.h b/src/btstack_defines.h index 1b8d44505..dfd188f02 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -3232,6 +3232,18 @@ typedef uint8_t sm_key_t[16]; */ #define GATTSERVICE_SUBEVENT_HID_REPORT 0x14 +/** + * @format 1212111 + * @param subevent_code + * @param hids_cid + * @param service_index + * @param base_usb_hid_version Version number of base USB HID Specification implemented by HID Device + * @param country_code Country HID Device hardware is localized for (not localized: 0x00) + * @param remote_wake Indicates whether HID Device is capable of sending a wake-signal to a HID Host + * @param normally_connectable Indicates whether HID Device will be advertising when bonded but not connected. +*/ +#define GATTSERVICE_SUBEVENT_HID_INFORMATION 0x15 + // MAP Meta Event Group diff --git a/src/btstack_event.h b/src/btstack_event.h index 768cfd622..a4347fb80 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -9597,6 +9597,61 @@ static inline const uint8_t * gattservice_subevent_hid_report_get_report(const u return &event[9]; } +/** + * @brief Get field hids_cid from event GATTSERVICE_SUBEVENT_HID_INFORMATION + * @param event packet + * @return hids_cid + * @note: btstack_type 2 + */ +static inline uint16_t gattservice_subevent_hid_information_get_hids_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field service_index from event GATTSERVICE_SUBEVENT_HID_INFORMATION + * @param event packet + * @return service_index + * @note: btstack_type 1 + */ +static inline uint8_t gattservice_subevent_hid_information_get_service_index(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field base_usb_hid_version from event GATTSERVICE_SUBEVENT_HID_INFORMATION + * @param event packet + * @return base_usb_hid_version + * @note: btstack_type 2 + */ +static inline uint16_t gattservice_subevent_hid_information_get_base_usb_hid_version(const uint8_t * event){ + return little_endian_read_16(event, 6); +} +/** + * @brief Get field country_code from event GATTSERVICE_SUBEVENT_HID_INFORMATION + * @param event packet + * @return country_code + * @note: btstack_type 1 + */ +static inline uint8_t gattservice_subevent_hid_information_get_country_code(const uint8_t * event){ + return event[8]; +} +/** + * @brief Get field remote_wake from event GATTSERVICE_SUBEVENT_HID_INFORMATION + * @param event packet + * @return remote_wake + * @note: btstack_type 1 + */ +static inline uint8_t gattservice_subevent_hid_information_get_remote_wake(const uint8_t * event){ + return event[9]; +} +/** + * @brief Get field normally_connectable from event GATTSERVICE_SUBEVENT_HID_INFORMATION + * @param event packet + * @return normally_connectable + * @note: btstack_type 1 + */ +static inline uint8_t gattservice_subevent_hid_information_get_normally_connectable(const uint8_t * event){ + return event[10]; +} + /** * @brief Get field map_cid from event MAP_SUBEVENT_CONNECTION_OPENED * @param event packet diff --git a/test/pts/hog_host_test.c b/test/pts/hog_host_test.c index 44917da8a..79be28dd1 100644 --- a/test/pts/hog_host_test.c +++ b/test/pts/hog_host_test.c @@ -74,6 +74,8 @@ static hci_con_handle_t connection_handle; static uint16_t hids_cid; static uint16_t battery_service_cid; +static uint8_t query_service_index; + static bool connect_hids_client = false; static bool connect_battery_client = false; static bool connect_device_information_client = false; @@ -403,6 +405,14 @@ static void hid_service_gatt_client_event_handler(uint8_t packet_type, uint16_t gattservice_subevent_hid_report_get_report_len(packet)); break; + case GATTSERVICE_SUBEVENT_HID_INFORMATION: + printf("Hid Information: service index %d, USB HID 0x%02X, country code %d, remote wake %d, normally connectable %d\n", + gattservice_subevent_hid_information_get_service_index(packet), + gattservice_subevent_hid_information_get_base_usb_hid_version(packet), + gattservice_subevent_hid_information_get_country_code(packet), + gattservice_subevent_hid_information_get_remote_wake(packet), + gattservice_subevent_hid_information_get_normally_connectable(packet)); + break; default: break; } @@ -555,17 +565,52 @@ static void stdin_process(char character){ hog_host_connect_device_information_client(); break; - + case 'i': + query_service_index = 0; + printf("Get HID information for service index %d\n", query_service_index); + hids_client_get_hid_information(hids_cid, query_service_index); + break; + + case 'j': + query_service_index = 1; + printf("Get HID information for service index %d\n", query_service_index); + hids_client_get_hid_information(hids_cid, query_service_index); + break; + + case 'k': + printf("Read Battery Level for service index 0\n"); + battery_service_client_read_battery_level(battery_service_cid, 0); + 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 2\n"); + hids_client_send_get_report(hids_cid, 2); + break; + + case '3': + printf("Get report with ID 3\n"); + hids_client_send_get_report(hids_cid, 3); + break; + + case '4': printf("Get report with ID 4\n"); hids_client_send_get_report(hids_cid, 4); break; + case '5': + printf("Get report with ID 5\n"); + hids_client_send_get_report(hids_cid, 5); + break; + + case '6': + printf("Get report with ID 6\n"); + hids_client_send_get_report(hids_cid, 6); + break; + case 'r':{ uint8_t report[] = {0, 0, 0, 0, 0, 0, 0, 0}; printf("Send output report with id 0x01\n");