hid_host: rewrite establishing input connection

This commit is contained in:
Milanka Ringwald 2021-02-09 10:23:15 +01:00 committed by Matthias Ringwald
parent 31a4e45b22
commit 05439aa6ca
5 changed files with 277 additions and 321 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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");