mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-02-24 12:40:47 +00:00
hid_host: implement hid control messages (suspend, exit suspend, unplug)
This commit is contained in:
parent
b6685dec89
commit
59a2ea7477
@ -54,20 +54,74 @@
|
||||
|
||||
#define MAX_ATTRIBUTE_VALUE_SIZE 300
|
||||
|
||||
static btstack_packet_handler_t hid_callback;
|
||||
#define CONTROL_MESSAGE_BITMASK_SUSPEND 1
|
||||
#define CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND 2
|
||||
#define CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG 4
|
||||
|
||||
// Simplified US Keyboard with Shift modifier
|
||||
|
||||
#define CHAR_ILLEGAL 0xff
|
||||
#define CHAR_RETURN '\n'
|
||||
#define CHAR_ESCAPE 27
|
||||
#define CHAR_TAB '\t'
|
||||
#define CHAR_BACKSPACE 0x7f
|
||||
#define NUM_KEYS 6
|
||||
|
||||
// English (US)
|
||||
//
|
||||
static const uint8_t keytable_us_none [] = {
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 0-3
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', // 4-13
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', // 14-23
|
||||
'u', 'v', 'w', 'x', 'y', 'z', // 24-29
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', // 30-39
|
||||
CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ', // 40-44
|
||||
'-', '=', '[', ']', '\\', CHAR_ILLEGAL, ';', '\'', 0x60, ',', // 45-54
|
||||
'.', '/', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 55-60
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 61-64
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 65-68
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 69-72
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 73-76
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 77-80
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 81-84
|
||||
'*', '-', '+', '\n', '1', '2', '3', '4', '5', // 85-97
|
||||
'6', '7', '8', '9', '0', '.', 0xa7, // 97-100
|
||||
};
|
||||
|
||||
static const uint8_t keytable_us_shift[] = {
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 0-3
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', // 4-13
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', // 14-23
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z', // 24-29
|
||||
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', // 30-39
|
||||
CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ', // 40-44
|
||||
'_', '+', '{', '}', '|', CHAR_ILLEGAL, ':', '"', 0x7E, '<', // 45-54
|
||||
'>', '?', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 55-60
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 61-64
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 65-68
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 69-72
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 73-76
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 77-80
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 81-84
|
||||
'*', '-', '+', '\n', '1', '2', '3', '4', '5', // 85-97
|
||||
'6', '7', '8', '9', '0', '.', 0xb1, // 97-100
|
||||
};
|
||||
|
||||
|
||||
static uint8_t last_keys[NUM_KEYS];
|
||||
|
||||
static uint8_t * hid_host_descriptor_storage;
|
||||
static uint16_t hid_host_descriptor_storage_len;
|
||||
|
||||
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_linked_list_t connections;
|
||||
static uint16_t hid_host_cid_counter = 0;
|
||||
|
||||
static btstack_packet_handler_t hid_callback;
|
||||
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);
|
||||
|
||||
@ -98,7 +152,6 @@ static bool hid_descriptor_storage_store(hid_host_connection_t * connection, uin
|
||||
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;
|
||||
|
||||
@ -120,6 +173,15 @@ static void hid_descriptor_storage_delete(hid_host_connection_t * connection){
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t * hid_descriptor_storage_get_descriptor_data(hid_host_connection_t * connection){
|
||||
return &hid_host_descriptor_storage[connection->hid_descriptor_offset];
|
||||
}
|
||||
|
||||
static const uint16_t hid_descriptor_storage_get_descriptor_len(hid_host_connection_t * connection){
|
||||
return connection->hid_descriptor_len;
|
||||
}
|
||||
|
||||
|
||||
// HID Util
|
||||
static inline void hid_emit_connected_event(hid_host_connection_t * context, uint8_t status){
|
||||
uint8_t event[15];
|
||||
@ -146,11 +208,27 @@ static inline void hid_emit_event(hid_host_connection_t * context, uint8_t subev
|
||||
pos++; // skip len
|
||||
event[pos++] = subevent_type;
|
||||
little_endian_store_16(event,pos,context->hid_cid);
|
||||
pos+=2;
|
||||
pos += 2;
|
||||
event[1] = pos - 2;
|
||||
hid_callback(HCI_EVENT_PACKET, context->hid_cid, &event[0], pos);
|
||||
}
|
||||
|
||||
static inline void hid_emit_incoming_connection_event(hid_host_connection_t * context){
|
||||
uint8_t event[13];
|
||||
int pos = 0;
|
||||
event[pos++] = HCI_EVENT_HID_META;
|
||||
pos++; // skip len
|
||||
event[pos++] = HID_SUBEVENT_INCOMING_CONNECTION;
|
||||
little_endian_store_16(event, pos, context->hid_cid);
|
||||
pos += 2;
|
||||
reverse_bd_addr(context->remote_addr, &event[pos]);
|
||||
pos += 6;
|
||||
little_endian_store_16(event,pos,context->con_handle);
|
||||
pos += 2;
|
||||
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){
|
||||
@ -170,9 +248,13 @@ static hid_host_connection_t * hid_host_create_connection(bd_addr_t remote_addr)
|
||||
}
|
||||
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_connectionhid_host_connect, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
|
||||
connection->control_cid = 0;
|
||||
connection->control_psm = 0;
|
||||
connection->interrupt_cid = 0;
|
||||
connection->interrupt_psm = 0;
|
||||
connection->con_handle = HCI_CON_HANDLE_INVALID;
|
||||
|
||||
(void)memcpy(connection->remote_addr, remote_addr, 6);
|
||||
btstack_linked_list_add(&connections, (btstack_linked_item_t *) connection);
|
||||
return connection;
|
||||
}
|
||||
@ -204,7 +286,7 @@ static hid_host_connection_t * hid_host_get_connection_for_l2cap_cid(uint16_t l2
|
||||
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->interrupt_cid != l2cap_cid || connection->control_cid != l2cap_cid) continue;
|
||||
if ((connection->interrupt_cid != l2cap_cid) && (connection->control_cid != l2cap_cid)) continue;
|
||||
return connection;
|
||||
}
|
||||
return NULL;
|
||||
@ -308,6 +390,9 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_
|
||||
}
|
||||
printf("HID Descriptor:\n");
|
||||
printf_hexdump(descriptor, descriptor_len);
|
||||
|
||||
printf("Stored Descriptor %d\n", hid_descriptor_storage_get_descriptor_len(connection));
|
||||
printf_hexdump(hid_descriptor_storage_get_descriptor_data(connection), hid_descriptor_storage_get_descriptor_len(connection));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -334,7 +419,6 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_
|
||||
|
||||
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);
|
||||
printf("l2cap_create_channel HID 0x%02x\n", connection->control_cid);
|
||||
if (status){
|
||||
printf("Connecting to HID Control failed: 0x%02x\n", connection->control_cid);
|
||||
}
|
||||
@ -355,68 +439,22 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// Simplified US Keyboard with Shift modifier
|
||||
|
||||
#define CHAR_ILLEGAL 0xff
|
||||
#define CHAR_RETURN '\n'
|
||||
#define CHAR_ESCAPE 27
|
||||
#define CHAR_TAB '\t'
|
||||
#define CHAR_BACKSPACE 0x7f
|
||||
|
||||
//
|
||||
// English (US)
|
||||
//
|
||||
static const uint8_t keytable_us_none [] = {
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 0-3
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', // 4-13
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', // 14-23
|
||||
'u', 'v', 'w', 'x', 'y', 'z', // 24-29
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', // 30-39
|
||||
CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ', // 40-44
|
||||
'-', '=', '[', ']', '\\', CHAR_ILLEGAL, ';', '\'', 0x60, ',', // 45-54
|
||||
'.', '/', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 55-60
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 61-64
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 65-68
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 69-72
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 73-76
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 77-80
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 81-84
|
||||
'*', '-', '+', '\n', '1', '2', '3', '4', '5', // 85-97
|
||||
'6', '7', '8', '9', '0', '.', 0xa7, // 97-100
|
||||
};
|
||||
|
||||
static const uint8_t keytable_us_shift[] = {
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 0-3
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', // 4-13
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', // 14-23
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z', // 24-29
|
||||
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', // 30-39
|
||||
CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ', // 40-44
|
||||
'_', '+', '{', '}', '|', CHAR_ILLEGAL, ':', '"', 0x7E, '<', // 45-54
|
||||
'>', '?', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 55-60
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 61-64
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 65-68
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 69-72
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 73-76
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 77-80
|
||||
CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, // 81-84
|
||||
'*', '-', '+', '\n', '1', '2', '3', '4', '5', // 85-97
|
||||
'6', '7', '8', '9', '0', '.', 0xb1, // 97-100
|
||||
};
|
||||
|
||||
|
||||
#define NUM_KEYS 6
|
||||
static uint8_t last_keys[NUM_KEYS];
|
||||
static void hid_host_handle_interrupt_report(const uint8_t * report, uint16_t report_len){
|
||||
static void hid_host_handle_interrupt_report(hid_host_connection_t * connection, const uint8_t * report, uint16_t report_len){
|
||||
// check if HID Input Report
|
||||
if (report_len < 1) return;
|
||||
if (*report != 0xa1) return;
|
||||
report++;
|
||||
report_len--;
|
||||
|
||||
printf("hid_host_handle_interrupt_report len %d\n", hid_descriptor_storage_get_descriptor_len(connection));
|
||||
printf_hexdump(hid_descriptor_storage_get_descriptor_data(connection), hid_descriptor_storage_get_descriptor_len(connection));
|
||||
|
||||
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(connection),
|
||||
hid_descriptor_storage_get_descriptor_len(connection),
|
||||
HID_REPORT_TYPE_INPUT, report, report_len);
|
||||
|
||||
int shift = 0;
|
||||
uint8_t new_keys[NUM_KEYS];
|
||||
memset(new_keys, 0, sizeof(new_keys));
|
||||
@ -468,7 +506,7 @@ static void hid_host_handle_interrupt_report(const uint8_t * report, uint16_t re
|
||||
}
|
||||
memcpy(last_keys, new_keys, NUM_KEYS);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static void hid_host_handle_control_packet(hid_host_connection_t * connection, uint8_t *packet, uint16_t size){
|
||||
UNUSED(size);
|
||||
@ -533,8 +571,7 @@ static void hid_host_handle_control_packet(hid_host_connection_t * connection, u
|
||||
|
||||
switch ((hid_control_param_t)param){
|
||||
case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG:
|
||||
// hid_host_emit_event(device, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG);
|
||||
connection->unplugged = true;
|
||||
hid_emit_event(connection, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -545,6 +582,7 @@ static void hid_host_handle_control_packet(hid_host_connection_t * connection, u
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
@ -558,10 +596,12 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
switch (packet_type) {
|
||||
|
||||
case L2CAP_DATA_PACKET:
|
||||
printf("L2CAP_DATA_PACKET channel 0x%02x\n", channel);
|
||||
connection = hid_host_get_connection_for_l2cap_cid(channel);
|
||||
if (!connection) break;
|
||||
|
||||
if (channel == connection->interrupt_cid){
|
||||
// hid_host_handle_interrupt_report(packet, size);
|
||||
hid_host_handle_interrupt_report(connection, packet, size);
|
||||
break;
|
||||
}
|
||||
if (channel == connection->control_cid){
|
||||
@ -577,22 +617,15 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
connection = hid_host_create_connection(address);
|
||||
if (!connection) {
|
||||
if (!connection){
|
||||
log_error("Cannot create connection for %s", bd_addr_to_str(address));
|
||||
l2cap_decline_connection(channel);
|
||||
break;
|
||||
@ -602,10 +635,10 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
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);
|
||||
|
||||
hid_emit_incoming_connection_event(connection);
|
||||
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));
|
||||
@ -667,6 +700,8 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
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;
|
||||
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);
|
||||
break;
|
||||
|
||||
@ -704,18 +739,79 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid);
|
||||
if (!connection) return;
|
||||
|
||||
if (connection->control_cid == l2cap_cid){
|
||||
switch(connection->state){
|
||||
case HID_HOST_CONNECTION_ESTABLISHED:
|
||||
if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_SUSPEND) != 0){
|
||||
connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_SUSPEND;
|
||||
uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_SUSPEND };
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, 1);
|
||||
break;
|
||||
}
|
||||
if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND) != 0){
|
||||
connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND;
|
||||
uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_EXIT_SUSPEND };
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, 1);
|
||||
break;
|
||||
}
|
||||
if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG) != 0){
|
||||
connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG;
|
||||
uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG };
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, 1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_HOST_W2_SEND_GET_REPORT:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_GET_REPORT << 4) | connection->report_type;
|
||||
uint8_t report[] = {header, connection->report_id};
|
||||
// TODO: optional Report ID (1)
|
||||
// TODO: optional Maximum number of bytes to transfer during data phase, little end. (2)
|
||||
|
||||
connection->state = HID_HOST_W4_GET_REPORT_RESPONSE;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
|
||||
break;
|
||||
}
|
||||
case HID_HOST_W2_SEND_SET_REPORT:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_SET_REPORT << 4) | connection->report_type;
|
||||
connection->state = HID_HOST_W4_SET_REPORT_RESPONSE;
|
||||
|
||||
l2cap_reserve_packet_buffer();
|
||||
uint8_t * out_buffer = l2cap_get_outgoing_buffer();
|
||||
out_buffer[0] = header;
|
||||
out_buffer[1] = connection->report_id;
|
||||
(void)memcpy(out_buffer + 2, connection->report, connection->report_len);
|
||||
if (connection->boot_mode){
|
||||
l2cap_send_prepared(connection->interrupt_cid, connection->report_len + 2);
|
||||
} else {
|
||||
l2cap_send_prepared(connection->control_cid, connection->report_len + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HID_HOST_W2_SEND_GET_PROTOCOL:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_GET_PROTOCOL << 4);
|
||||
uint8_t report[] = {header};
|
||||
connection->state = HID_HOST_W4_GET_PROTOCOL_RESPONSE;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
|
||||
break;
|
||||
}
|
||||
case HID_HOST_W2_SEND_SET_PROTOCOL:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | connection->protocol_mode;
|
||||
uint8_t report[] = {header};
|
||||
|
||||
connection->state = HID_HOST_W4_SET_PROTOCOL_RESPONSE;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
printf("L2CAP_EVENT_CAN_SEND_NOW, hid_host.state = %d\n", connection->state);
|
||||
switch(connection->state){
|
||||
case HID_HOST_W2_SEND_GET_REPORT:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_GET_REPORT << 4) | connection->report_type;
|
||||
uint8_t report[] = {header, connection->report_id};
|
||||
// TODO: optional Report ID (1)
|
||||
// TODO: optional Maximum number of bytes to transfer during data phase, little end. (2)
|
||||
|
||||
connection->state = HID_HOST_W4_GET_REPORT_RESPONSE;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
|
||||
break;
|
||||
}
|
||||
|
||||
case HID_HOST_W2_SEND_SET_REPORT:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_SET_REPORT << 4) | connection->report_type;
|
||||
connection->state = HID_HOST_W4_SET_REPORT_RESPONSE;
|
||||
@ -732,21 +828,7 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HID_HOST_W2_SEND_GET_PROTOCOL:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_GET_PROTOCOL << 4);
|
||||
uint8_t report[] = {header};
|
||||
connection->state = HID_HOST_W4_GET_PROTOCOL_RESPONSE;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
|
||||
break;
|
||||
}
|
||||
case HID_HOST_W2_SEND_SET_PROTOCOL:{
|
||||
uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | connection->protocol_mode;
|
||||
uint8_t report[] = {header};
|
||||
|
||||
connection->state = HID_HOST_W4_SET_PROTOCOL_RESPONSE;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
|
||||
break;
|
||||
}
|
||||
|
||||
case HID_HOST_W2_SEND_REPORT:{
|
||||
connection->state = HID_HOST_W4_SEND_REPORT_RESPONSE;
|
||||
uint8_t header = (HID_MESSAGE_TYPE_DATA << 4) | connection->report_type;
|
||||
@ -762,6 +844,10 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8
|
||||
break;
|
||||
}
|
||||
|
||||
if (connection->control_tasks != 0){
|
||||
l2cap_request_can_send_now_event(connection->control_cid);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -808,9 +894,22 @@ static void hid_host_handle_start_sdp_client_query(void * context){
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){
|
||||
UNUSED(protocol_mode);
|
||||
uint8_t hid_host_accept_connection(uint16_t hid_cid){
|
||||
hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
|
||||
if (!connection) return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
l2cap_accept_connection(connection->control_cid);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t hid_host_decline_connection(uint16_t hid_cid){
|
||||
hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
|
||||
if (!connection) return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
l2cap_decline_connection(connection->control_cid);
|
||||
hid_host_finalize_connection(connection);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){
|
||||
if (hid_cid == NULL) {
|
||||
return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
}
|
||||
@ -823,22 +922,15 @@ uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mod
|
||||
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;
|
||||
connection->protocol_mode = protocol_mode;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -869,10 +961,36 @@ void hid_host_disconnect(uint16_t hid_cid){
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_request_can_send_now_event(uint16_t hid_cid){
|
||||
UNUSED(hid_cid);
|
||||
|
||||
static inline uint8_t hid_host_send_control_message(uint16_t hid_cid, uint8_t control_message_bitmask){
|
||||
hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
|
||||
if (!connection || !connection->control_cid) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
|
||||
if (connection->state < HID_HOST_CONTROL_CONNECTION_ESTABLISHED) {
|
||||
return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
}
|
||||
if (connection->state >= HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){
|
||||
return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
}
|
||||
|
||||
connection->control_tasks |= control_message_bitmask;
|
||||
l2cap_request_can_send_now_event(connection->control_cid);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t hid_host_send_suspend(uint16_t hid_cid){
|
||||
return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_SUSPEND);
|
||||
}
|
||||
|
||||
uint8_t hid_host_send_exit_suspend(uint16_t hid_cid){
|
||||
return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND);
|
||||
}
|
||||
|
||||
uint8_t hid_host_send_virtual_cable_unplug(uint16_t hid_cid){
|
||||
return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG);
|
||||
}
|
||||
|
||||
|
||||
void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
|
||||
UNUSED(hid_cid);
|
||||
UNUSED(message);
|
||||
@ -992,26 +1110,3 @@ uint8_t hid_host_send_output_report(uint16_t hid_cid, uint8_t report_id, uint8_t
|
||||
return hid_host_send_report(hid_cid, HID_REPORT_TYPE_OUTPUT, report_id, report, report_len);
|
||||
}
|
||||
|
||||
void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
|
||||
hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
|
||||
if (!connection || !connection->control_cid) return;
|
||||
l2cap_send(connection->control_cid, (uint8_t*) message, message_len);
|
||||
}
|
||||
|
||||
uint8_t hid_host_send_suspend(uint16_t hid_cid){
|
||||
uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_SUSPEND };
|
||||
hid_host_send_control_message(hid_cid, &report[0], sizeof(report));
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t hid_host_send_exit_suspend(uint16_t hid_cid){
|
||||
uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_EXIT_SUSPEND };
|
||||
hid_host_send_control_message(hid_cid, &report[0], sizeof(report));
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t hid_host_send_virtual_cable_unplug(uint16_t hid_cid){
|
||||
uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG };
|
||||
hid_host_send_control_message(hid_cid, &report[0], sizeof(report));
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
@ -92,8 +92,7 @@ typedef struct {
|
||||
|
||||
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;
|
||||
@ -104,6 +103,12 @@ typedef struct {
|
||||
hid_report_type_t report_type;
|
||||
uint8_t report_id;
|
||||
|
||||
// control message, bit mask:
|
||||
// SUSSPEND 1
|
||||
// EXIT_SUSSPEND 2
|
||||
// VIRTUAL_CABLE_UNPLUG 4
|
||||
uint8_t control_tasks;
|
||||
|
||||
// set report
|
||||
uint8_t * report;
|
||||
uint16_t report_len;
|
||||
@ -132,18 +137,20 @@ void hid_host_register_packet_handler(btstack_packet_handler_t callback);
|
||||
*/
|
||||
uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid);
|
||||
|
||||
uint8_t hid_host_accept_connection(uint16_t hid_cid);
|
||||
uint8_t hid_host_decline_connection(uint16_t hid_cid);
|
||||
|
||||
/*
|
||||
* @brief Disconnect from HID Host
|
||||
* @param hid_cid
|
||||
*/
|
||||
void hid_host_disconnect(uint16_t hid_cid);
|
||||
|
||||
/**
|
||||
* @brief Request can send now event to send HID Report
|
||||
* Generates an HID_SUBEVENT_CAN_SEND_NOW subevent
|
||||
* @param hid_cid
|
||||
*/
|
||||
void hid_host_request_can_send_now_event(uint16_t hid_cid);
|
||||
// Control messages:
|
||||
uint8_t hid_host_send_suspend(uint16_t hid_cid);
|
||||
uint8_t hid_host_send_exit_suspend(uint16_t hid_cid);
|
||||
|
||||
uint8_t hid_host_send_virtual_cable_unplug(uint16_t hid_cid);
|
||||
|
||||
/**
|
||||
* @brief Send HID message on interrupt channel
|
||||
@ -151,20 +158,10 @@ void hid_host_request_can_send_now_event(uint16_t hid_cid);
|
||||
*/
|
||||
void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len);
|
||||
|
||||
/**
|
||||
* @brief Send HID message on control channel
|
||||
* @param hid_cid
|
||||
*/
|
||||
void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len);
|
||||
|
||||
|
||||
uint8_t hid_host_send_set_protocol_mode(uint16_t hid_cid, hid_protocol_mode_t protocol_mode);
|
||||
uint8_t hid_host_send_get_protocol(uint16_t hid_cid);
|
||||
|
||||
uint8_t hid_host_send_suspend(uint16_t hid_cid);
|
||||
uint8_t hid_host_send_exit_suspend(uint16_t hid_cid);
|
||||
|
||||
uint8_t hid_host_send_virtual_cable_unplug(uint16_t hid_cid);
|
||||
|
||||
uint8_t hid_host_send_output_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len);
|
||||
|
||||
|
@ -63,7 +63,6 @@ static bool send_through_interrupt_channel = false;
|
||||
|
||||
// SDP
|
||||
static uint8_t hid_descriptor[MAX_ATTRIBUTE_VALUE_SIZE];
|
||||
static uint16_t hid_descriptor_len;
|
||||
|
||||
// PTS
|
||||
static const char * remote_addr_string = "00:1B:DC:08:E2:5C";
|
||||
@ -78,7 +77,7 @@ static void hid_host_setup(void){
|
||||
l2cap_init();
|
||||
|
||||
// Initialize HID Host
|
||||
hid_host_init(hid_descriptor, hid_descriptor_len);
|
||||
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
|
||||
@ -130,7 +129,19 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
|
||||
|
||||
case HCI_EVENT_HID_META:
|
||||
switch (hci_event_hid_meta_get_subevent_code(packet)){
|
||||
case HID_SUBEVENT_INCOMING_CONNECTION:
|
||||
if (unplugged){
|
||||
hid_host_decline_connection(hid_subevent_incoming_connection_get_hid_cid(packet));
|
||||
break;
|
||||
}
|
||||
hid_host_accept_connection(hid_subevent_incoming_connection_get_hid_cid(packet));
|
||||
break;
|
||||
|
||||
case HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG:
|
||||
if (hid_host_cid != hid_subevent_virtual_cable_unplug_get_hid_cid(packet)) return;
|
||||
unplugged = true;
|
||||
break;
|
||||
|
||||
case HID_SUBEVENT_CONNECTION_OPENED:
|
||||
status = hid_subevent_connection_opened_get_status(packet);
|
||||
if (status) {
|
||||
@ -170,7 +181,13 @@ 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 - start SDP scan and connect");
|
||||
printf("c - start SDP scan and connect to %s\n", remote_addr_string);
|
||||
printf("C - disconnect from %s\n", remote_addr_string);
|
||||
printf("\n");
|
||||
printf("s - suspend\n");
|
||||
printf("S - exit suspend\n");
|
||||
printf("U - unplug\n");
|
||||
printf("\n");
|
||||
printf("o - get output report\n");
|
||||
printf("Ctrl-c - exit\n");
|
||||
printf("---\n");
|
||||
@ -200,7 +217,7 @@ static void stdin_process(char cmd){
|
||||
printf("Cannot connect, host is unplugged.\n");
|
||||
break;
|
||||
}
|
||||
printf("Start SDP scan and connect to %s.\n", bd_addr_to_str(remote_addr));
|
||||
printf("Start SDP scan and connect to %s.\n", remote_addr_string);
|
||||
|
||||
if (boot_mode){
|
||||
status = hid_host_connect(remote_addr, HID_PROTOCOL_MODE_BOOT, &hid_host_cid);
|
||||
@ -210,7 +227,7 @@ static void stdin_process(char cmd){
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
printf("Disconnect from %s...\n", bd_addr_to_str(remote_addr));
|
||||
printf("Disconnect from %s...\n", remote_addr_string);
|
||||
hid_host_disconnect(hid_host_cid);
|
||||
break;
|
||||
|
||||
@ -222,7 +239,7 @@ static void stdin_process(char cmd){
|
||||
printf("Send \'Exit suspend\'\n");
|
||||
hid_host_send_exit_suspend(hid_host_cid);
|
||||
break;
|
||||
case 'u':
|
||||
case 'U':
|
||||
printf("Send \'Unplug\'\n");
|
||||
unplugged = true;
|
||||
hid_host_send_virtual_cable_unplug(hid_host_cid);
|
||||
|
Loading…
x
Reference in New Issue
Block a user