From 05439aa6caba9fab55a4dbeb8854ae2692315c16 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Tue, 9 Feb 2021 10:23:15 +0100 Subject: [PATCH] hid_host: rewrite establishing input connection --- example/Makefile.inc | 2 +- example/hid_host_demo.c | 373 +++++++++++++++++---------------------- src/classic/hid_host.c | 204 +++++++++++---------- src/classic/hid_host.h | 15 +- test/pts/hid_host_test.c | 4 +- 5 files changed, 277 insertions(+), 321 deletions(-) diff --git a/example/Makefile.inc b/example/Makefile.inc index 63373f987..a47de356e 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -399,7 +399,7 @@ hfp_ag_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_DECODE hfp_hf_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${CVSD_PLC_OBJ} wav_util.o sco_demo_util.o btstack_ring_buffer.o hfp.o hfp_hf.o hfp_hf_demo.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -hid_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_hid_parser.o hid_host_demo.o +hid_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_hid_parser.o hid_host.o hid_host_demo.o ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ hid_keyboard_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_ring_buffer.o hid_device.o btstack_hid_parser.o hid_keyboard_demo.o diff --git a/example/hid_host_demo.c b/example/hid_host_demo.c index c300b22d1..93af25893 100644 --- a/example/hid_host_demo.c +++ b/example/hid_host_demo.c @@ -55,23 +55,10 @@ #define MAX_ATTRIBUTE_VALUE_SIZE 300 -// SDP -static uint8_t hid_descriptor[MAX_ATTRIBUTE_VALUE_SIZE]; -static uint16_t hid_descriptor_len; - -static uint16_t hid_control_psm; -static uint16_t hid_interrupt_psm; - -static uint8_t attribute_value[MAX_ATTRIBUTE_VALUE_SIZE]; -static const unsigned int attribute_value_buffer_size = MAX_ATTRIBUTE_VALUE_SIZE; - -// L2CAP -static uint16_t l2cap_hid_control_cid; -static uint16_t l2cap_hid_interrupt_cid; - -// MBP 2016 -static const char * remote_addr_string = "F4-0F-24-3B-1B-E1"; +// MBP 2016 static const char * remote_addr_string = "F4-0F-24-3B-1B-E1"; // iMpulse static const char * remote_addr_string = "64:6E:6C:C1:AA:B5"; +// Logitec +static const char * remote_addr_string = "00:1F:20:86:DF:52"; static bd_addr_t remote_addr; @@ -126,7 +113,17 @@ static const uint8_t keytable_us_shift[] = { '6', '7', '8', '9', '0', '.', 0xb1, /* 97-100 */ }; +// SDP +static uint8_t hid_descriptor[MAX_ATTRIBUTE_VALUE_SIZE]; +// App +static enum { + APP_IDLE, + APP_CONNECTED +} app_state = APP_IDLE; + +static uint16_t hid_host_cid = 0; +static bool hid_host_descriptor_available = false; /* @section Main application configuration * * @text In the application configuration, L2CAP is initialized @@ -134,16 +131,15 @@ static const uint8_t keytable_us_shift[] = { /* LISTING_START(PanuSetup): Panu setup */ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -static void handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void hid_host_setup(void){ // Initialize L2CAP l2cap_init(); - // register L2CAP Services for reconnections - l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level()); - l2cap_register_service(packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level()); + // Initialize HID Host + hid_host_init(hid_descriptor, sizeof(hid_descriptor)); + hid_host_register_packet_handler(packet_handler); // Allow sniff mode requests by HID device and support role switch gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_SNIFF_MODE | LM_LINK_POLICY_ENABLE_ROLE_SWITCH); @@ -160,132 +156,6 @@ static void hid_host_setup(void){ } /* LISTING_END */ -/* @section SDP parser callback - * - * @text The SDP parsers retrieves the BNEP PAN UUID as explained in - * Section [on SDP BNEP Query example](#sec:sdpbnepqueryExample}. - */ - -static void handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - - UNUSED(packet_type); - UNUSED(channel); - UNUSED(size); - - des_iterator_t attribute_list_it; - des_iterator_t additional_des_it; - des_iterator_t prot_it; - uint8_t *des_element; - uint8_t *element; - uint32_t uuid; - uint8_t status; - - switch (hci_event_packet_get_type(packet)){ - case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: - if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { - attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); - if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { - switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { - case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: - for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { - if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; - des_element = des_iterator_get_element(&attribute_list_it); - des_iterator_init(&prot_it, des_element); - element = des_iterator_get_element(&prot_it); - if (!element) continue; - if (de_get_element_type(element) != DE_UUID) continue; - uuid = de_get_uuid32(element); - des_iterator_next(&prot_it); - switch (uuid){ - case BLUETOOTH_PROTOCOL_L2CAP: - if (!des_iterator_has_more(&prot_it)) continue; - de_element_get_uint16(des_iterator_get_element(&prot_it), &hid_control_psm); - printf("HID Control PSM: 0x%04x\n", (int) hid_control_psm); - break; - default: - break; - } - } - break; - case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: - for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { - if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; - des_element = des_iterator_get_element(&attribute_list_it); - for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { - if (des_iterator_get_type(&additional_des_it) != DE_DES) continue; - des_element = des_iterator_get_element(&additional_des_it); - des_iterator_init(&prot_it, des_element); - element = des_iterator_get_element(&prot_it); - if (!element) continue; - if (de_get_element_type(element) != DE_UUID) continue; - uuid = de_get_uuid32(element); - des_iterator_next(&prot_it); - switch (uuid){ - case BLUETOOTH_PROTOCOL_L2CAP: - if (!des_iterator_has_more(&prot_it)) continue; - de_element_get_uint16(des_iterator_get_element(&prot_it), &hid_interrupt_psm); - printf("HID Interrupt PSM: 0x%04x\n", (int) hid_interrupt_psm); - break; - default: - break; - } - } - } - break; - case BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST: - for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { - if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; - des_element = des_iterator_get_element(&attribute_list_it); - for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { - if (des_iterator_get_type(&additional_des_it) != DE_STRING) continue; - element = des_iterator_get_element(&additional_des_it); - const uint8_t * descriptor = de_get_string(element); - hid_descriptor_len = de_get_data_size(element); - memcpy(hid_descriptor, descriptor, hid_descriptor_len); - printf("HID Descriptor:\n"); - printf_hexdump(hid_descriptor, hid_descriptor_len); - } - } - break; - default: - break; - } - } - } else { - fprintf(stderr, "SDP attribute value buffer size exceeded: available %d, required %d\n", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); - } - break; - - case SDP_EVENT_QUERY_COMPLETE: - if (sdp_event_query_complete_get_status(packet) != ERROR_CODE_SUCCESS){ - printf("SDP Query failed\n"); - break; - } - if ((l2cap_hid_interrupt_cid != 0) && (l2cap_hid_control_cid != 0)){ - printf("HID device re-connected\n"); - break; - } - if (!hid_control_psm) { - hid_control_psm = BLUETOOTH_PSM_HID_CONTROL; - printf("HID Control PSM missing, using default 0x%04x\n", hid_control_psm); - } - if (!hid_interrupt_psm) { - hid_interrupt_psm = BLUETOOTH_PSM_HID_INTERRUPT; - printf("HID Interrupt PSM missing, using default 0x%04x\n", hid_interrupt_psm); - break; - } - printf("Setup HID\n"); - status = l2cap_create_channel(packet_handler, remote_addr, hid_control_psm, 48, &l2cap_hid_control_cid); - if (status){ - printf("Connecting to HID Control failed: 0x%02x\n", status); - } - break; - - default: - break; - } -} - /* * @section HID Report Handler * @@ -300,10 +170,16 @@ static void hid_host_handle_interrupt_report(const uint8_t * report, uint16_t re // check if HID Input Report if (report_len < 1) return; if (*report != 0xa1) return; + report++; report_len--; + btstack_hid_parser_t parser; - btstack_hid_parser_init(&parser, hid_descriptor, hid_descriptor_len, HID_REPORT_TYPE_INPUT, report, report_len); + btstack_hid_parser_init(&parser, + hid_descriptor_storage_get_descriptor_data(hid_host_cid), + hid_descriptor_storage_get_descriptor_len(hid_host_cid), + HID_REPORT_TYPE_INPUT, report, report_len); + int shift = 0; uint8_t new_keys[NUM_KEYS]; memset(new_keys, 0, sizeof(new_keys)); @@ -366,26 +242,32 @@ static void hid_host_handle_interrupt_report(const uint8_t * report, uint16_t re static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { /* LISTING_PAUSE */ + UNUSED(channel); + UNUSED(size); + uint8_t event; bd_addr_t event_addr; uint8_t status; - uint16_t l2cap_cid; /* LISTING_RESUME */ switch (packet_type) { case HCI_EVENT_PACKET: event = hci_event_packet_get_type(packet); + switch (event) { +#ifndef HAVE_BTSTACK_STDIN /* @text When BTSTACK_EVENT_STATE with state HCI_STATE_WORKING * is received and the example is started in client mode, the remote SDP HID query is started. */ case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ - printf("Start SDP HID query for remote HID Device %s.\n", bd_addr_to_str(remote_addr)); - sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE); + status = hid_host_connect(remote_addr, HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT, &hid_host_cid); + if (status != ERROR_CODE_SUCCESS){ + printf("HID host connect failed, status 0x%02x\n", status); + } } break; - +#endif /* LISTING_PAUSE */ case HCI_EVENT_PIN_CODE_REQUEST: // inform about pin code request @@ -401,84 +283,149 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack break; /* LISTING_RESUME */ - case L2CAP_EVENT_INCOMING_CONNECTION: - l2cap_cid = l2cap_event_incoming_connection_get_local_cid(packet); - switch (l2cap_event_incoming_connection_get_psm(packet)){ - case PSM_HID_CONTROL: - case PSM_HID_INTERRUPT: - l2cap_accept_connection(l2cap_cid); + case HCI_EVENT_HID_META: + switch (hci_event_hid_meta_get_subevent_code(packet)){ + case HID_SUBEVENT_INCOMING_CONNECTION: + hid_host_accept_connection(hid_subevent_incoming_connection_get_hid_cid(packet), HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT); break; - default: - l2cap_decline_connection(l2cap_cid); - break; - } - break; - case L2CAP_EVENT_CHANNEL_OPENED: - status = packet[2]; - if (status){ - printf("L2CAP Connection failed: 0x%02x\n", status); - break; - } - l2cap_cid = l2cap_event_channel_opened_get_local_cid(packet); - switch (l2cap_event_channel_opened_get_psm(packet)){ - case PSM_HID_CONTROL: - if (l2cap_event_channel_opened_get_incoming(packet) == 0) { - status = l2cap_create_channel(packet_handler, remote_addr, hid_interrupt_psm, 48, &l2cap_hid_interrupt_cid); - if (status){ - printf("Connecting to HID Interrupt failed: 0x%02x\n", status); - break; - } + + case HID_SUBEVENT_CONNECTION_OPENED: + status = hid_subevent_connection_opened_get_status(packet); + if (status) { + // outgoing connection failed + printf("Connection failed, status 0x%x\n", status); + app_state = APP_IDLE; + hid_host_cid = 0; + return; } - l2cap_hid_control_cid = l2cap_cid; + app_state = APP_CONNECTED; + hid_host_descriptor_available = false; + hid_host_cid = hid_subevent_connection_opened_get_hid_cid(packet); + printf("HID Host connected...\n"); break; - case PSM_HID_INTERRUPT: - l2cap_hid_interrupt_cid = l2cap_cid; + + case HID_SUBEVENT_DESCRIPTOR_AVAILABLE: + status = hid_subevent_descriptor_available_get_status(packet); + if (status == ERROR_CODE_SUCCESS){ + hid_host_descriptor_available = true; + } break; + case HID_SUBEVENT_CONNECTION_CLOSED: + hid_host_cid = 0; + hid_host_descriptor_available = false; + printf("HID Host disconnected..\n"); + break; + + case HID_SUBEVENT_GET_REPORT_RESPONSE: + status = hid_subevent_get_report_response_get_handshake_status(packet); + if (status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){ + printf("Error get report, status 0x%02x\n", status); + break; + } + printf("Received report[%d]: ", hid_subevent_get_report_response_get_report_len(packet)); + printf_hexdump(hid_subevent_get_report_response_get_report(packet), hid_subevent_get_report_response_get_report_len(packet)); + printf("\n"); + break; + + case HID_SUBEVENT_SET_REPORT_RESPONSE: + status = hid_subevent_set_report_response_get_handshake_status(packet); + if (status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){ + printf("Error set report, status 0x%02x\n", status); + break; + } + printf("Report set.\n"); + break; + + case HID_SUBEVENT_GET_PROTOCOL_RESPONSE: + status = hid_subevent_get_protocol_response_get_handshake_status(packet); + if (status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){ + printf("Error get report, status 0x%02x\n", status); + break; + } + switch ((hid_protocol_mode_t)hid_subevent_get_protocol_response_get_protocol_mode(packet)){ + case HID_PROTOCOL_MODE_BOOT: + printf("HID device is in BOOT mode.\n"); + break; + case HID_PROTOCOL_MODE_REPORT: + printf("HID device is in REPORT mode.\n"); + break; + default: + break; + } + break; + + case HID_SUBEVENT_SET_PROTOCOL_RESPONSE: + status = hid_subevent_set_report_response_get_handshake_status(packet); + if (status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){ + printf("Error set protocol, status 0x%02x\n", status); + break; + } + printf("Protocol set.\n"); + break; + + case HID_SUBEVENT_REPORT: + printf("Received input report[%d]: \n", hid_subevent_report_get_report_len(packet)); + if (hid_host_descriptor_available){ + hid_host_handle_interrupt_report(hid_subevent_report_get_report(packet), hid_subevent_report_get_report_len(packet)); + printf("\n"); + } else { + printf("Cannot handle input report, HID Descriptor is not available\n"); + printf_hexdump(hid_subevent_report_get_report(packet), hid_subevent_report_get_report_len(packet)); + } + break; + default: break; } - - if ((l2cap_hid_control_cid != 0) && (l2cap_hid_interrupt_cid != 0)){ - if (hid_descriptor_len == 0){ - printf("Start SDP HID query to get HID Descriptor\n"); - sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE); - } else { - printf("HID Connection established\n"); - } - } break; - case L2CAP_EVENT_CHANNEL_CLOSED: - if ((l2cap_hid_control_cid != 0) && (l2cap_hid_interrupt_cid != 0)){ - printf("HID Connection closed\n"); - hid_descriptor_len = 0; - } - l2cap_cid = l2cap_event_channel_closed_get_local_cid(packet); - if (l2cap_cid == l2cap_hid_control_cid){ - l2cap_hid_control_cid = 0; - } - if (l2cap_cid == l2cap_hid_interrupt_cid){ - l2cap_hid_interrupt_cid = 0; - } default: break; } break; - case L2CAP_DATA_PACKET: - // for now, just dump incoming data - if (channel == l2cap_hid_interrupt_cid){ - hid_host_handle_interrupt_report(packet, size); - } else if (channel == l2cap_hid_control_cid){ - printf("HID Control: "); - printf_hexdump(packet, size); - } else { - break; - } default: break; } } /* LISTING_END */ +#ifdef HAVE_BTSTACK_STDIN +static void show_usage(void){ + bd_addr_t iut_address; + gap_local_bd_addr(iut_address); + printf("\n--- Bluetooth HID Host Test Console %s ---\n", bd_addr_to_str(iut_address)); + printf("c - Connect to %s in report mode, with fallback to BOOT mode.\n", remote_addr_string); + printf("C - Disconnect from %s\n", remote_addr_string); + + printf("\n"); + printf("Ctrl-c - exit\n"); + printf("---\n"); +} + +static void stdin_process(char cmd){ + uint8_t status = ERROR_CODE_SUCCESS; + switch (cmd){ + case 'c': + printf("Connect to %s in report mode, with fallback to BOOT mode.\n", remote_addr_string); + status = hid_host_connect(remote_addr, HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT, &hid_host_cid); + break; + + case 'C': + printf("Disconnect from %s...\n", remote_addr_string); + hid_host_disconnect(hid_host_cid); + break; + case '\n': + case '\r': + break; + default: + show_usage(); + break; + } + if (status != ERROR_CODE_SUCCESS){ + printf("HID host cmd \'%c\' failed, status 0x%02x\n", cmd, status); + } +} +#endif + int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]){ @@ -490,6 +437,10 @@ int btstack_main(int argc, const char * argv[]){ // parse human readable Bluetooth address sscanf_bd_addr(remote_addr_string, remote_addr); +#ifdef HAVE_BTSTACK_STDIN + btstack_stdin_setup(stdin_process); +#endif + // Turn on the device hci_power_control(HCI_POWER_ON); return 0; diff --git a/src/classic/hid_host.c b/src/classic/hid_host.c index e12573e6e..375e2b2bc 100644 --- a/src/classic/hid_host.c +++ b/src/classic/hid_host.c @@ -182,6 +182,18 @@ static void hid_emit_connected_event(hid_host_connection_t * connection, uint8_t hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); } +static void hid_emit_descriptor_available_event(hid_host_connection_t * connection){ + uint8_t event[6]; + uint16_t pos = 0; + event[pos++] = HCI_EVENT_HID_META; + pos++; // skip len + event[pos++] = HID_SUBEVENT_DESCRIPTOR_AVAILABLE; + little_endian_store_16(event,pos,connection->hid_cid); + pos += 2; + event[pos++] = connection->hid_descriptor_status; + event[1] = pos - 2; + hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); +} static void hid_emit_event(hid_host_connection_t * connection, uint8_t subevent_type){ uint8_t event[5]; @@ -326,7 +338,7 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_ uint32_t uuid; uint8_t status = ERROR_CODE_SUCCESS; bool try_fallback_to_boot; - bool connect_in_report_mode; + bool finalize_connection; hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(sdp_query_context_hid_host_control_cid); @@ -335,14 +347,7 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_ return; } - switch (connection->state){ - case HID_HOST_W4_SDP_QUERY_RESULT: - case HID_HOST_W4_PROTOCOL_MODE_VERIFIED: - break; - default: - btstack_assert(false); - return; - } + btstack_assert(connection->state == HID_HOST_W4_SDP_QUERY_RESULT); switch (hci_event_packet_get_type(packet)){ case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: @@ -411,8 +416,15 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_ uint16_t descriptor_len = de_get_data_size(element); uint16_t i; + bool stored = false; + + connection->hid_descriptor_status = ERROR_CODE_SUCCESS; for (i = 0; i < descriptor_len; i++){ - hid_descriptor_storage_store(connection, descriptor[i]); + stored = hid_descriptor_storage_store(connection, descriptor[i]); + if (!stored){ + connection->hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; + break; + } } } } @@ -429,80 +441,71 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_ case SDP_EVENT_QUERY_COMPLETE: status = sdp_event_query_complete_get_status(packet); try_fallback_to_boot = false; - connect_in_report_mode = false; - - switch (status){ - // remote does not have SDP server: - case L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_PSM: - switch (connection->state){ - case HID_HOST_W4_SDP_QUERY_RESULT: - try_fallback_to_boot = true; - break; - default: // HID_HOST_W4_PROTOCOL_MODE_VERIFIED - // verify that protocol mode is BOOT - connection->set_protocol = true; - connection->requested_protocol_mode = HID_PROTOCOL_MODE_BOOT; - l2cap_request_can_send_now_event(connection->control_cid); - break; - } - break; + finalize_connection = false; + switch (status){ // remote has SDP server case ERROR_CODE_SUCCESS: // but no HID record if (!connection->control_psm || !connection->interrupt_psm) { status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; - try_fallback_to_boot = true; + if (connection->requested_protocol_mode == HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT){ + try_fallback_to_boot = true; + } else { + finalize_connection = true; + } break; } - switch (connection->state){ - case HID_HOST_W4_SDP_QUERY_RESULT: - connect_in_report_mode = true; - break; - default: // HID_HOST_W4_PROTOCOL_MODE_VERIFIED - hid_emit_connected_event(connection, status); - connection->state = HID_HOST_CONNECTION_ESTABLISHED; - break; + // report mode possible + break; + + // SDP connection failed or remote does not have SDP server + default: + if (connection->requested_protocol_mode == HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT){ + try_fallback_to_boot = true; + } else { + finalize_connection = true; } break; - - // SDP connection failed - default: - hid_emit_connected_event(connection, status); - hid_host_finalize_connection(connection); - sdp_query_context_hid_host_control_cid = 0; - break; } - - if (connect_in_report_mode) { - connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; - status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->control_psm, 48, &connection->control_cid); - if (status != ERROR_CODE_SUCCESS){ - hid_emit_connected_event(connection, status); - connection->state = HID_HOST_IDLE; - } + + if (finalize_connection){ + sdp_query_context_hid_host_control_cid = 0; + hid_emit_connected_event(connection, status); + hid_host_finalize_connection(connection); break; } if (try_fallback_to_boot){ - switch (connection->requested_protocol_mode){ - case HID_PROTOCOL_MODE_BOOT: - case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT: - connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; - status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 48, &connection->control_cid); - if (status != ERROR_CODE_SUCCESS){ - hid_emit_connected_event(connection, status); - connection->state = HID_HOST_IDLE; - } - break; - default: + if (connection->incoming){ + connection->set_protocol = true; + connection->requested_protocol_mode = HID_PROTOCOL_MODE_BOOT; + l2cap_request_can_send_now_event(connection->control_cid); + } else { + connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; + status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 0xffff, &connection->control_cid); + if (status != ERROR_CODE_SUCCESS){ + sdp_query_context_hid_host_control_cid = 0; hid_emit_connected_event(connection, status); hid_host_finalize_connection(connection); - sdp_query_context_hid_host_control_cid = 0; - break; - } + } + } break; } + + // report mode possible + if (connection->incoming) { + connection->set_protocol = true; + connection->requested_protocol_mode = HID_PROTOCOL_MODE_REPORT; + l2cap_request_can_send_now_event(connection->control_cid); + } else { + connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; + status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->control_psm, 0xffff, &connection->control_cid); + if (status != ERROR_CODE_SUCCESS){ + hid_emit_connected_event(connection, status); + hid_host_finalize_connection(connection); + } + } break; default: @@ -553,46 +556,32 @@ static void hid_host_handle_control_packet(hid_host_connection_t * connection, u case HID_HOST_CONTROL_CONNECTION_ESTABLISHED: // only outgoing case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED: // incoming and outgoing + case HID_HOST_W4_SDP_QUERY_RESULT: if (!connection->w4_set_protocol_response) break; connection->w4_set_protocol_response = false; - // handle incoming connection - if (connection->incoming){ - switch (message_status){ - case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL: - // we are already connected, here it is only confirmed that we are in required protocol - connection->state = HID_HOST_CONNECTION_ESTABLISHED; - hid_emit_connected_event(connection, ERROR_CODE_SUCCESS); - break; - default: - switch(connection->protocol_mode){ - case HID_PROTOCOL_MODE_BOOT: - case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT: - hid_emit_connected_event(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); - return; - default: - hid_emit_event_with_status(connection, HID_SUBEVENT_SET_PROTOCOL_RESPONSE, message_status); - break; - } - break; - } - break; - } - // handle outgoing connection switch (message_status){ case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL: - status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->interrupt_psm, 48, &connection->interrupt_cid); - if (status){ - log_info("HID Interrupt Connection failed: 0x%02x\n", status); - break; + // we are already connected, here it is only confirmed that we are in required protocol + if (connection->incoming){ + connection->incoming = false; + connection->state = HID_HOST_CONNECTION_ESTABLISHED; + + hid_emit_connected_event(connection, ERROR_CODE_SUCCESS); + hid_emit_descriptor_available_event(connection); + } else { + status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->interrupt_psm, 0xffff, &connection->interrupt_cid); + if (status){ + log_info("HID Interrupt Connection failed: 0x%02x\n", status); + break; + } + connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; } - connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; - return; - + break; default: - hid_emit_event_with_status(connection, HID_SUBEVENT_SET_PROTOCOL_RESPONSE, message_status); hid_emit_connected_event(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); + hid_host_finalize_connection(connection); break; } break; @@ -740,9 +729,10 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 } status = l2cap_event_channel_opened_get_status(packet); - if (status){ + if (status != ERROR_CODE_SUCCESS){ log_info("L2CAP connection %s failed: 0x%02xn", bd_addr_to_str(address), status); hid_emit_connected_event(connection, status); + hid_host_finalize_connection(connection); break; } @@ -753,18 +743,22 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 // A Bluetooth HID Host or Bluetooth HID device shall always open both the control and Interrupt channels. (HID_v1.1.1, Chapter 5.2.2) // We expect incomming interrupt connection from remote HID device connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; + log_info("Incoming control connection opened: w4 interrupt"); break; case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED: - - switch (connection->protocol_mode){ + // hid_emit_connected_event(connection, ERROR_CODE_SUCCESS); + + switch (connection->requested_protocol_mode){ case HID_PROTOCOL_MODE_BOOT: connection->set_protocol = true; l2cap_request_can_send_now_event(connection->control_cid); + log_info("Incoming interrupt connection opened: set boot mode"); break; default: // SDP query - connection->state = HID_HOST_W4_PROTOCOL_MODE_VERIFIED; + log_info("Incoming interrupt connection opened: start SDP query"); + connection->state = HID_HOST_W2_SEND_SDP_QUERY; hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query; (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request); break; @@ -781,7 +775,7 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 // handle outgoing connection switch (connection->state){ case HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED: - log_info("Opened control channel, protocol mode %d\n", connection->requested_protocol_mode); + log_info("Opened control connection, requested protocol mode %d\n", connection->requested_protocol_mode); connection->con_handle = l2cap_event_channel_opened_get_handle(packet); connection->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED; @@ -793,7 +787,7 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 l2cap_request_can_send_now_event(connection->control_cid); break; default: - status = l2cap_create_channel(hid_host_packet_handler, address, connection->interrupt_psm, 48, &connection->interrupt_cid); + status = l2cap_create_channel(hid_host_packet_handler, address, connection->interrupt_psm, 0xffff, &connection->interrupt_cid); if (status){ log_info("Connecting to HID Interrupt failed: 0x%02x", status); hid_emit_connected_event(connection, status); @@ -810,6 +804,7 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 log_info("HID host connection established, cids: control 0x%02x, interrupt 0x%02x interrupt, hid 0x%02x", connection->control_cid, connection->interrupt_cid, connection->hid_cid); hid_emit_connected_event(connection, ERROR_CODE_SUCCESS); + hid_emit_descriptor_available_event(connection); break; default: @@ -979,8 +974,9 @@ static void hid_host_handle_start_sdp_client_query(void * context){ hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); switch (connection->state){ - case HID_HOST_W2_SEND_SDP_QUERY: + case HID_HOST_W2_SEND_SDP_QUERY: connection->state = HID_HOST_W4_SDP_QUERY_RESULT; + connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; break; default: continue; @@ -1045,7 +1041,7 @@ uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mod switch (connection->requested_protocol_mode){ case HID_PROTOCOL_MODE_BOOT: connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; - status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 48, &connection->control_cid); + status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 0xffff, &connection->control_cid); break; default: hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query; diff --git a/src/classic/hid_host.h b/src/classic/hid_host.h index e67ba2921..688356c40 100644 --- a/src/classic/hid_host.h +++ b/src/classic/hid_host.h @@ -60,7 +60,6 @@ typedef enum { HID_HOST_W4_SET_BOOT_MODE, HID_HOST_W4_INCOMING_INTERRUPT_CONNECTION, HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED, - HID_HOST_W4_PROTOCOL_MODE_VERIFIED, HID_HOST_CONNECTION_ESTABLISHED, HID_HOST_W2_SEND_GET_REPORT, @@ -101,6 +100,9 @@ typedef struct { uint16_t hid_descriptor_offset; uint16_t hid_descriptor_len; uint16_t hid_descriptor_max_len; + uint8_t hid_descriptor_status; // ERROR_CODE_SUCCESS if descriptor available, + // ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if not, and + // ERROR_CODE_MEMORY_CAPACITY_EXCEEDED if descriptor is larger then the available space uint8_t user_request_can_send_now; @@ -135,9 +137,16 @@ void hid_host_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_sto void hid_host_register_packet_handler(btstack_packet_handler_t callback); /* - * @brief Create HID connection to HID Device and emit HID_SUBEVENT_CONNECTION_OPENED event with status code. + * @brief Create HID connection to HID Device and emit HID_SUBEVENT_CONNECTION_OPENED event with status code, + * followed by HID_SUBEVENT_DESCRIPTOR_AVAILABLE that informs if the HID Descriptor was found. In the case of incoming + * connection, i.e. HID Device initiating the connection, the HID_SUBEVENT_DESCRIPTOR_AVAILABLE is delayed, and the reports + * may already come via HID_SUBEVENT_REPORT event. It is up to the application code if + * these events should be buffered or ignored until the descriptor is available. * @note HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT will try ti set up REPORT mode, but fallback to BOOT mode if necessary. - * @note Reports from HID Device are received via HID_SUBEVENT_REPORT. + * @note HID_SUBEVENT_DESCRIPTOR_AVAILABLE possible status values are: + * - ERROR_CODE_SUCCESS if descriptor available, + * - ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if not, and + * - ERROR_CODE_MEMORY_CAPACITY_EXCEEDED if descriptor is larger then the available space * @param remote_addr * @param protocol_mode see hid_protocol_mode_t in hid.h * @param hid_cid to use for other commands diff --git a/test/pts/hid_host_test.c b/test/pts/hid_host_test.c index 822206aa0..fc104e0aa 100644 --- a/test/pts/hid_host_test.c +++ b/test/pts/hid_host_test.c @@ -110,7 +110,7 @@ static enum { static bool unplugged = false; static uint16_t hid_host_cid = 0; -static hid_protocol_mode_t hid_host_protocol_mode = HID_PROTOCOL_MODE_REPORT; +static hid_protocol_mode_t hid_host_protocol_mode = HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT; // SDP static uint8_t hid_descriptor[MAX_ATTRIBUTE_VALUE_SIZE]; @@ -233,8 +233,8 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack switch (packet_type) { case HCI_EVENT_PACKET: event = hci_event_packet_get_type(packet); + switch (event) { - case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ printf("BTstack up and running. \n");