hid_host: start sdp query and connect

This commit is contained in:
Milanka Ringwald 2021-01-07 10:48:04 +01:00 committed by Matthias Ringwald
parent f399f7fbd7
commit fd7ba7a6d2
3 changed files with 548 additions and 346 deletions

View File

@ -45,59 +45,300 @@
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_hid_parser.h"
#include "btstack_memory.h"
#include "classic/hid.h"
#include "classic/hid_host.h"
#include "classic/sdp_util.h"
#include "classic/sdp_client.h"
#include "l2cap.h"
typedef enum {
HID_HOST_IDLE,
HID_HOST_CONTROL_CONNECTION_ESTABLISHED,
HID_HOST_W4_SET_BOOT_MODE,
HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED,
HID_HOST_CONNECTION_ESTABLISHED,
HID_HOST_W2_SEND_GET_REPORT,
HID_HOST_W4_GET_REPORT_RESPONSE,
HID_HOST_W2_SEND_SET_REPORT,
HID_HOST_W4_SET_REPORT_RESPONSE,
HID_HOST_W2_SEND_GET_PROTOCOL,
HID_HOST_W4_GET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_SET_PROTOCOL,
HID_HOST_W4_SET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_REPORT,
HID_HOST_W4_SEND_REPORT_RESPONSE
} hid_host_state_t;
#define MAX_ATTRIBUTE_VALUE_SIZE 300
typedef struct {
uint16_t cid;
bd_addr_t bd_addr;
hci_con_handle_t con_handle;
uint16_t control_cid;
uint16_t interrupt_cid;
static btstack_packet_handler_t hid_callback;
hid_host_state_t state;
hid_protocol_mode_t protocol_mode;
uint8_t user_request_can_send_now;
// get report
hid_report_type_t report_type;
uint8_t report_id;
// set report
uint8_t * report;
uint16_t report_len;
} hid_host_connection_t;
static const uint8_t * hid_host_descriptor_storage;
static uint8_t * hid_host_descriptor_storage;
static uint16_t hid_host_descriptor_storage_len;
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
static btstack_linked_list_t connections;
static uint16_t hid_host_cid_counter = 0;
// SDP
static uint8_t attribute_value[MAX_ATTRIBUTE_VALUE_SIZE];
static const unsigned int attribute_value_buffer_size = MAX_ATTRIBUTE_VALUE_SIZE;
static uint16_t sdp_query_context_hid_host_control_cid = 0;
static btstack_context_callback_registration_t hid_host_handle_sdp_client_query_request;
static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static uint16_t hid_descriptor_storage_get_available_space(void){
// assumes all descriptors are back to back
uint16_t free_space = hid_host_descriptor_storage_len;
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &connections);
while (btstack_linked_list_iterator_has_next(&it)){
hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
free_space -= connection->hid_descriptor_len;
}
return free_space;
}
static void hid_descriptor_storage_init(hid_host_connection_t * connection){
connection->hid_descriptor_len = 0;
connection->hid_descriptor_max_len = hid_descriptor_storage_get_available_space();
connection->hid_descriptor_offset = hid_host_descriptor_storage_len - connection->hid_descriptor_max_len;
}
static bool hid_descriptor_storage_store(hid_host_connection_t * connection, uint8_t byte){
if (connection->hid_descriptor_len >= connection->hid_descriptor_max_len) return false;
hid_host_descriptor_storage[connection->hid_descriptor_offset + connection->hid_descriptor_len] = byte;
connection->hid_descriptor_len++;
return true;
}
static void hid_descriptor_storage_delete(hid_host_connection_t * connection){
uint16_t next_offset = connection->hid_descriptor_offset + connection->hid_descriptor_len;
memmove(&hid_host_descriptor_storage[connection->hid_descriptor_offset],
&hid_host_descriptor_storage[next_offset],
hid_host_descriptor_storage_len - next_offset);
connection->hid_descriptor_len = 0;
connection->hid_descriptor_offset = 0;
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &connections);
while (btstack_linked_list_iterator_has_next(&it)){
hid_host_connection_t * conn = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
if (conn->hid_descriptor_offset >= next_offset){
conn->hid_descriptor_offset = next_offset;
next_offset += conn->hid_descriptor_len;
}
}
}
// HID Util
static inline void hid_emit_connected_event(hid_host_connection_t * context, uint8_t status){
uint8_t event[15];
int pos = 0;
event[pos++] = HCI_EVENT_HID_META;
pos++; // skip len
event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
little_endian_store_16(event, pos, context->hid_cid);
pos+=2;
event[pos++] = status;
reverse_bd_addr(context->remote_addr, &event[pos]);
pos += 6;
little_endian_store_16(event,pos,context->con_handle);
pos += 2;
event[pos++] = context->incoming;
event[1] = pos - 2;
hid_callback(HCI_EVENT_PACKET, context->hid_cid, &event[0], pos);
}
// HID Host
static uint16_t hid_host_get_next_cid(void){
if (hid_host_cid_counter == 0xffff) {
hid_host_cid_counter = 1;
} else {
hid_host_cid_counter++;
}
return hid_host_cid_counter;
}
static hid_host_connection_t * hid_host_create_connection(bd_addr_t remote_addr){
hid_host_connection_t * connection = btstack_memory_hid_host_connection_get();
if (!connection){
log_error("Not enough memory to create connection");
return NULL;
}
connection->state = HID_HOST_IDLE;
connection->hid_cid = hid_host_get_next_cid();
(void)memcpy(connection->remote_addr, remote_addr, 6);
printf("hid_host_create_connection, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
btstack_linked_list_add(&connections, (btstack_linked_item_t *) connection);
return connection;
}
static hid_host_connection_t * hid_host_get_connection_for_bd_addr(bd_addr_t addr){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &connections);
while (btstack_linked_list_iterator_has_next(&it)){
hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
if (memcmp(addr, connection->remote_addr, 6) != 0) continue;
return connection;
}
return NULL;
}
static hid_host_connection_t * hid_host_connection_for_hid_cid(uint16_t hid_cid){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &connections);
while (btstack_linked_list_iterator_has_next(&it)){
hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
if (connection->hid_cid != hid_cid) continue;
return connection;
}
return NULL;
}
static void hid_host_finalize_connection(hid_host_connection_t * connection){
btstack_linked_list_remove(&connections, (btstack_linked_item_t*) connection);
btstack_memory_hid_host_connection_free(connection);
}
static void hid_host_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 = ERROR_CODE_SUCCESS;
hid_host_connection_t * connection = hid_host_connection_for_hid_cid(sdp_query_context_hid_host_control_cid);
if (!connection) {
log_error("SDP query, connection with 0x%02x cid not found", sdp_query_context_hid_host_control_cid);
return;
}
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 (de_get_element_type(element) != DE_UUID) continue;
uuid = de_get_uuid32(element);
switch (uuid){
case BLUETOOTH_PROTOCOL_L2CAP:
if (!des_iterator_has_more(&prot_it)) continue;
des_iterator_next(&prot_it);
de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->control_psm);
log_info("HID Control PSM: 0x%04x", (int) &connection->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 (de_get_element_type(element) != DE_UUID) continue;
uuid = de_get_uuid32(element);
switch (uuid){
case BLUETOOTH_PROTOCOL_L2CAP:
if (!des_iterator_has_more(&prot_it)) continue;
des_iterator_next(&prot_it);
de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->interrupt_psm);
log_info("HID Interrupt PSM: 0x%04x", (int) &connection->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);
uint16_t descriptor_len = de_get_data_size(element);
uint16_t i;
for (i = 0; i < descriptor_len; i++){
hid_descriptor_storage_store(connection, descriptor[i]);
}
printf("HID Descriptor:\n");
printf_hexdump(descriptor, 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 (!connection->control_psm) {
printf("HID Control PSM missing\n");
status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
break;
}
if (!connection->interrupt_psm) {
printf("HID Interrupt PSM missing\n");
status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
break;
}
printf("Setup HID\n");
status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->control_psm, 48, &connection->control_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
}
break;
default:
// bail out, we must have had an incoming connection in the meantime; just trigger next sdp query on complete
if (hci_event_packet_get_type(packet) == SDP_EVENT_QUERY_COMPLETE){
(void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request);
}
return;
}
if (status != ERROR_CODE_SUCCESS){
hid_emit_connected_event(connection, status);
hid_host_finalize_connection(connection);
sdp_query_context_hid_host_control_cid = 0;
}
}
static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
uint8_t event;
// bd_addr_t address;
// uint8_t status;
// uint16_t l2cap_cid;
// hid_host_t * hid_host;
bd_addr_t address;
uint8_t status;
uint16_t cid;
hid_host_connection_t * connection;
// uint8_t param;
// hid_message_type_t message_type;
// hid_handshake_param_type_t message_status;
@ -106,11 +347,112 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
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");
case L2CAP_EVENT_INCOMING_CONNECTION:
l2cap_event_incoming_connection_get_address(packet, address);
connection = hid_host_get_connection_for_bd_addr(address);
if (connection && connection->unplugged){
log_info("Decline connection for %s, host is unplugged", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
if (connection){
log_error("Connection already exists %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
connection = hid_host_create_connection(address);
if (!connection) {
log_error("Cannot create connection for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED;
connection->con_handle = l2cap_event_incoming_connection_get_handle(packet);
connection->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
connection->incoming = true;
log_info("Accept connection on Control channel %s", bd_addr_to_str(address));
l2cap_accept_connection(channel);
break;
case PSM_HID_INTERRUPT:
if (!connection || (connection->interrupt_cid != 0) || (l2cap_event_incoming_connection_get_handle(packet) != connection->con_handle)){
log_error("Decline connection for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
connection->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
log_info("Accept connection on Interrupt channel %s", bd_addr_to_str(address));
l2cap_accept_connection(channel);
break;
default:
log_info("Decline connection for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
break;
case L2CAP_EVENT_CHANNEL_OPENED:
l2cap_event_channel_opened_get_address(packet, address);
status = l2cap_event_channel_opened_get_status(packet);
if (status){
log_info("L2CAP connection %s failed: 0x%02xn", bd_addr_to_str(address), status);
break;
}
connection = hid_host_get_connection_for_bd_addr(address);
if (!connection){
log_error("Connection does not exist %s", bd_addr_to_str(address));
break;
}
cid = l2cap_event_channel_opened_get_local_cid(packet);
switch (l2cap_event_channel_opened_get_psm(packet)){
case PSM_HID_CONTROL:
if (connection->state != HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED) break;
connection->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
if (connection->boot_mode){
break;
}
if (!connection->incoming){
status = l2cap_create_channel(hid_host_packet_handler, address, connection->interrupt_psm, 48, &connection->interrupt_cid);
if (status){
log_info("Connecting to HID Interrupt failed: 0x%02x", status);
break;
}
connection->con_handle = l2cap_event_channel_opened_get_handle(packet);
connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
}
break;
case PSM_HID_INTERRUPT:
if (connection->state != HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED) break;
if (connection->con_handle != l2cap_event_channel_opened_get_handle(packet)) break;
connection->state = HID_HOST_CONNECTION_ESTABLISHED;
hid_emit_connected_event(connection, ERROR_CODE_SUCCESS);
log_info("Connection on interrupt channel established, interrupt_cid 0x%02x", connection->interrupt_cid);
break;
default:
break;
}
// disconnect?
break;
default:
break;
}
@ -119,22 +461,74 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
}
}
void hid_host_init(const uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
void hid_host_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
hid_host_descriptor_storage = hid_descriptor_storage;
hid_host_descriptor_storage_len = hid_descriptor_storage_len;
// 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());
l2cap_register_service(hid_host_packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level());
l2cap_register_service(hid_host_packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level());
}
void hid_host_register_packet_handler(btstack_packet_handler_t callback){
UNUSED(callback);
hid_callback = callback;
}
uint8_t hid_host_connect(bd_addr_t addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){
UNUSED(hid_cid);
static void hid_host_handle_start_sdp_client_query(void * context){
UNUSED(context);
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &connections);
while (btstack_linked_list_iterator_has_next(&it)){
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:
connection->state = HID_HOST_W4_SDP_QUERY_RESULT;
break;
default:
continue;
}
printf("hid_descriptor_storage_init, start query, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
hid_descriptor_storage_init(connection);
sdp_query_context_hid_host_control_cid = connection->hid_cid;
sdp_client_query_uuid16(&hid_host_handle_sdp_client_query_result, (uint8_t *) connection->remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
return;
}
}
uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){
UNUSED(protocol_mode);
if (hid_cid == NULL) {
return ERROR_CODE_COMMAND_DISALLOWED;
}
hid_host_connection_t * connection = hid_host_get_connection_for_bd_addr(remote_addr);
if (connection){
return ERROR_CODE_COMMAND_DISALLOWED;
}
connection = hid_host_create_connection(remote_addr);
if (!connection) return BTSTACK_MEMORY_ALLOC_FAILED;
*hid_cid = connection->hid_cid;
connection->state = HID_HOST_W2_SEND_SDP_QUERY;
connection->incoming = false;
connection->control_cid = 0;
connection->control_psm = 0;
connection->interrupt_cid = 0;
connection->interrupt_psm = 0;
printf("hid_host_connect, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query;
// ignore ERROR_CODE_COMMAND_DISALLOWED because in that case, we already have requested an SDP callback
(void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request);
return ERROR_CODE_SUCCESS;
}

View File

@ -48,6 +48,62 @@
extern "C" {
#endif
typedef enum {
HID_HOST_IDLE,
HID_HOST_W2_SEND_SDP_QUERY,
HID_HOST_W4_SDP_QUERY_RESULT,
HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED,
HID_HOST_CONTROL_CONNECTION_ESTABLISHED,
HID_HOST_W4_SET_BOOT_MODE,
HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED,
HID_HOST_CONNECTION_ESTABLISHED,
HID_HOST_W2_SEND_GET_REPORT,
HID_HOST_W4_GET_REPORT_RESPONSE,
HID_HOST_W2_SEND_SET_REPORT,
HID_HOST_W4_SET_REPORT_RESPONSE,
HID_HOST_W2_SEND_GET_PROTOCOL,
HID_HOST_W4_GET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_SET_PROTOCOL,
HID_HOST_W4_SET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_REPORT,
HID_HOST_W4_SEND_REPORT_RESPONSE
} hid_host_state_t;
typedef struct {
uint16_t hid_cid;
hci_con_handle_t con_handle;
bd_addr_t remote_addr;
bool incoming;
bool boot_mode;
uint16_t control_cid;
uint16_t control_psm;
uint16_t interrupt_cid;
uint16_t interrupt_psm;
hid_host_state_t state;
hid_protocol_mode_t protocol_mode;
bool unplugged;
uint16_t hid_descriptor_offset;
uint16_t hid_descriptor_len;
uint16_t hid_descriptor_max_len;
uint8_t user_request_can_send_now;
// get report
hid_report_type_t report_type;
uint8_t report_id;
// set report
uint8_t * report;
uint16_t report_len;
} hid_host_connection_t;
/* API_START */
/**
* @brief Set up HID Host
@ -55,7 +111,7 @@ extern "C" {
* @param hid_descriptor_storage
* @param hid_descriptor_storage_len
*/
void hid_host_init(const uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len);
void hid_host_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len);
/**
* @brief Register callback for the HID Device Host.
@ -65,11 +121,11 @@ void hid_host_register_packet_handler(btstack_packet_handler_t callback);
/*
* @brief Create HID connection to HID Host
* @param addr
* @param remote_addr
* @param hid_cid to use for other commands
* @result status
*/
uint8_t hid_host_connect(bd_addr_t addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid);
uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid);
/*
* @brief Disconnect from HID Host

View File

@ -55,17 +55,19 @@
#define MAX_ATTRIBUTE_VALUE_SIZE 300
static enum {
APP_IDLE,
APP_CONNECTED
} app_state = APP_IDLE;
static uint16_t hid_host_cid = 0;
// 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;
// PTS
static const char * remote_addr_string = "00:1B:DC:08:E2:5C";
@ -73,25 +75,6 @@ static bd_addr_t remote_addr;
static btstack_packet_callback_registration_t hci_event_callback_registration;
// Needed for queries
typedef enum {
HID_HOST_IDLE,
HID_HOST_CONTROL_CONNECTION_ESTABLISHED,
HID_HOST_W4_SET_BOOT_MODE,
HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED,
HID_HOST_CONNECTION_ESTABLISHED,
HID_HOST_W2_SEND_GET_REPORT,
HID_HOST_W4_GET_REPORT_RESPONSE,
HID_HOST_W2_SEND_SET_REPORT,
HID_HOST_W4_SET_REPORT_RESPONSE,
HID_HOST_W2_SEND_GET_PROTOCOL,
HID_HOST_W4_GET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_SET_PROTOCOL,
HID_HOST_W4_SET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_REPORT,
HID_HOST_W4_SEND_REPORT_RESPONSE
} hid_host_state_t;
// Simplified US Keyboard with Shift modifier
@ -168,19 +151,11 @@ typedef struct hid_host {
uint8_t user_request_can_send_now;
} hid_host_t;
static hid_host_t _hid_host;
static uint16_t hid_host_cid = 0;
static bool boot_mode = false;
static bool send_through_interrupt_channel = false;
static uint16_t hid_host_get_next_cid(void){
hid_host_cid++;
if (!hid_host_cid){
hid_host_cid = 1;
}
return hid_host_cid;
}
static hid_host_t * hid_host_get_instance_for_hid_cid(uint16_t hid_cid){
if (_hid_host.cid == hid_cid){
return &_hid_host;
@ -300,27 +275,6 @@ static uint8_t hid_host_send_output_report(uint16_t hid_cid, uint8_t report_id,
return hid_host_send_report(hid_cid, HID_REPORT_TYPE_OUTPUT, report_id, report, report_len);
}
static uint8_t hid_host_send_feature_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){
return hid_host_send_report(hid_cid, HID_REPORT_TYPE_FEATURE, report_id, report, report_len);
}
static uint8_t hid_host_send_input_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){
return hid_host_send_report(hid_cid, HID_REPORT_TYPE_INPUT, report_id, report, report_len);
}
// static void hid_host_connect(bd_addr_t addr, uint16_t * hid_cid){
// }
static hid_host_t * hid_host_provide_instance_for_bd_addr(bd_addr_t bd_addr){
if (!_hid_host.cid){
(void)memcpy(_hid_host.bd_addr, bd_addr, 6);
_hid_host.cid = hid_host_get_next_cid();
// _hid_host.protocol_mode = HID_PROTOCOL_MODE_REPORT;
_hid_host.con_handle = HCI_CON_HANDLE_INVALID;
}
return &_hid_host;
}
static hid_host_t * hid_host_get_instance_for_l2cap_cid(uint16_t cid){
if ((_hid_host.control_cid == cid) || (_hid_host.interrupt_cid == cid)){
@ -398,7 +352,6 @@ static void _hid_host_disconnect(uint16_t hid_cid){
/* 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){
@ -421,121 +374,6 @@ static void hid_host_setup(void){
// Disable stdout buffering
setbuf(stdout, NULL);
}
/* 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 (de_get_element_type(element) != DE_UUID) continue;
uuid = de_get_uuid32(element);
switch (uuid){
case BLUETOOTH_PROTOCOL_L2CAP:
if (!des_iterator_has_more(&prot_it)) continue;
des_iterator_next(&prot_it);
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 (de_get_element_type(element) != DE_UUID) continue;
uuid = de_get_uuid32(element);
switch (uuid){
case BLUETOOTH_PROTOCOL_L2CAP:
if (!des_iterator_has_more(&prot_it)) continue;
des_iterator_next(&prot_it);
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 (!hid_control_psm) {
printf("HID Control PSM missing\n");
break;
}
if (!hid_interrupt_psm) {
printf("HID Interrupt PSM missing\n");
break;
}
printf("Setup HID\n");
(void)memcpy(_hid_host.bd_addr, remote_addr, 6);
status = l2cap_create_channel(packet_handler, remote_addr, hid_control_psm, 48, &_hid_host.control_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
}
break;
}
}
/*
@ -655,109 +493,24 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
printf("SSP User Confirmation Auto accept\n");
break;
/* LISTING_RESUME */
case L2CAP_EVENT_INCOMING_CONNECTION:
printf("L2CAP_EVENT_INCOMING_CONNECTION \n");
l2cap_event_incoming_connection_get_address(packet, address);
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
case PSM_HID_INTERRUPT:
hid_host = hid_host_provide_instance_for_bd_addr(address);
if (!hid_host) {
log_error("L2CAP_EVENT_INCOMING_CONNECTION, cannot create instance for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
if (hid_host->unplugged) {
log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s, host is unplugged", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
if ((hid_host->con_handle == HCI_CON_HANDLE_INVALID) || (l2cap_event_incoming_connection_get_handle(packet) == hid_host->con_handle)){
hid_host->con_handle = l2cap_event_incoming_connection_get_handle(packet);
// hid_host->incoming = 1;
l2cap_event_incoming_connection_get_address(packet, hid_host->bd_addr);
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
hid_host->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
break;
case PSM_HID_INTERRUPT:
hid_host->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
break;
default:
break;
}
printf("L2CAP_EVENT_INCOMING_CONNECTION l2cap_accept_connection\n");
l2cap_accept_connection(channel);
} else {
l2cap_decline_connection(channel);
log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
}
break;
default:
log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
break;
case L2CAP_EVENT_CHANNEL_OPENED:
status = l2cap_event_channel_opened_get_status(packet);
if (status){
printf("L2CAP Connection failed: 0x%02x\n", status);
break;
}
l2cap_cid = l2cap_event_channel_opened_get_local_cid(packet);
if (!l2cap_cid) break;
hid_host = hid_host_get_instance_for_l2cap_cid(l2cap_cid);
if (!hid_host) {
log_error("L2CAP_EVENT_CHANNEL_OPENED, cannot find instance for %s", bd_addr_to_str(address));
break;
}
switch (l2cap_event_channel_opened_get_psm(packet)){
case PSM_HID_CONTROL:
printf("Control channel opened 0x%2x == 0x%2x\n", l2cap_cid, hid_host->control_cid);
if (l2cap_cid == hid_host->control_cid){
hid_host->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
if (boot_mode){
// status = hid_host_send_set_protocol_mode(hid_host->cid, HID_PROTOCOL_MODE_BOOT);
// if (status){
// printf("Boot mode failed: 0x%02x\n", status);
// hid_host->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
// break;
// }
printf("Boot mode L2CAP_EVENT_CHANNEL_OPENED\n");
break;
}
status = l2cap_create_channel(packet_handler, remote_addr, hid_interrupt_psm, 48, &hid_host->interrupt_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
hid_host->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
break;
}
hid_host->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
}
break;
case PSM_HID_INTERRUPT:
printf("Interupt channel opened 0x%2x == 0x%2x\n", l2cap_cid, hid_host->interrupt_cid);
if (l2cap_cid == hid_host->interrupt_cid){
// if (hid_host->state == HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED){
hid_host->state = HID_HOST_CONNECTION_ESTABLISHED;
printf("HID Connection established\n");
// }
case HCI_EVENT_HID_META:
switch (hci_event_hid_meta_get_subevent_code(packet)){
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;
}
app_state = APP_CONNECTED;
hid_host_cid = hid_subevent_connection_opened_get_hid_cid(packet);
printf("HID Host Connected..\n");
break;
default:
break;
}
// disconnect?
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
@ -945,22 +698,36 @@ static void show_usage(void){
static void stdin_process(char cmd){
uint8_t status = ERROR_CODE_SUCCESS;
switch (cmd){
case 'a':
printf("Start SDP HID query for remote HID Device.\n");
sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
case 'R':
printf("Set Protocol mode\n");
boot_mode = false;
status = hid_host_send_set_protocol_mode(_hid_host.cid, HID_PROTOCOL_MODE_REPORT);
break;
case 'b':
printf("Set Boot mode\n");
boot_mode = false;
status = hid_host_send_set_protocol_mode(_hid_host.cid, HID_PROTOCOL_MODE_BOOT);
break;
case 'B':
printf("Set Boot mode\n");
boot_mode = true;
break;
case 'c':
if (_hid_host.unplugged){
printf("Cannot reconnect, host is unplugged.\n");
printf("Cannot connect, host is unplugged.\n");
break;
}
printf("Reconnect, control PSM.\n");
printf("Start SDP scan and connect to %s.\n", bd_addr_to_str(remote_addr));
status = l2cap_create_channel(packet_handler, remote_addr, BLUETOOTH_PSM_HID_CONTROL, 48, &_hid_host.control_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
if (boot_mode){
status = hid_host_connect(remote_addr, HID_PROTOCOL_MODE_BOOT, &hid_host_cid);
} else {
status = hid_host_connect(remote_addr, HID_PROTOCOL_MODE_REPORT, &hid_host_cid);
}
break;
case 'i':
if (_hid_host.unplugged){
printf("Cannot reconnect, host is unplugged.\n");
@ -1038,21 +805,6 @@ static void stdin_process(char cmd){
status = hid_host_send_get_protocol(_hid_host.cid);
break;
case 'R':
printf("Set Protocol mode\n");
boot_mode = false;
status = hid_host_send_set_protocol_mode(_hid_host.cid, HID_PROTOCOL_MODE_REPORT);
break;
case 'b':
printf("Set Boot mode\n");
boot_mode = false;
status = hid_host_send_set_protocol_mode(_hid_host.cid, HID_PROTOCOL_MODE_BOOT);
break;
case 'B':
printf("Set Boot mode\n");
boot_mode = true;
break;
case 'X':
printf("Set send through interrupt channel\n");
send_through_interrupt_channel = true;