From ab30106e8f1393df666b2ba62fdae3513471491a Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 7 Jan 2021 17:08:52 +0100 Subject: [PATCH] hid_host: move code from test code to lib --- src/classic/hid_host.c | 501 ++++++++++++++++++++++++++++++-- src/classic/hid_host.h | 25 +- test/pts/hid_host_test.c | 613 +++------------------------------------ 3 files changed, 545 insertions(+), 594 deletions(-) diff --git a/src/classic/hid_host.c b/src/classic/hid_host.c index 303a8ed8c..64536c27d 100644 --- a/src/classic/hid_host.c +++ b/src/classic/hid_host.c @@ -139,6 +139,18 @@ static inline void hid_emit_connected_event(hid_host_connection_t * context, uin hid_callback(HCI_EVENT_PACKET, context->hid_cid, &event[0], pos); } +static inline void hid_emit_event(hid_host_connection_t * context, uint8_t subevent_type){ + uint8_t event[5]; + int pos = 0; + event[pos++] = HCI_EVENT_HID_META; + pos++; // skip len + event[pos++] = subevent_type; + little_endian_store_16(event,pos,context->hid_cid); + 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){ @@ -159,7 +171,7 @@ 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_connection, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr)); + printf("hid_host_create_connectionhid_host_connect, 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; @@ -176,7 +188,7 @@ static hid_host_connection_t * hid_host_get_connection_for_bd_addr(bd_addr_t add return NULL; } -static hid_host_connection_t * hid_host_connection_for_hid_cid(uint16_t hid_cid){ +static hid_host_connection_t * hid_host_get_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)){ @@ -187,6 +199,17 @@ static hid_host_connection_t * hid_host_connection_for_hid_cid(uint16_t hid_cid) return NULL; } +static hid_host_connection_t * hid_host_get_connection_for_l2cap_cid(uint16_t l2cap_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->interrupt_cid != l2cap_cid || connection->control_cid != l2cap_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); @@ -205,11 +228,13 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_ 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); + hid_host_connection_t * connection = hid_host_get_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; } + + if (connection->state != HID_HOST_W4_SDP_QUERY_RESULT) return; switch (hci_event_packet_get_type(packet)){ case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: @@ -307,10 +332,11 @@ static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_ break; } - printf("Setup HID\n"); + 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", status); + printf("Connecting to HID Control failed: 0x%02x\n", connection->control_cid); } break; @@ -329,6 +355,196 @@ 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){ + // 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); + int shift = 0; + uint8_t new_keys[NUM_KEYS]; + memset(new_keys, 0, sizeof(new_keys)); + int new_keys_count = 0; + while (btstack_hid_parser_has_more(&parser)){ + uint16_t usage_page; + uint16_t usage; + int32_t value; + btstack_hid_parser_get_field(&parser, &usage_page, &usage, &value); + if (usage_page != 0x07) continue; + switch (usage){ + case 0xe1: + case 0xe6: + if (value){ + shift = 1; + } + continue; + case 0x00: + continue; + default: + break; + } + if (usage >= sizeof(keytable_us_none)) continue; + + // store new keys + new_keys[new_keys_count++] = usage; + + // check if usage was used last time (and ignore in that case) + int i; + for (i=0;i> 4); + message_status = (hid_handshake_param_type_t)(packet[0] & 0x0F); + printf("HID Control data, message_type 0x%02x, status 0x%02x: \n", message_type, message_status); + + // TODO handle handshake message_status + switch (message_type){ + case HID_MESSAGE_TYPE_DATA: + switch (connection->state){ + case HID_HOST_W4_GET_REPORT_RESPONSE: + printf("HID_HOST_W4_GET_REPORT_RESPONSE \n"); + break; + case HID_HOST_W4_SET_REPORT_RESPONSE: + printf("HID_HOST_W4_SET_REPORT_RESPONSE \n"); + break; + case HID_HOST_W4_GET_PROTOCOL_RESPONSE: + printf("HID_HOST_W4_GET_PROTOCOL_RESPONSE \n"); + break; + case HID_HOST_W4_SEND_REPORT_RESPONSE: + printf("HID_HOST_W4_SEND_REPORT_RESPONSE\n"); + break; + case HID_HOST_W4_SET_PROTOCOL_RESPONSE: + printf("HID_HOST_W4_SET_PROTOCOL_RESPONSE \n"); + switch (message_status){ + case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL: + switch(connection->protocol_mode){ + case HID_PROTOCOL_MODE_BOOT:{ + status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->interrupt_psm, 48, &connection->interrupt_cid); + if (status){ + printf("Connecting to HID Control failed: 0x%02x\n", status); + connection->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED; + break; + } + connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; + break; + } + case HID_PROTOCOL_MODE_REPORT: + break; + default: + break; + } + default: + break; + } + break; + default: + printf("HID_MESSAGE_TYPE_DATA ???\n"); + break; + } + connection->state = HID_HOST_CONNECTION_ESTABLISHED; + break; + + case HID_MESSAGE_TYPE_HID_CONTROL: + param = packet[0] & 0x0F; + + 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; + break; + default: + break; + } + break; + default: + break; + } +} + static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(channel); UNUSED(size); @@ -336,14 +552,24 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 uint8_t event; bd_addr_t address; uint8_t status; - uint16_t cid; + uint16_t l2cap_cid; hid_host_connection_t * connection; - // uint8_t param; - // hid_message_type_t message_type; - // hid_handshake_param_type_t message_status; - switch (packet_type) { + + case L2CAP_DATA_PACKET: + connection = hid_host_get_connection_for_l2cap_cid(channel); + if (!connection) break; + if (channel == connection->interrupt_cid){ + // hid_host_handle_interrupt_report(packet, size); + break; + } + if (channel == connection->control_cid){ + hid_host_handle_control_packet(connection, packet, size); + break; + } + break; + case HCI_EVENT_PACKET: event = hci_event_packet_get_type(packet); switch (event) { @@ -415,8 +641,8 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 break; } - cid = l2cap_event_channel_opened_get_local_cid(packet); - + // l2cap_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; @@ -440,11 +666,8 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 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: @@ -453,6 +676,92 @@ static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8 // disconnect? break; + case L2CAP_EVENT_CHANNEL_CLOSED: + l2cap_cid = l2cap_event_channel_closed_get_local_cid(packet); + connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid); + if (!connection) return; + + if (l2cap_cid == connection->interrupt_cid){ + connection->interrupt_cid = 0; + if (connection->state == HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){ + connection->state = HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED; + l2cap_disconnect(connection->control_cid, 0); + } + break; + } + + if (l2cap_cid == connection->control_cid){ + connection->control_cid = 0; + hid_emit_event(connection, HID_SUBEVENT_CONNECTION_CLOSED); + hid_descriptor_storage_delete(connection); + hid_host_finalize_connection(connection); + break; + } + break; + + case L2CAP_EVENT_CAN_SEND_NOW: + l2cap_cid = l2cap_event_can_send_now_get_local_cid(packet); + connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid); + if (!connection) return; + + 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; + + 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; + } + 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; + + 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); + l2cap_send_prepared(connection->interrupt_cid, connection->report_len + 2); + } + default: + break; + } + default: break; } @@ -523,6 +832,7 @@ uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mod 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; @@ -534,7 +844,29 @@ uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mod void hid_host_disconnect(uint16_t hid_cid){ - UNUSED(hid_cid); + hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); + if (!connection) return; + + switch (connection->state){ + case HID_HOST_IDLE: + case HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED: + case HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED: + return; + default: + break; + } + + if (connection->interrupt_cid){ + connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED; + l2cap_disconnect(connection->interrupt_cid, 0); // reason isn't used + return; + } + + if (connection->control_cid){ + connection->state = HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED; + l2cap_disconnect(connection->control_cid, 0); // reason isn't used + return; + } } void hid_host_request_can_send_now_event(uint16_t hid_cid){ @@ -547,8 +879,139 @@ void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, UNUSED(message_len); } +static uint8_t hid_host_send_get_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id){ + 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_CONNECTION_ESTABLISHED){ + return ERROR_CODE_COMMAND_DISALLOWED; + } + + connection->state = HID_HOST_W2_SEND_GET_REPORT; + connection->report_type = report_type; + connection->report_id = report_id; + + l2cap_request_can_send_now_event(connection->control_cid); + return ERROR_CODE_SUCCESS; +} + +uint8_t hid_host_send_get_output_report(uint16_t hid_cid, uint8_t report_id){ + return hid_host_send_get_report(hid_cid, HID_REPORT_TYPE_OUTPUT, report_id); +} + +uint8_t hid_host_send_get_feature_report(uint16_t hid_cid, uint8_t report_id){ + return hid_host_send_get_report(hid_cid, HID_REPORT_TYPE_FEATURE, report_id); +} + +uint8_t hid_host_send_get_input_report(uint16_t hid_cid, uint8_t report_id){ + return hid_host_send_get_report(hid_cid, HID_REPORT_TYPE_INPUT, report_id); +} + +static uint8_t hid_host_send_set_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id, uint8_t * report, uint8_t report_len){ + 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_CONNECTION_ESTABLISHED){ + printf("hid_host_send_set_report: unexpected state 0%02x\n", HID_HOST_CONNECTION_ESTABLISHED); + return ERROR_CODE_COMMAND_DISALLOWED; + } + + connection->state = HID_HOST_W2_SEND_SET_REPORT; + connection->report_type = report_type; + connection->report_id = report_id; + connection->report = report; + connection->report_len = report_len; + + l2cap_request_can_send_now_event(connection->control_cid); + return ERROR_CODE_SUCCESS; +} + +uint8_t hid_host_send_set_output_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){ + return hid_host_send_set_report(hid_cid, HID_REPORT_TYPE_OUTPUT, report_id, report, report_len); +} + +uint8_t hid_host_send_set_feature_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){ + return hid_host_send_set_report(hid_cid, HID_REPORT_TYPE_FEATURE, report_id, report, report_len); +} + +uint8_t hid_host_send_set_input_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){ + return hid_host_send_set_report(hid_cid, HID_REPORT_TYPE_INPUT, report_id, report, report_len); +} + +uint8_t hid_host_send_get_protocol(uint16_t hid_cid){ + 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_CONNECTION_ESTABLISHED) return ERROR_CODE_COMMAND_DISALLOWED; + + connection->state = HID_HOST_W2_SEND_GET_PROTOCOL; + + l2cap_request_can_send_now_event(connection->control_cid); + return ERROR_CODE_SUCCESS; +} + +uint8_t hid_host_send_set_protocol_mode(uint16_t hid_cid, hid_protocol_mode_t protocol_mode){ + 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; + } + + connection->state = HID_HOST_W2_SEND_SET_PROTOCOL; + connection->protocol_mode = protocol_mode; + + l2cap_request_can_send_now_event(connection->control_cid); + return ERROR_CODE_SUCCESS; +} + + +static uint8_t hid_host_send_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id, uint8_t * report, uint8_t report_len){ + 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_CONNECTION_ESTABLISHED){ + return ERROR_CODE_COMMAND_DISALLOWED; + } + + connection->state = HID_HOST_W2_SEND_REPORT; + connection->report_type = report_type; + connection->report_id = report_id; + connection->report = report; + connection->report_len = report_len; + + l2cap_request_can_send_now_event(connection->control_cid); + return ERROR_CODE_SUCCESS; +} + +uint8_t hid_host_send_output_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_OUTPUT, report_id, report, report_len); +} + void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){ - UNUSED(hid_cid); - UNUSED(message); - UNUSED(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; } \ No newline at end of file diff --git a/src/classic/hid_host.h b/src/classic/hid_host.h index 6fe9746c8..154a9e2f5 100644 --- a/src/classic/hid_host.h +++ b/src/classic/hid_host.h @@ -69,10 +69,15 @@ typedef enum { 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_W4_SEND_REPORT_RESPONSE, + + HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED, + HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED } hid_host_state_t; typedef struct { + btstack_linked_item_t item; + uint16_t hid_cid; hci_con_handle_t con_handle; @@ -153,6 +158,24 @@ void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, 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); + +uint8_t hid_host_send_set_output_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len); +uint8_t hid_host_send_set_feature_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len); +uint8_t hid_host_send_set_input_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len); + +uint8_t hid_host_send_get_output_report(uint16_t hid_cid, uint8_t report_id); +uint8_t hid_host_send_get_feature_report(uint16_t hid_cid, uint8_t report_id); +uint8_t hid_host_send_get_input_report(uint16_t hid_cid, uint8_t report_id); + /* API_END */ diff --git a/test/pts/hid_host_test.c b/test/pts/hid_host_test.c index b00ffa2c5..692a2853c 100644 --- a/test/pts/hid_host_test.c +++ b/test/pts/hid_host_test.c @@ -35,17 +35,13 @@ * */ -#define __BTSTACK_FILE__ "hid_host_tast.c" +#define __BTSTACK_FILE__ "hid_host_test.c" /* - * hid_host_tast.c + * hid_host_test.c */ -/* EXAMPLE_START(hid_host_tast): HID Host Demo - * - * @text This example implements an HID Host. For now, it connnects to a fixed device, queries the HID SDP - * record and opens the HID Control + Interrupt channels - */ + #include #include @@ -61,296 +57,19 @@ static enum { } app_state = APP_IDLE; static uint16_t hid_host_cid = 0; +static bool unplugged; +static bool boot_mode = false; +static bool send_through_interrupt_channel = false; // SDP static uint8_t hid_descriptor[MAX_ATTRIBUTE_VALUE_SIZE]; static uint16_t hid_descriptor_len; -static uint16_t hid_interrupt_psm; - // PTS static const char * remote_addr_string = "00:1B:DC:08:E2:5C"; - static bd_addr_t remote_addr; static btstack_packet_callback_registration_t hci_event_callback_registration; - - -// 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 */ -}; - - -// hid device state -typedef struct hid_host { - uint16_t cid; - bd_addr_t bd_addr; - hci_con_handle_t con_handle; - uint16_t control_cid; - uint16_t interrupt_cid; - - bool unplugged; - - // get report - hid_report_type_t report_type; - uint8_t report_id; - - // set report - uint8_t * report; - uint16_t report_len; - - // set protocol - hid_protocol_mode_t protocol_mode; - - hid_host_state_t state; - uint8_t user_request_can_send_now; -} hid_host_t; - - -static hid_host_t _hid_host; -static bool boot_mode = false; -static bool send_through_interrupt_channel = false; - -static hid_host_t * hid_host_get_instance_for_hid_cid(uint16_t hid_cid){ - if (_hid_host.cid == hid_cid){ - return &_hid_host; - } - return NULL; -} - -static uint8_t hid_host_send_get_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host || !hid_host->control_cid){ - return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - } - if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED){ - return ERROR_CODE_COMMAND_DISALLOWED; - } - - hid_host->state = HID_HOST_W2_SEND_GET_REPORT; - hid_host->report_type = report_type; - hid_host->report_id = report_id; - - l2cap_request_can_send_now_event(hid_host->control_cid); - return ERROR_CODE_SUCCESS; -} - -static uint8_t hid_host_send_get_output_report(uint16_t hid_cid, uint8_t report_id){ - return hid_host_send_get_report(hid_cid, HID_REPORT_TYPE_OUTPUT, report_id); -} - -static uint8_t hid_host_send_get_feature_report(uint16_t hid_cid, uint8_t report_id){ - return hid_host_send_get_report(hid_cid, HID_REPORT_TYPE_FEATURE, report_id); -} - -static uint8_t hid_host_send_get_input_report(uint16_t hid_cid, uint8_t report_id){ - return hid_host_send_get_report(hid_cid, HID_REPORT_TYPE_INPUT, report_id); -} - -static uint8_t hid_host_send_set_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id, uint8_t * report, uint8_t report_len){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host || !hid_host->control_cid){ - return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - } - - if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED){ - printf("hid_host_send_set_report: unexpected state 0%02x\n", HID_HOST_CONNECTION_ESTABLISHED); - return ERROR_CODE_COMMAND_DISALLOWED; - } - - hid_host->state = HID_HOST_W2_SEND_SET_REPORT; - hid_host->report_type = report_type; - hid_host->report_id = report_id; - hid_host->report = report; - hid_host->report_len = report_len; - - l2cap_request_can_send_now_event(hid_host->control_cid); - return ERROR_CODE_SUCCESS; -} - -static uint8_t hid_host_send_set_output_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){ - return hid_host_send_set_report(hid_cid, HID_REPORT_TYPE_OUTPUT, report_id, report, report_len); -} - -static uint8_t hid_host_send_set_feature_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){ - return hid_host_send_set_report(hid_cid, HID_REPORT_TYPE_FEATURE, report_id, report, report_len); -} - -static uint8_t hid_host_send_set_input_report(uint16_t hid_cid, uint8_t report_id, uint8_t * report, uint8_t report_len){ - return hid_host_send_set_report(hid_cid, HID_REPORT_TYPE_INPUT, report_id, report, report_len); -} - -static uint8_t hid_host_send_get_protocol(uint16_t hid_cid){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host || !hid_host->control_cid) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED) return ERROR_CODE_COMMAND_DISALLOWED; - - hid_host->state = HID_HOST_W2_SEND_GET_PROTOCOL; - - l2cap_request_can_send_now_event(hid_host->control_cid); - return ERROR_CODE_SUCCESS; -} - -static uint8_t hid_host_send_set_protocol_mode(uint16_t hid_cid, hid_protocol_mode_t protocol_mode){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host || !hid_host->control_cid) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - if (hid_host->state != HID_HOST_CONTROL_CONNECTION_ESTABLISHED){ - return ERROR_CODE_COMMAND_DISALLOWED; - } - - - hid_host->state = HID_HOST_W2_SEND_SET_PROTOCOL; - hid_host->protocol_mode = protocol_mode; - - l2cap_request_can_send_now_event(hid_host->control_cid); - return ERROR_CODE_SUCCESS; -} - - -static uint8_t hid_host_send_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id, uint8_t * report, uint8_t report_len){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host || !hid_host->control_cid){ - return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - } - if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED){ - return ERROR_CODE_COMMAND_DISALLOWED; - } - - hid_host->state = HID_HOST_W2_SEND_REPORT; - hid_host->report_type = report_type; - hid_host->report_id = report_id; - hid_host->report = report; - hid_host->report_len = report_len; - - l2cap_request_can_send_now_event(hid_host->control_cid); - return ERROR_CODE_SUCCESS; -} - -static uint8_t hid_host_send_output_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_OUTPUT, report_id, report, report_len); -} - - -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)){ - return &_hid_host; - } - return NULL; -} - -static void _hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host || !hid_host->control_cid) return; - l2cap_send(hid_host->control_cid, (uint8_t*) message, message_len); -} - -static uint8_t hid_host_send_suspend(uint16_t hcid){ - uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_SUSPEND }; - _hid_host_send_control_message(hcid, &report[0], sizeof(report)); - return ERROR_CODE_SUCCESS; -} - -static uint8_t hid_host_send_exit_suspend(uint16_t hcid){ - uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_EXIT_SUSPEND }; - _hid_host_send_control_message(hcid, &report[0], sizeof(report)); - return ERROR_CODE_SUCCESS; -} - -static uint8_t hid_host_send_virtual_cable_unplug(uint16_t hcid){ - uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG }; - _hid_host_send_control_message(hcid, &report[0], sizeof(report)); - return ERROR_CODE_SUCCESS; -} - -static void hid_host_disconnect_interrupt_channel(uint16_t hid_cid){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host){ - log_error("hid_host_disconnect_interrupt_channel: could not find hid device instace"); - return; - } - log_info("Disconnect from interrupt channel HID Host"); - if (hid_host->interrupt_cid){ - l2cap_disconnect(hid_host->interrupt_cid, 0); // reason isn't used - } -} - -static void hid_host_disconnect_control_channel(uint16_t hid_cid){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host){ - log_error("hid_host_disconnect_control_channel: could not find hid device instace"); - return; - } - log_info("Disconnect from control channel HID Host"); - if (hid_host->control_cid){ - l2cap_disconnect(hid_host->control_cid, 0); // reason isn't used - } -} - -static void _hid_host_disconnect(uint16_t hid_cid){ - hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); - if (!hid_host){ - log_error("hid_host_disconnect: could not find hid device instace"); - return; - } - log_info("Disconnect from HID Host"); - if (hid_host->interrupt_cid){ - l2cap_disconnect(hid_host->interrupt_cid, 0); // reason isn't used - } - if (hid_host->control_cid){ - l2cap_disconnect(hid_host->control_cid, 0); // reason isn't used - } -} -/* @section Main application configuration - * - * @text In the application configuration, L2CAP is initialized - */ - -/* LISTING_START(PanuSetup): Panu setup */ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void hid_host_setup(void){ @@ -360,6 +79,7 @@ static void hid_host_setup(void){ // Initialize HID Host hid_host_init(hid_descriptor, hid_descriptor_len); + 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); @@ -376,110 +96,25 @@ static void hid_host_setup(void){ } -/* - * @section HID Report Handler - * - * @text Use BTstack's compact HID Parser to process incoming HID Report - * Iterate over all fields and process fields with usage page = 0x07 / Keyboard - * Check if SHIFT is down and process first character (don't handle multiple key presses) - * - */ -#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){ - // 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); - int shift = 0; - uint8_t new_keys[NUM_KEYS]; - memset(new_keys, 0, sizeof(new_keys)); - int new_keys_count = 0; - while (btstack_hid_parser_has_more(&parser)){ - uint16_t usage_page; - uint16_t usage; - int32_t value; - btstack_hid_parser_get_field(&parser, &usage_page, &usage, &value); - if (usage_page != 0x07) continue; - switch (usage){ - case 0xe1: - case 0xe6: - if (value){ - shift = 1; - } - continue; - case 0x00: - continue; - default: - break; - } - if (usage >= sizeof(keytable_us_none)) continue; +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); - // store new keys - new_keys[new_keys_count++] = usage; - - // check if usage was used last time (and ignore in that case) - int i; - for (i=0;iinterrupt_cid){ - hid_host->interrupt_cid = 0; - } - if (l2cap_cid == hid_host->control_cid){ - hid_host->control_cid = 0; - } - if (!hid_host->interrupt_cid && !hid_host->control_cid){ - // hid_host->connected = 0; - hid_host->con_handle = HCI_CON_HANDLE_INVALID; - hid_host->cid = 0; - // hid_host_emit_event(device, HID_SUBEVENT_CONNECTION_CLOSED); - } - break; - - case L2CAP_EVENT_CAN_SEND_NOW: - l2cap_cid = l2cap_event_can_send_now_get_local_cid(packet); - hid_host = hid_host_get_instance_for_l2cap_cid(l2cap_cid); - if (!hid_host) return; - - printf("L2CAP_EVENT_CAN_SEND_NOW, hid_host.state = %d\n", hid_host->state); - switch(hid_host->state){ - case HID_HOST_W2_SEND_GET_REPORT:{ - uint8_t header = (HID_MESSAGE_TYPE_GET_REPORT << 4) | hid_host->report_type; - uint8_t report[] = {header, hid_host->report_id}; - // TODO: optional Report ID (1) - // TODO: optional Maximum number of bytes to transfer during data phase, little end. (2) - - hid_host->state = HID_HOST_W4_GET_REPORT_RESPONSE; - l2cap_send(hid_host->control_cid, (uint8_t*) report, sizeof(report)); - break; - } - case HID_HOST_W2_SEND_SET_REPORT:{ - uint8_t header = (HID_MESSAGE_TYPE_SET_REPORT << 4) | hid_host->report_type; - hid_host->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] = hid_host->report_id; - (void)memcpy(out_buffer + 2, hid_host->report, hid_host->report_len); - if (boot_mode){ - l2cap_send_prepared(hid_host->interrupt_cid, hid_host->report_len + 2); - } else { - l2cap_send_prepared(hid_host->control_cid, hid_host->report_len + 2); - } - break; - } - case HID_HOST_W2_SEND_GET_PROTOCOL:{ - uint8_t header = (HID_MESSAGE_TYPE_GET_PROTOCOL << 4); - uint8_t report[] = {header}; - hid_host->state = HID_HOST_W4_GET_PROTOCOL_RESPONSE; - l2cap_send(hid_host->control_cid, (uint8_t*) report, sizeof(report)); - break; - } - case HID_HOST_W2_SEND_SET_PROTOCOL:{ - uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | hid_host->protocol_mode; - uint8_t report[] = {header}; - - hid_host->state = HID_HOST_W4_SET_PROTOCOL_RESPONSE; - l2cap_send(hid_host->control_cid, (uint8_t*) report, sizeof(report)); - break; - } - case HID_HOST_W2_SEND_REPORT:{ - hid_host->state = HID_HOST_W4_SEND_REPORT_RESPONSE; - uint8_t header = (HID_MESSAGE_TYPE_DATA << 4) | hid_host->report_type; - - l2cap_reserve_packet_buffer(); - uint8_t * out_buffer = l2cap_get_outgoing_buffer(); - out_buffer[0] = header; - out_buffer[1] = hid_host->report_id; - (void)memcpy(out_buffer + 2, hid_host->report, hid_host->report_len); - l2cap_send_prepared(hid_host->interrupt_cid, hid_host->report_len + 2); - } - default: - break; - } default: break; } break; - case L2CAP_DATA_PACKET: - if (channel == _hid_host.interrupt_cid){ - printf("HID Interrupt data: \n"); - printf_hexdump(packet, size); - hid_host_handle_interrupt_report(packet, size); - break; - } - if (channel != _hid_host.control_cid) break; - hid_host = hid_host_get_instance_for_l2cap_cid(channel); - if (!hid_host) { - log_error("no host with cid 0x%02x", channel); - return; - } - message_type = (hid_message_type_t)(packet[0] >> 4); - message_status = (hid_handshake_param_type_t)(packet[0] & 0x0F); - printf("HID Control data, message_type 0x%02x, status 0x%02x: \n", message_type, message_status); - printf_hexdump(packet, size); - // TODO handle handshake message_status - switch (message_type){ - case HID_MESSAGE_TYPE_DATA: - switch (hid_host->state){ - case HID_HOST_W4_GET_REPORT_RESPONSE: - printf("HID_HOST_W4_GET_REPORT_RESPONSE \n"); - break; - case HID_HOST_W4_SET_REPORT_RESPONSE: - printf("HID_HOST_W4_SET_REPORT_RESPONSE \n"); - break; - case HID_HOST_W4_GET_PROTOCOL_RESPONSE: - printf("HID_HOST_W4_GET_PROTOCOL_RESPONSE \n"); - break; - case HID_HOST_W4_SEND_REPORT_RESPONSE: - printf("HID_HOST_W4_SEND_REPORT_RESPONSE\n"); - break; - case HID_HOST_W4_SET_PROTOCOL_RESPONSE: - printf("HID_HOST_W4_SET_PROTOCOL_RESPONSE \n"); - switch (message_status){ - case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL: - switch(hid_host->protocol_mode){ - case HID_PROTOCOL_MODE_BOOT:{ - 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 HID_PROTOCOL_MODE_REPORT: - break; - default: - break; - } - default: - break; - } - break; - default: - printf("HID_MESSAGE_TYPE_DATA ???\n"); - break; - } - hid_host->state = HID_HOST_CONNECTION_ESTABLISHED; - break; - - case HID_MESSAGE_TYPE_HID_CONTROL: - param = packet[0] & 0x0F; - - switch ((hid_control_param_t)param){ - case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG: - // hid_host_emit_event(device, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG); - hid_host->unplugged = true; - break; - default: - break; - } - break; - default: - break; - } default: break; } + } /* LISTING_END */ @@ -702,12 +183,12 @@ static void stdin_process(char cmd){ case 'R': printf("Set Protocol mode\n"); boot_mode = false; - status = hid_host_send_set_protocol_mode(_hid_host.cid, HID_PROTOCOL_MODE_REPORT); + 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); + status = hid_host_send_set_protocol_mode(hid_host_cid, HID_PROTOCOL_MODE_BOOT); break; case 'B': printf("Set Boot mode\n"); @@ -715,7 +196,7 @@ static void stdin_process(char cmd){ break; case 'c': - if (_hid_host.unplugged){ + if (unplugged){ printf("Cannot connect, host is unplugged.\n"); break; } @@ -728,81 +209,66 @@ static void stdin_process(char cmd){ } break; - case 'i': - if (_hid_host.unplugged){ - printf("Cannot reconnect, host is unplugged.\n"); - break; - } - printf("Reconnect, interrupt PSM.\n"); - status = l2cap_create_channel(packet_handler, remote_addr, BLUETOOTH_PSM_HID_INTERRUPT, 48, &_hid_host.interrupt_cid); - if (status){ - printf("Connecting to HID Control failed: 0x%02x\n", status); - } - break; - case 'I': - printf("Disconnect from interrupt channel %s...\n", bd_addr_to_str(remote_addr)); - hid_host_disconnect_interrupt_channel(_hid_host.cid); - break; case 'C': - printf("Disconnect from control channel %s...\n", bd_addr_to_str(remote_addr)); - hid_host_disconnect_control_channel(_hid_host.cid); + printf("Disconnect from %s...\n", bd_addr_to_str(remote_addr)); + hid_host_disconnect(hid_host_cid); break; case 's': printf("Send \'Suspend\'\n"); - hid_host_send_suspend(_hid_host.cid); + hid_host_send_suspend(hid_host_cid); break; case 'S': printf("Send \'Exit suspend\'\n"); - hid_host_send_exit_suspend(_hid_host.cid); + hid_host_send_exit_suspend(hid_host_cid); break; case 'u': printf("Send \'Unplug\'\n"); - _hid_host.unplugged = true; - hid_host_send_virtual_cable_unplug(_hid_host.cid); + unplugged = true; + hid_host_send_virtual_cable_unplug(hid_host_cid); break; case '1': printf("Get feature report with id 0x03 from %s\n", remote_addr_string); - status = hid_host_send_get_feature_report(_hid_host.cid, 0x03); + status = hid_host_send_get_feature_report(hid_host_cid, 0x03); break; case '2': printf("Get output report with id 0x05 from %s\n", remote_addr_string); - status = hid_host_send_get_output_report(_hid_host.cid, 0x05); + status = hid_host_send_get_output_report(hid_host_cid, 0x05); break; case '3': printf("Get input report from with id 0x02 %s\n", remote_addr_string); - status = hid_host_send_get_input_report(_hid_host.cid, 0x02); + status = hid_host_send_get_input_report(hid_host_cid, 0x02); break; case '4':{ uint8_t report[] = {0, 0}; printf("Set output report with id 0x03\n"); - status = hid_host_send_set_output_report(_hid_host.cid, 0x03, report, sizeof(report)); + status = hid_host_send_set_output_report(hid_host_cid, 0x03, report, sizeof(report)); break; } case '5':{ uint8_t report[] = {0, 0, 0}; printf("Set feature report with id 0x05\n"); - status = hid_host_send_set_feature_report(_hid_host.cid, 0x05, report, sizeof(report)); + status = hid_host_send_set_feature_report(hid_host_cid, 0x05, report, sizeof(report)); break; } case '6':{ uint8_t report[] = {0, 0, 0, 0}; printf("Set input report with id 0x02\n"); - status = hid_host_send_set_input_report(_hid_host.cid, 0x02, report, sizeof(report)); + status = hid_host_send_set_input_report(hid_host_cid, 0x02, report, sizeof(report)); break; } case '7':{ uint8_t report[] = {0,0,0, 0,0,0, 0,0}; printf("Set output report with id 0x01\n"); - status = hid_host_send_set_output_report(_hid_host.cid, 0x01, report, sizeof(report)); + status = hid_host_send_set_output_report(hid_host_cid, 0x01, report, sizeof(report)); break; } case 'p': printf("Get Protocol\n"); - status = hid_host_send_get_protocol(_hid_host.cid); + status = hid_host_send_get_protocol(hid_host_cid); break; case 'X': @@ -817,19 +283,19 @@ static void stdin_process(char cmd){ case '8':{ uint8_t report[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; printf("Send output report with id 0x03\n"); - status = hid_host_send_output_report(_hid_host.cid, 0x03, report, sizeof(report)); + status = hid_host_send_output_report(hid_host_cid, 0x03, report, sizeof(report)); break; } case '9':{ uint8_t report[] = {0, 0, 0, 0, 0, 0, 0, 0}; printf("Set output report with id 0x01\n"); - status = hid_host_send_set_output_report(_hid_host.cid, 0x01, report, sizeof(report)); + status = hid_host_send_set_output_report(hid_host_cid, 0x01, report, sizeof(report)); break; } case '0':{ uint8_t report[] = {0, 0, 0, 0, 0, 0, 0, 0}; printf("Send output report with id 0x01\n"); - status = hid_host_send_output_report(_hid_host.cid, 0x01, report, sizeof(report)); + status = hid_host_send_output_report(hid_host_cid, 0x01, report, sizeof(report)); break; } @@ -861,4 +327,3 @@ int btstack_main(int argc, const char * argv[]){ return 0; } -/* EXAMPLE_END */