mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-21 22:20:57 +00:00
hid_host: rewrite establishing input connection
This commit is contained in:
parent
31a4e45b22
commit
05439aa6ca
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user