test/pts/hid_host: handle get/set report/protocol

This commit is contained in:
Milanka Ringwald 2020-12-08 12:01:17 +01:00 committed by Matthias Ringwald
parent ffe3f1a125
commit 17ec83426d
2 changed files with 525 additions and 54 deletions

View File

@ -73,7 +73,8 @@ typedef enum {
HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID, // Invalid report ID transmitted.
HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST, // The device does not support the request. This result code shall be used if the HIDP message type is unsupported.
HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER, // A parameter value is out of range or inappropriate for the request.
HID_HANDSHAKE_PARAM_TYPE_ERR_UNKNOWN = 0x0E // Device could not identify the error condition. 0xF = ERR_FATAL. Restart is essential to resume functionality.
HID_HANDSHAKE_PARAM_TYPE_ERR_UNKNOWN = 0x0E, // Device could not identify the error condition.
HID_HANDSHAKE_PARAM_TYPE_ERR_FATAL = 0x0F // Restart is essential to resume functionality
} hid_handshake_param_type_t;
typedef enum {

View File

@ -35,13 +35,13 @@
*
*/
#define __BTSTACK_FILE__ "hid_host_demo.c"
#define __BTSTACK_FILE__ "hid_host_tast.c"
/*
* hid_host_demo.c
* hid_host_tast.c
*/
/* EXAMPLE_START(hid_host_demo): HID Host Demo
/* 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
@ -65,27 +65,29 @@ static uint16_t hid_interrupt_psm;
static uint8_t attribute_value[MAX_ATTRIBUTE_VALUE_SIZE];
static const unsigned int attribute_value_buffer_size = MAX_ATTRIBUTE_VALUE_SIZE;
// L2CAP
static uint16_t l2cap_hid_control_cid;
static uint16_t l2cap_hid_interrupt_cid;
// MBP 2016
static const char * remote_addr_string = "00-1F-20-86-DF-52";
// iMpulse static const char * remote_addr_string = "64:6E:6C:C1:AA:B5";
// 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;
// Needed for queries
typedef enum {
typedef enum {
HID_HOST_IDLE,
HID_HOST_CONTROL_CONNECTION_ESTABLISHED,
HID_HOST_W2_REQUEST_OUTPUT_REPORT
HID_HOST_W2_SEND_GET_REPORT,
HID_HOST_W4_GET_REPORT_RESPONSE,
HID_HOST_W2_SEND_SET_REPORT,
HID_HOST_W4_SET_REPORT_RESPONSE,
HID_HOST_W2_SEND_GET_PROTOCOL,
HID_HOST_W4_GET_PROTOCOL_RESPONSE,
HID_HOST_W2_SEND_SET_PROTOCOL,
HID_HOST_W4_SET_PROTOCOL_RESPONSE,
} hid_host_state_t;
static hid_host_state_t hid_host_state = HID_HOST_IDLE;
// Simplified US Keyboard with Shift modifier
#define CHAR_ILLEGAL 0xff
@ -136,6 +138,216 @@ static const uint8_t keytable_us_shift[] = {
};
// 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 uint16_t hid_host_cid = 0;
static uint16_t hid_host_get_next_cid(void){
hid_host_cid++;
if (!hid_host_cid){
hid_host_cid = 1;
}
return hid_host_cid;
}
static hid_host_t * hid_host_get_instance_for_hid_cid(uint16_t hid_cid){
if (_hid_host.cid == hid_cid){
return &_hid_host;
}
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_CONTROL_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_CONTROL_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_CONTROL_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(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 void hid_host_connect(bd_addr_t addr, uint16_t * hid_cid){
// }
static hid_host_t * hid_host_provide_instance_for_bd_addr(bd_addr_t bd_addr){
if (!_hid_host.cid){
(void)memcpy(_hid_host.bd_addr, bd_addr, 6);
_hid_host.cid = hid_host_get_next_cid();
// _hid_host.protocol_mode = HID_PROTOCOL_MODE_REPORT;
_hid_host.con_handle = HCI_CON_HANDLE_INVALID;
}
return &_hid_host;
}
static hid_host_t * hid_host_get_instance_for_l2cap_cid(uint16_t cid){
if ((_hid_host.control_cid == cid) || (_hid_host.interrupt_cid == cid)){
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
@ -149,11 +361,20 @@ static void hid_host_setup(void){
// Initialize L2CAP
l2cap_init();
// register L2CAP Services for reconnections
l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level());
l2cap_register_service(packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level());
// 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);
// try to become master on incoming connections
hci_set_master_slave_policy(HCI_ROLE_MASTER);
// register for HCI events
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// Disable stdout buffering
setbuf(stdout, NULL);
}
@ -264,7 +485,8 @@ static void handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel
break;
}
printf("Setup HID\n");
status = l2cap_create_channel(packet_handler, remote_addr, hid_control_psm, 48, &l2cap_hid_control_cid);
(void)memcpy(_hid_host.bd_addr, remote_addr, 6);
status = l2cap_create_channel(packet_handler, remote_addr, hid_control_psm, 48, &_hid_host.control_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
}
@ -354,9 +576,13 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
{
/* LISTING_PAUSE */
uint8_t event;
bd_addr_t event_addr;
bd_addr_t address;
uint8_t status;
uint16_t l2cap_cid;
hid_host_t * hid_host;
uint8_t param;
hid_message_type_t message_type;
hid_handshake_param_type_t message_status;
/* LISTING_RESUME */
switch (packet_type) {
@ -368,8 +594,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
*/
case BTSTACK_EVENT_STATE:
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
printf("Start SDP HID query for remote HID Device.\n");
sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
printf("BTstack up and running. \n");
}
break;
@ -377,8 +602,8 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
case HCI_EVENT_PIN_CODE_REQUEST:
// inform about pin code request
printf("Pin code request - using '0000'\n");
hci_event_pin_code_request_get_bd_addr(packet, event_addr);
gap_pin_code_response(event_addr, "0000");
hci_event_pin_code_request_get_bd_addr(packet, address);
gap_pin_code_response(address, "0000");
break;
case HCI_EVENT_USER_CONFIRMATION_REQUEST:
@ -388,36 +613,148 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
break;
/* LISTING_RESUME */
case L2CAP_EVENT_INCOMING_CONNECTION:
printf("L2CAP_EVENT_INCOMING_CONNECTION \n");
l2cap_event_incoming_connection_get_address(packet, address);
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
case PSM_HID_INTERRUPT:
hid_host = hid_host_provide_instance_for_bd_addr(address);
if (!hid_host) {
log_error("L2CAP_EVENT_INCOMING_CONNECTION, cannot create instance for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
if (hid_host->unplugged) {
log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s, host is unplugged", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
if ((hid_host->con_handle == HCI_CON_HANDLE_INVALID) || (l2cap_event_incoming_connection_get_handle(packet) == hid_host->con_handle)){
hid_host->con_handle = l2cap_event_incoming_connection_get_handle(packet);
// hid_host->incoming = 1;
l2cap_event_incoming_connection_get_address(packet, hid_host->bd_addr);
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
hid_host->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
break;
case PSM_HID_INTERRUPT:
hid_host->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
break;
default:
break;
}
printf("L2CAP_EVENT_INCOMING_CONNECTION l2cap_accept_connection\n");
l2cap_accept_connection(channel);
} else {
l2cap_decline_connection(channel);
log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
}
break;
default:
log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
l2cap_decline_connection(channel);
break;
}
break;
case L2CAP_EVENT_CHANNEL_OPENED:
status = packet[2];
status = l2cap_event_channel_opened_get_status(packet);
if (status){
printf("L2CAP Connection failed: 0x%02x\n", status);
break;
}
l2cap_cid = little_endian_read_16(packet, 13);
l2cap_cid = l2cap_event_channel_opened_get_local_cid(packet);
if (!l2cap_cid) break;
if (l2cap_cid == l2cap_hid_control_cid){
status = l2cap_create_channel(packet_handler, remote_addr, hid_interrupt_psm, 48, &l2cap_hid_interrupt_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
switch (l2cap_event_channel_opened_get_psm(packet)){
case PSM_HID_CONTROL:
if (l2cap_cid == _hid_host.control_cid){
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);
break;
}
_hid_host.state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
}
break;
}
hid_host_state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
}
if (l2cap_cid == l2cap_hid_interrupt_cid){
printf("HID Connection established\n");
case PSM_HID_INTERRUPT:
if (l2cap_cid == _hid_host.interrupt_cid){
printf("HID Connection established\n");
}
break;
default:
break;
}
// disconnect?
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
l2cap_cid = l2cap_event_channel_closed_get_local_cid(packet);
hid_host = hid_host_get_instance_for_l2cap_cid(l2cap_cid);
if (!hid_host) return;
if (l2cap_cid == hid_host->interrupt_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:
switch(hid_host_state){
case HID_HOST_W2_REQUEST_OUTPUT_REPORT:{
uint8_t header = (HID_MESSAGE_TYPE_GET_REPORT << 4) | HID_REPORT_TYPE_OUTPUT;
uint8_t report_id = 0x01;
uint8_t report[] = { header, report_id };
l2cap_send(l2cap_hid_control_cid, (uint8_t*) report, sizeof(report));
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);
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;
}
default:
break;
}
@ -426,15 +763,61 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
}
break;
case L2CAP_DATA_PACKET:
// for now, just dump incoming data
if (channel == l2cap_hid_interrupt_cid){
hid_host_handle_interrupt_report(packet, size);
} else if (channel == l2cap_hid_control_cid){
printf("\nHID Control: ");
if (channel == _hid_host.interrupt_cid){
printf("HID Interrupt data: \n");
printf_hexdump(packet, size);
} else {
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_SET_PROTOCOL_RESPONSE:
printf("HID_HOST_W4_GET_PROTOCOL_RESPONSE \n");
break;
default:
printf("HID_MESSAGE_TYPE_DATA ???\n");
break;
}
hid_host->state = HID_HOST_CONTROL_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;
}
@ -442,18 +825,11 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
/* LISTING_END */
static uint8_t hid_host_get_output_report(uint16_t control_cid){
if (hid_host_state != HID_HOST_CONTROL_CONNECTION_ESTABLISHED) return 0;
hid_host_state = HID_HOST_W2_REQUEST_OUTPUT_REPORT;
l2cap_request_can_send_now_event(control_cid);
return ERROR_CODE_SUCCESS;
}
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("o - get output report\n");
printf("Ctrl-c - exit\n");
printf("---\n");
@ -462,10 +838,104 @@ static void show_usage(void){
static void stdin_process(char cmd){
uint8_t status = ERROR_CODE_SUCCESS;
switch (cmd){
case 'o':
printf("Get output report from %s\n", remote_addr_string);
status = hid_host_get_output_report(l2cap_hid_control_cid);
case 'a':
printf("Start SDP HID query for remote HID Device.\n");
sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
break;
case 'c':
if (_hid_host.unplugged){
printf("Cannot reconnect, host is unplugged.\n");
break;
}
printf("Reconnect, control PSM.\n");
status = l2cap_create_channel(packet_handler, remote_addr, BLUETOOTH_PSM_HID_CONTROL, 48, &_hid_host.control_cid);
if (status){
printf("Connecting to HID Control failed: 0x%02x\n", status);
}
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);
break;
case 's':
printf("Send \'Suspend\'\n");
hid_host_send_suspend(_hid_host.cid);
break;
case 'S':
printf("Send \'Exit suspend\'\n");
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);
break;
case '1':
printf("Get feature report with id 0x05 from %s\n", remote_addr_string);
status = hid_host_send_get_feature_report(_hid_host.cid, 0x05);
break;
case '2':
printf("Get output report with id 0x03 from %s\n", remote_addr_string);
status = hid_host_send_get_output_report(_hid_host.cid, 0x03);
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);
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));
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));
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));
break;
}
case 'p':
printf("Get Protocol\n");
status = hid_host_send_get_protocol(_hid_host.cid);
break;
case 'R':
printf("Set Report mode\n");
status = hid_host_send_set_protocol(_hid_host.cid, HID_PROTOCOL_MODE_REPORT);
break;
case 'B':
printf("Set Boot mode\n");
status = hid_host_send_set_protocol(_hid_host.cid, HID_PROTOCOL_MODE_BOOT);
break;
case '\n':
case '\r':
break;