From 6510739ba0ed43e77edb4d936da5754246111efa Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Tue, 25 Sep 2018 10:17:10 +0200 Subject: [PATCH] hid_device: fix create device on incoming connection, add suspend --- src/btstack_defines.h | 15 +++++++ src/btstack_event.h | 20 ++++++++++ src/classic/hid_device.c | 82 +++++++++++++++++++++++++++++++++----- src/classic/hid_device.h | 18 ++++++--- test/pts/hid_device_test.c | 42 ++++++++++++++----- 5 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/btstack_defines.h b/src/btstack_defines.h index e61d89a07..688f56e5a 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -2093,6 +2093,21 @@ typedef uint8_t sm_key_t[16]; */ #define HID_SUBEVENT_CAN_SEND_NOW 0x03 +/** + * @format 12 + * @param subevent_code + * @param con_handle +*/ +#define HID_SUBEVENT_SUSPEND 0x04 + +/** + * @format 12 + * @param subevent_code + * @param con_handle +*/ +#define HID_SUBEVENT_EXIT_SUSPEND 0x05 + + // HIDS Meta Event Group /** diff --git a/src/btstack_event.h b/src/btstack_event.h index afa7ac77e..4f628401b 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -6628,6 +6628,26 @@ static inline uint16_t hid_subevent_can_send_now_get_hid_cid(const uint8_t * eve return little_endian_read_16(event, 3); } +/** + * @brief Get field con_handle from event HID_SUBEVENT_SUSPEND + * @param event packet + * @return con_handle + * @note: btstack_type 2 + */ +static inline uint16_t hid_subevent_suspend_get_con_handle(const uint8_t * event){ + return little_endian_read_16(event, 3); +} + +/** + * @brief Get field con_handle from event HID_SUBEVENT_EXIT_SUSPEND + * @param event packet + * @return con_handle + * @note: btstack_type 2 + */ +static inline uint16_t hid_subevent_exit_suspend_get_con_handle(const uint8_t * event){ + return little_endian_read_16(event, 3); +} + /** * @brief Get field con_handle from event HIDS_SUBEVENT_CAN_SEND_NOW * @param event packet diff --git a/src/classic/hid_device.c b/src/classic/hid_device.c index fc17b628f..120c05947 100644 --- a/src/classic/hid_device.c +++ b/src/classic/hid_device.c @@ -103,8 +103,11 @@ static hid_device_t * hid_device_get_instance_for_cid(uint16_t cid){ return NULL; } -static hid_device_t * hid_device_create_instance_for_con_handle(uint16_t con_handle){ - UNUSED(con_handle); +static hid_device_t * hid_device_create_instance_for_bt_addr(bd_addr_t bd_addr){ + memcpy(_hid_device.bd_addr, bd_addr, 6); + if (!_hid_device.cid){ + _hid_device.cid = hid_device_get_next_cid(); + } return &_hid_device; } @@ -251,6 +254,10 @@ void hid_create_sdp_record( } de_pop_sequence(service, attribute); + uint8_t hid_remote_wake = 1; + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_REMOTE_WAKE); + de_add_number(service, DE_BOOL, DE_SIZE_8, hid_remote_wake); + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE); de_add_number(service, DE_BOOL, DE_SIZE_8, hid_boot_device); } @@ -264,7 +271,7 @@ static inline void hid_device_emit_connected_event(hid_device_t * context, uint8 little_endian_store_16(event,pos,context->cid); pos+=2; event[pos++] = status; - memcpy(&event[pos], context->bd_addr, 6); + reverse_bd_addr(context->bd_addr, &event[pos]); pos += 6; little_endian_store_16(event,pos,context->con_handle); pos += 2; @@ -300,6 +307,19 @@ static inline void hid_device_emit_can_send_now_event(hid_device_t * context){ hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos); } +static inline void hid_device_emit_event(hid_device_t * context, uint8_t subevent_type){ + uint8_t event[4]; + int pos = 0; + event[pos++] = HCI_EVENT_HID_META; + pos++; // skip len + event[pos++] = subevent_type; + little_endian_store_16(event,pos,context->cid); + pos+=2; + event[1] = pos - 2; + if (pos != sizeof(event)) log_error("hid_device_emit_event size %u", pos); + hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos); +} + static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){ UNUSED(channel); UNUSED(packet_size); @@ -309,6 +329,8 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack hid_device_t * device = NULL; uint8_t report[20]; int report_size; + uint8_t param; + bd_addr_t address; switch (packet_type){ case L2CAP_DATA_PACKET: @@ -326,9 +348,24 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack device->report_id = packet[1]; } device->state = HID_DEVICE_W2_SEND_REPORT; - printf(" answer get report type %d report_type\n", device->report_type); + // printf(" answer get report type %d report_type\n", device->report_type); l2cap_request_can_send_now_event(device->control_cid); break; + case HID_MESSAGE_TYPE_HID_CONTROL: + param = packet[0] & 0x0F; + switch (param){ + case HID_CONTROL_PARAM_SUSPEND: + hid_device_emit_event(device, HID_SUBEVENT_SUSPEND); + break; + case HID_CONTROL_PARAM_EXIT_SUSPEND: + hid_device_emit_event(device, HID_SUBEVENT_EXIT_SUSPEND); + break; + default: + device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST; + l2cap_request_can_send_now_event(device->control_cid); + break; + } + break; default: printf("L2CAP_DATA_PACKET %d \n", message_type); device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST; @@ -342,7 +379,8 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack switch (l2cap_event_incoming_connection_get_psm(packet)){ case PSM_HID_CONTROL: case PSM_HID_INTERRUPT: - device = hid_device_create_instance_for_con_handle(l2cap_event_incoming_connection_get_handle(packet)); + l2cap_event_incoming_connection_get_address(packet, address); + device = hid_device_create_instance_for_bt_addr(address); if (!device) { log_error("L2CAP_EVENT_INCOMING_CONNECTION, no hid device for con handle 0x%02x", l2cap_event_incoming_connection_get_handle(packet)); l2cap_decline_connection(channel); @@ -351,6 +389,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack if (device->con_handle == 0 || l2cap_event_incoming_connection_get_handle(packet) == device->con_handle){ device->con_handle = l2cap_event_incoming_connection_get_handle(packet); device->incoming = 1; + l2cap_event_incoming_connection_get_address(packet, device->bd_addr); l2cap_accept_connection(channel); } else { l2cap_decline_connection(channel); @@ -390,6 +429,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack default: break; } + // connect HID Interrupt for outgoing if (device->incoming == 0 && psm == PSM_HID_CONTROL){ log_info("Create outgoing HID Interrupt"); @@ -406,9 +446,8 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack device = hid_device_get_instance_for_cid(l2cap_event_channel_closed_get_local_cid(packet)); if (!device) return; + // connected_before = device->connected; device->incoming = 0; - device->connected = 0; - connected_before = device->connected; if (l2cap_event_channel_closed_get_local_cid(packet) == device->control_cid){ log_info("HID Control closed"); device->control_cid = 0; @@ -417,8 +456,10 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack log_info("HID Interrupt closed"); device->interrupt_cid = 0; } - if (connected_before && !device->connected){ + if (!device->interrupt_cid && !device->control_cid){ + device->connected = 0; device->con_handle = 0; + device->cid = 0; log_info("HID Disconnected"); hid_device_emit_connection_closed_event(device); } @@ -434,7 +475,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack break; case HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST: report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER; - hid_device_send_control_message(device->cid, &report[0], report_size); + hid_device_send_control_message(device->cid, &report[0], 1); break; default: log_info("HID Can send now, emit event"); @@ -522,19 +563,38 @@ uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid){ } // assign hic_cid *hid_cid = hid_device_get_next_cid(); - // store address memcpy(hid_device->bd_addr, addr, 6); // reset state + hid_device->cid = *hid_cid; hid_device->incoming = 0; hid_device->connected = 0; hid_device->control_cid = 0; hid_device->interrupt_cid = 0; - // create l2cap control using fixed HID L2CAP PSM log_info("Create outgoing HID Control"); uint8_t status = l2cap_create_channel(packet_handler, hid_device->bd_addr, PSM_HID_CONTROL, 48, &hid_device->control_cid); return status; } + +/* + * @brief Disconnect from HID Host + * @param hid_cid + * @result status + */ +void hid_device_disconnect(uint16_t hid_cid){ + hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid); + if (!hid_device){ + log_error("hid_device_disconnect: could not find hid device instace"); + return; + } + log_info("Disconnect from HID Host"); + if (hid_device->control_cid){ + l2cap_disconnect(hid_device->control_cid, 0); // reason isn't used + } + if (hid_device->interrupt_cid){ + l2cap_disconnect(hid_device->interrupt_cid, 0); // reason isn't used + } +} diff --git a/src/classic/hid_device.h b/src/classic/hid_device.h index 78389fdaa..5cab67710 100644 --- a/src/classic/hid_device.h +++ b/src/classic/hid_device.h @@ -73,12 +73,12 @@ typedef enum { } hid_handshake_param_type_t; typedef enum { - HID_CONTROL_NOP_DEPRECATED = 0, // Deprecated: No Operation. - HID_CONTROL_HARD_RESET_DEPRECATED, // Deprecated: Device performs Power On System Test (POST) then initializes all internal variables and initiates normal operations. - HID_CONTROL_SOFT_RESET_DEPRECATED, // Deprecated: Device initializes all internal variables and initiates normal operations. - HID_CONTROL_SUSPEND = 0x03, // Go to reduced power mode. - HID_CONTROL_EXIT_SUSPEND, // Exit reduced power mode. - HID_CONTROL_VIRTUAL_CABLE_UNPLUG + HID_CONTROL_PARAM_NOP_DEPRECATED = 0, // Deprecated: No Operation. + HID_CONTROL_PARAM_HARD_RESET_DEPRECATED, // Deprecated: Device performs Power On System Test (POST) then initializes all internal variables and initiates normal operations. + HID_CONTROL_PARAM_SOFT_RESET_DEPRECATED, // Deprecated: Device initializes all internal variables and initiates normal operations. + HID_CONTROL_PARAM_SUSPEND = 0x03, // Go to reduced power mode. + HID_CONTROL_PARAM_EXIT_SUSPEND, // Exit reduced power mode. + HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG } hid_control_param_t; typedef enum { @@ -142,6 +142,12 @@ void hid_device_register_report_request_callback(void (*callback) (uint16_t hid_ */ uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid); +/* + * @brief Disconnect from HID Host + * @param hid_cid + */ +void hid_device_disconnect(uint16_t hid_cid); + /** * @brief Request can send now event to send HID Report * Generates an HID_SUBEVENT_CAN_SEND_NOW subevent diff --git a/test/pts/hid_device_test.c b/test/pts/hid_device_test.c index 9286bf97d..897c7e6df 100644 --- a/test/pts/hid_device_test.c +++ b/test/pts/hid_device_test.c @@ -237,8 +237,8 @@ static void hid_report_callback(uint16_t cid, hid_report_type_t report_type, uin UNUSED(report_id); UNUSED(report_max_size); printf("hid_report_callback \n"); - int modifier = 0x90; int keycode = 0; - uint8_t leds = 0x64; + // int modifier = 0x90; int keycode = 0; + // uint8_t leds = 0x64; uint8_t header = (HID_MESSAGE_TYPE_DATA << 4) | report_type; // uint8_t report[] = { header, 0x01, modifier, 0, keycode, 0, 0, 0, 0, 0}; @@ -265,37 +265,47 @@ static void hid_report_callback(uint16_t cid, hid_report_type_t report_type, uin #ifdef HAVE_BTSTACK_STDIN // On systems with STDIN, we can directly type on the console +#if 0 static void show_usage(void){ bd_addr_t iut_address; gap_local_bd_addr(iut_address); printf("\n--- Bluetooth HID Host Test Console %s ---\n", bd_addr_to_str(iut_address)); + printf("c - connect \n"); printf("l - set limited discoverable mode\n"); printf("L - restset limited discoverable mode\n"); printf("Ctrl-c - exit\n"); printf("---\n"); } - +#endif static void stdin_process(char character){ uint8_t modifier; uint8_t keycode; int found; switch (character){ + case 'C': + printf("Disconnect from %s...\n", bd_addr_to_str(device_addr)); + hid_device_disconnect(hid_cid); + break; + case 'c': + printf("Connecting to %s...\n", bd_addr_to_str(device_addr)); + hid_device_connect(device_addr, &hid_cid); + return; case 'l': printf("set limited discoverable mode\n"); gap_discoverable_control(1); - gap_set_class_of_device(0x2540); + // TODO move into HCI init + hci_send_cmd(&hci_write_current_iac_lap_two_iacs, 2, GAP_IAC_GENERAL_INQUIRY, GAP_IAC_LIMITED_INQUIRY); return; case 'L': gap_discoverable_control(0); - gap_set_class_of_device(0x0540); printf("reset limited discoverable mode\n"); return; case '\n': case '\r': break; default: - show_usage(); + // show_usage(); break; } @@ -393,9 +403,10 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack return; } app_state = APP_CONNECTED; + hid_subevent_connection_opened_get_bd_addr(packet, device_addr); hid_cid = hid_subevent_connection_opened_get_hid_cid(packet); #ifdef HAVE_BTSTACK_STDIN - printf("HID Connected, please start typing...\n"); + printf("HID Connected, please start typing... %s\n", bd_addr_to_str(device_addr)); #else printf("HID Connected, sending demo text...\n"); hid_embedded_start_typing(); @@ -406,7 +417,14 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack app_state = APP_NOT_CONNECTED; hid_cid = 0; break; - + + case HID_SUBEVENT_SUSPEND: + printf("HID Suspend\n"); + break; + case HID_SUBEVENT_EXIT_SUSPEND: + printf("HID Exit Suspend\n"); + break; + case HID_SUBEVENT_CAN_SEND_NOW: if (send_keycode){ send_report(send_modifier, send_keycode); @@ -456,8 +474,14 @@ int btstack_main(int argc, const char * argv[]){ // SDP Server sdp_init(); memset(hid_service_buffer, 0, sizeof(hid_service_buffer)); + + uint8_t hid_reconnect_initiate = 1; + uint8_t hid_virtual_cable = 1; // hid sevice subclass 2540 Keyboard, hid counntry code 33 US, hid virtual cable off, hid reconnect initiate off, hid boot device off - hid_create_sdp_record(hid_service_buffer, 0x10001, 0x2540, 33, 0, 0, 0, hid_descriptor_keyboard_boot_mode, sizeof(hid_descriptor_keyboard_boot_mode), hid_device_name); + hid_create_sdp_record(hid_service_buffer, 0x10001, 0x2540, 33, + hid_virtual_cable, hid_reconnect_initiate, 0, + hid_descriptor_keyboard_boot_mode, sizeof(hid_descriptor_keyboard_boot_mode), hid_device_name); + printf("HID service record size: %u\n", de_get_len( hid_service_buffer)); sdp_register_service(hid_service_buffer);