diff --git a/src/ble/gatt-service/hids_client.c b/src/ble/gatt-service/hids_client.c index eda5f8383..3ba134db9 100644 --- a/src/ble/gatt-service/hids_client.c +++ b/src/ble/gatt-service/hids_client.c @@ -279,35 +279,6 @@ static uint8_t find_report_index_for_report_id(hids_client_t * client, uint8_t r return HIDS_CLIENT_INVALID_REPORT_INDEX; } - -static uint8_t find_report_index_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){ - uint8_t i; - switch (client->protocol_mode){ - case HID_PROTOCOL_MODE_BOOT: - for (i = 0; i < client->num_reports; i++){ - if (!client->reports[i].boot_report){ - continue; - } - if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){ - return i; - } - } - break; - - default: - for (i = 0; i < client->num_reports; i++){ - if (client->reports[i].boot_report){ - continue; - } - if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){ - return i; - } - } - break; - } - return HIDS_CLIENT_INVALID_REPORT_INDEX; -} - static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){ uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle); @@ -812,17 +783,19 @@ static void hids_run_for_client(hids_client_t * client){ break; - case HIDS_CLIENT_W2_SEND_REPORT: + case HIDS_CLIENT_W2_SEND_WRITE_REPORT: #ifdef ENABLE_TESTING_SUPPORT - printf(" Send report [%d, %d, 0x%04X]:\n", + printf(" Write report [%d, %d, 0x%04X]:\n", client->report_index, client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle); #endif - client->state = HIDS_CLIENT_STATE_CONNECTED; - - att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, + client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE; + + // see GATT_EVENT_QUERY_COMPLETE for end of write + att_status = gatt_client_write_value_of_characteristic( + &handle_report_event, client->con_handle, client->reports[client->report_index].value_handle, client->report_len, (uint8_t *)client->report); UNUSED(att_status); @@ -866,6 +839,17 @@ static void hids_run_for_client(hids_client_t * client){ client->con_handle, client->handle); break; + + case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE: +#ifdef ENABLE_TESTING_SUPPORT + printf(" Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle); +#endif + client->state = HIDS_CLIENT_STATE_CONNECTED; + + att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value); + UNUSED(att_status); + break; + default: break; } @@ -1314,9 +1298,11 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint #endif case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT: + case HIDS_CLIENT_W4_WRITE_REPORT_DONE: client->state = HIDS_CLIENT_STATE_CONNECTED; break; + default: break; } @@ -1367,7 +1353,7 @@ uint8_t hids_client_disconnect(uint16_t hids_cid){ return ERROR_CODE_SUCCESS; } -uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){ +uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){ hids_client_t * client = hids_get_client_for_cid(hids_cid); if (client == NULL){ return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; @@ -1377,7 +1363,7 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint return ERROR_CODE_COMMAND_DISALLOWED; } - uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT); + uint8_t report_index = find_report_index_for_report_id(client, report_id); if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){ return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; @@ -1394,7 +1380,7 @@ uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; } - client->state = HIDS_CLIENT_W2_SEND_REPORT; + client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT; client->report_index = report_index; client->report = report; client->report_len = report_len; @@ -1474,6 +1460,62 @@ uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){ return ERROR_CODE_SUCCESS; } +uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, hid_protocol_mode_t protocol_mode, uint8_t service_index){ + hids_client_t * client = hids_get_client_for_cid(hids_cid); + if (client == NULL){ + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + + if (client->state != HIDS_CLIENT_STATE_CONNECTED) { + return ERROR_CODE_COMMAND_DISALLOWED; + } + + if (service_index >= client->num_instances){ + return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + client->service_index = service_index; + client->handle = client->services[client->service_index].protocol_mode_value_handle; + client->value = (uint8_t)protocol_mode; + + client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE; + hids_run_for_client(client); + return ERROR_CODE_SUCCESS; +} + + +static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){ + hids_client_t * client = hids_get_client_for_cid(hids_cid); + if (client == NULL){ + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + + if (client->state != HIDS_CLIENT_STATE_CONNECTED) { + return ERROR_CODE_COMMAND_DISALLOWED; + } + + if (service_index >= client->num_instances){ + return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + client->service_index = service_index; + client->handle = client->services[client->service_index].control_point_value_handle; + client->value = value; + + client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE; + hids_run_for_client(client); + return ERROR_CODE_SUCCESS; +} + +uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){ + return hids_client_send_control_point_cmd(hids_cid, service_index, 0); +} + +uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){ + return hids_client_send_control_point_cmd(hids_cid, service_index, 1); +} + + void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){ hids_client_descriptor_storage = hid_descriptor_storage; hids_client_descriptor_storage_len = hid_descriptor_storage_len; diff --git a/src/ble/gatt-service/hids_client.h b/src/ble/gatt-service/hids_client.h index 4206ae6e7..4b19ad3af 100644 --- a/src/ble/gatt-service/hids_client.h +++ b/src/ble/gatt-service/hids_client.h @@ -97,14 +97,18 @@ typedef enum { HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED, HIDS_CLIENT_STATE_CONNECTED, - HIDS_CLIENT_W2_SEND_REPORT, + HIDS_CLIENT_W2_SEND_WRITE_REPORT, + HIDS_CLIENT_W4_WRITE_REPORT_DONE, + HIDS_CLIENT_W2_SEND_GET_REPORT, HIDS_CLIENT_W4_GET_REPORT_RESULT, HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC, HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT, + HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE, + #ifdef ENABLE_TESTING_SUPPORT HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION, HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT, @@ -183,10 +187,11 @@ typedef struct { // index used for report and report map search uint8_t report_index; - uint16_t handle; uint16_t report_len; const uint8_t * report; - + // used to write control_point and protocol_mode + uint16_t handle; + uint8_t value; } hids_client_t; /* API_START */ @@ -216,7 +221,7 @@ uint8_t hids_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_ * @param report_len * @result status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, ERROR_CODE_COMMAND_DISALLOWED */ -uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len); +uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len); uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id); @@ -224,6 +229,12 @@ uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index); +uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, hid_protocol_mode_t protocol_mode, uint8_t service_index); + +uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index); + +uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index); + /** * @brief Disconnect from Battery Service. * @param hids_cid diff --git a/test/pts/hog_host.md b/test/pts/hog_host.md index c41cf6548..618ff977c 100644 --- a/test/pts/hog_host.md +++ b/test/pts/hog_host.md @@ -20,15 +20,25 @@ HOGP/RH/HGDC/BV-16-I: d HOGP/RH/HGRF/BV-01-I: h HOGP/RH/HGRF/BV-02-I: h -HOGP/RH/HGRF/BV-03-I: h, 1, 4, (confirmation) -HOGP/RH/HGRF/BV-04-I: h, 1, 4, (confirmation) +HOGP/RH/HGRF/BV-03-I: h, r, 1, 4, (confirmation) +HOGP/RH/HGRF/BV-04-I: h, r, 1, 4, (confirmation) HOGP/RH/HGRF/BV-05-I: h, (confirmation) -HOGP/RH/HGRF/BV-19-I: h, 2, 5, (confirmation) +HOGP/RH/HGRF/BV-19-I: h, r, 2, 5, (confirmation) HOGP/RH/HGRF/BV-06-I: h, (confirmation) -HOGP/RH/HGRF/BV-07-I: h, 3, 6 +HOGP/RH/HGRF/BV-07-I: h, r, 3, 6 HOGP/RH/HGRF/BV-08-I: h, (confirmation) -HOGP/RH/HGRF/BV-09-I: h, i, j, (confirmation) -HOGP/RH/HGRF/BV-10-I: b, k +HOGP/RH/HGRF/BV-09-I: h, r, i, I, (confirmation) +HOGP/RH/HGRF/BV-10-I: b, r, k HOGP/RH/HGRF/BV-11-I: b HOGP/RH/HGRF/BV-12-I: d -HOGP/RH/HGRF/BV-18-I: h \ No newline at end of file +HOGP/RH/HGRF/BV-18-I: h + +HOGP/RH/HGWF/BV-01-I: h, w, 1, 4 +HOGP/RH/HGWF/BV-02-I: h, w, 2, 5 +HOGP/RH/HGWF/BV-04-I: h, w, 3, 6 +HOGP/RH/HGWF/BV-05-I: h, w, s, S +HOGP/RH/HGWF/BV-06-I: h, w, e, E +HOGP/RH/HGWF/BV-07-I: h, w, p, P + + + diff --git a/test/pts/hog_host_test.c b/test/pts/hog_host_test.c index 02f1e59e1..c91cda205 100644 --- a/test/pts/hog_host_test.c +++ b/test/pts/hog_host_test.c @@ -63,7 +63,9 @@ static enum { W4_CONNECTED, W4_ENCRYPTED, W4_CLIENT_CONNECTED, - READY + READY, + READ_MODE, + WRITE_MODE } app_state; // PTS: @@ -74,8 +76,6 @@ static hci_con_handle_t connection_handle; static uint16_t hids_cid; static uint16_t battery_service_cid; -static uint8_t query_service_index; - static bool connect_hids_client = false; static bool connect_battery_client = false; static bool connect_device_information_client = false; @@ -341,7 +341,7 @@ static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa printf("Pairing failed, timeout\n"); break; case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: - printf("Pairing faileed, disconnected\n"); + printf("Pairing failed, disconnected\n"); break; case ERROR_CODE_AUTHENTICATION_FAILURE: printf("Pairing failed, reason = %u\n", sm_event_pairing_complete_get_reason(packet)); @@ -521,23 +521,56 @@ static void show_usage(void){ bd_addr_t iut_address; gap_local_bd_addr(iut_address); printf("\n--- Bluetooth HOG Host Test Console %s ---\n", bd_addr_to_str(iut_address)); - printf("c - Connect to remote device\n"); - printf("C - Disconnect from remote device\n"); printf("h - Connect to HID Service Client\n"); printf("b - Connect to Battery Service\n"); printf("d - Connect to Device Information Service\n"); printf("\n"); - printf("p - Get protocol mode for service 0\n"); - printf("q - Get protocol mode for service 1\n"); - printf("i - Get HID information for service index 0\n"); - printf("j - Get HID information for service index 1\n"); - printf("k - Get Battery Level for service index 0\n"); - printf("1 - Get report with ID 1\n"); - printf("2 - Get report with ID 2\n"); - printf("3 - Get report with ID 3\n"); - printf("4 - Get report with ID 4\n"); - printf("5 - Get report with ID 5\n"); - printf("6 - Get report with ID 6\n"); + + printf("r - switch to READ mode\n"); + printf("w - switch to WRITE mode\n"); + printf("\n"); + + // printf("C - Disconnect from remote device\n"); + + switch (app_state){ + case READ_MODE: + printf("READ mode\n\n"); + printf("1 - Get report with ID 1\n"); + printf("2 - Get report with ID 2\n"); + printf("3 - Get report with ID 3\n"); + printf("4 - Get report with ID 4\n"); + printf("5 - Get report with ID 5\n"); + printf("6 - Get report with ID 6\n"); + printf("\n"); + printf("p - Get protocol mode for service 0\n"); + printf("P - Get protocol mode for service 1\n"); + printf("i - Get HID information for service index 0\n"); + printf("I - Get HID information for service index 1\n"); + printf("k - Get Battery Level for service index 0\n"); + break; + case WRITE_MODE: + printf("Write mode\n\n"); + printf("1 - Write report with ID 1\n"); + printf("2 - Write report with ID 2\n"); + printf("3 - Write report with ID 3\n"); + printf("4 - Write report with ID 4\n"); + printf("5 - Write report with ID 5\n"); + printf("6 - Write report with ID 6\n"); + printf("\n"); + printf("p - Set Report protocol mode for service 0\n"); + printf("P - Set Report protocol mode for service 1\n"); + printf("s - Suspend service 0\n"); + printf("S - Suspend service 1\n"); + printf("e - Exit suspend service 0\n"); + printf("E - Exit suspend service 1\n"); + break; + + default: + printf("c - Connect to remote device\n"); + printf("\n"); + break; + } + printf("\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -565,23 +598,22 @@ static void hog_host_connect_device_information_client(void){ } static void stdin_process(char character){ - switch (character){ case 'c': app_state = W4_CONNECTED; printf("Connect to remote device\n"); gap_connect(remote_device.addr, remote_device.addr_type); - break; + return; case 'C': printf("Disconnect from remote device\n"); gap_disconnect(connection_handle); - break; + return; case 'h': printf("Connect to HID Service Client\n"); hog_host_connect_hids_client(); - break; + return; case 'b': printf("Connect to Battery Service\n"); @@ -591,77 +623,171 @@ static void stdin_process(char character){ case 'd': printf("Connect to Device Information Service\n"); hog_host_connect_device_information_client(); - break; + return; - case 'p': - printf("Get protocol mode for service 0\n"); - hids_client_get_protocol_mode(hids_cid, 0); - break; - case 'q': - printf("Get protocol mode for service 1\n"); - hids_client_get_protocol_mode(hids_cid, 1); - break; + case 'r': + printf("Switch to READ mode\n"); + app_state = READ_MODE; + return; - case 'i': - query_service_index = 0; - printf("Get HID information for service index %d\n", query_service_index); - hids_client_get_hid_information(hids_cid, query_service_index); - break; - - case 'j': - query_service_index = 1; - printf("Get HID information for service index %d\n", query_service_index); - hids_client_get_hid_information(hids_cid, query_service_index); - break; - - case 'k': - printf("Read Battery Level for service index 0\n"); - battery_service_client_read_battery_level(battery_service_cid, 0); - break; - - case '1': - printf("Get report with ID 1\n"); - hids_client_send_get_report(hids_cid, 1); - break; - case '2': - printf("Get report with ID 2\n"); - hids_client_send_get_report(hids_cid, 2); - break; - - case '3': - printf("Get report with ID 3\n"); - hids_client_send_get_report(hids_cid, 3); - break; - - case '4': - printf("Get report with ID 4\n"); - hids_client_send_get_report(hids_cid, 4); - break; - - case '5': - printf("Get report with ID 5\n"); - hids_client_send_get_report(hids_cid, 5); - break; - - case '6': - printf("Get report with ID 6\n"); - hids_client_send_get_report(hids_cid, 6); - break; - - case 'r':{ - uint8_t report[] = {0, 0, 0, 0, 0, 0, 0, 0}; - printf("Send output report with id 0x01\n"); - hids_client_send_report(hids_cid, 0x01, report, sizeof(report)); - break; - } + case 'w': + printf("Switch to WRITE mode\n"); + app_state = WRITE_MODE; + return; case '\n': case '\r': + return; + default: + break; + } + + switch (app_state){ + case READ_MODE: + switch (character){ + case 'p': + printf("Get protocol mode for service 0\n"); + hids_client_get_protocol_mode(hids_cid, 0); + break; + case 'P': + printf("Get protocol mode for service 1\n"); + hids_client_get_protocol_mode(hids_cid, 1); + break; + + case 'i': + printf("Get HID information for service index 0\n"); + hids_client_get_hid_information(hids_cid, 0); + break; + + case 'I': + printf("Get HID information for service index 1\n"); + hids_client_get_hid_information(hids_cid, 1); + break; + + case 'k': + printf("Read Battery Level for service index 0\n"); + battery_service_client_read_battery_level(battery_service_cid, 0); + break; + + case '1': + printf("Get report with ID 1\n"); + hids_client_send_get_report(hids_cid, 1); + break; + case '2': + printf("Get report with ID 2\n"); + hids_client_send_get_report(hids_cid, 2); + break; + + case '3': + printf("Get report with ID 3\n"); + hids_client_send_get_report(hids_cid, 3); + break; + + case '4': + printf("Get report with ID 4\n"); + hids_client_send_get_report(hids_cid, 4); + break; + + case '5': + printf("Get report with ID 5\n"); + hids_client_send_get_report(hids_cid, 5); + break; + + case '6': + printf("Get report with ID 6\n"); + hids_client_send_get_report(hids_cid, 6); + break; + case '\n': + case '\r': + break; + default: + show_usage(); + break; + } + break; + case WRITE_MODE: + switch (character){ + case '1':{ + uint8_t report[] = {0xAA, 0xB3, 0xF8, 0xA6, 0xCD}; + printf("Write report with id 0x01\n"); + hids_client_send_write_report(hids_cid, 0x01, report, sizeof(report)); + break; + } + case '2':{ + uint8_t report[] = {0xEF, 0x90, 0x78, 0x56, 0x34, 0x12, 0x00}; + printf("Write report with id 0x02\n"); + hids_client_send_write_report(hids_cid, 0x02, report, sizeof(report)); + break; + } + case '3':{ + uint8_t report[] = {0xEA, 0x45, 0x3F, 0x2D, 0x87}; + printf("Write report with id 0x03\n"); + hids_client_send_write_report(hids_cid, 0x03, report, sizeof(report)); + break; + } + case '4':{ + uint8_t report[] = {0xAA, 0xB3, 0xF8, 0xA6, 0xCD}; + printf("Write report with id 0x04\n"); + hids_client_send_write_report(hids_cid, 0x04, report, sizeof(report)); + break; + } + case '5':{ + uint8_t report[] = {0xEF, 0x90, 0x78, 0x56, 0x34, 0x12, 0x00}; + printf("Write report with id 0x05\n"); + hids_client_send_write_report(hids_cid, 0x05, report, sizeof(report)); + break; + } + case '6':{ + uint8_t report[] = {0xEA, 0x45, 0x3F, 0x2D, 0x87}; + printf("Write report with id 0x06\n"); + hids_client_send_write_report(hids_cid, 0x06, report, sizeof(report)); + break; + } + + case 's':{ + printf("Suspend service 0\n"); + hids_client_send_suspend(hids_cid, 0); + break; + } + case 'S':{ + printf("Suspend service 1\n"); + hids_client_send_suspend(hids_cid, 1); + break; + } + case 'e':{ + printf("Exit Suspend service 0\n"); + hids_client_send_exit_suspend(hids_cid, 0); + break; + } + case 'E':{ + printf("Exit Suspend service 1\n"); + hids_client_send_exit_suspend(hids_cid, 1); + break; + } + case 'p':{ + printf("Set Report protocol mode for service 0\n"); + hids_client_send_set_protocol_mode(hids_cid, HID_PROTOCOL_MODE_REPORT, 0); + break; + } + case 'P':{ + printf("Set Report protocol mode for service 0\n"); + hids_client_send_set_protocol_mode(hids_cid, HID_PROTOCOL_MODE_REPORT, 1); + break; + } + + case '\n': + case '\r': + break; + default: + show_usage(); + break; + } break; default: show_usage(); break; } + } #endif