diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 76106a378..6802c3857 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -628,7 +628,8 @@ extern "C" { #define HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED 0x06 #define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x07 #define HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR 0x08 -#define HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE 0x09 +#define HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE 0x09 +#define HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE 0x0A // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 9460c2649..13864d4a2 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -584,7 +584,7 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 } packet[size] = 0; - printf("\nparse command: %s\n", packet); + printf("\nresponse: %s\n", packet); int pos; for (pos = 0; pos < size ; pos++){ hfp_parse(context, packet[pos]); @@ -607,6 +607,7 @@ static void hfp_run(){ static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ switch (packet_type){ case RFCOMM_DATA_PACKET: + printf("\nRFCOMM_DATA_PACKET: %s\n", packet); hfp_handle_rfcomm_event(packet_type, channel, packet, size); break; case HCI_EVENT_PACKET: diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 44972a7db..d0d52331f 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -485,8 +485,9 @@ static void hfp_run_for_context(hfp_connection_t * context){ break; case HFP_CODECS_CONNECTION_ESTABLISHED: - + printf("HFP_CODECS_CONNECTION_ESTABLISHED \n"); if (context->notify_ag_on_new_codecs){ + printf("restart codecs negotiation \n"); hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid); context->negotiated_codec = 0; context->wait_ok = 1; @@ -494,6 +495,8 @@ static void hfp_run_for_context(hfp_connection_t * context){ break; } + hfp_emit_event(hfp_callback, HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE, 0); + if (context->establish_audio_connection){ // TODO AUDIO CONNECTION } @@ -564,7 +567,7 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 packet[size] = 0; int pos, i; - printf("parse command: %s\n", packet+2); + printf("response: %s\n", packet+2); for (pos = 0; pos < size ; pos++){ hfp_parse(context, packet[pos]); @@ -708,6 +711,26 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_ hfp_run_for_context(connection); } +void hfp_hf_negotiate_codecs(bd_addr_t bd_addr){ + hfp_hf_establish_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + if (!connection){ + log_error("HFP HF: connection doesn't exist."); + return; + } + connection->trigger_codec_connection_setup = 0; + + if (connection->state == HFP_CODECS_CONNECTION_ESTABLISHED) return; + + if (!has_codec_negotiation_feature(connection)){ + connection->trigger_codec_connection_setup = 0; + return; + } + connection->trigger_codec_connection_setup = 1; + + hfp_run_for_context(connection); +} + void hfp_hf_establish_audio_connection(bd_addr_t bd_addr){ hfp_hf_establish_service_level_connection(bd_addr); diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 9529c3ab5..f0e7d8feb 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -141,6 +141,8 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_ /** * @brief */ +void hfp_hf_negotiate_codecs(bd_addr_t bd_addr); + void hfp_hf_establish_audio_connection(bd_addr_t bd_addr); /** diff --git a/test/hfp/hfp_hf_client_test.c b/test/hfp/hfp_hf_client_test.c index 61d873c0d..d353eeef5 100644 --- a/test/hfp/hfp_hf_client_test.c +++ b/test/hfp/hfp_hf_client_test.c @@ -64,120 +64,21 @@ #include "hfp_hf.h" const uint8_t rfcomm_channel_nr = 1; -const char hfp_hf_service_name[] = "BTstack HFP HF Test"; -static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF}; -static bd_addr_t phone_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08}; +static bd_addr_t device_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08}; -static bd_addr_t device_addr; static uint8_t codecs[1] = {HFP_CODEC_CVSD}; static uint16_t indicators[1] = {0x01}; +static uint8_t service_level_connection_established = 0; +static uint8_t codecs_connection_established = 0; +static uint8_t audio_connection_established = 0; + // prototypes -static void show_usage(); uint8_t * get_rfcomm_payload(); uint16_t get_rfcomm_payload_len(); void inject_rfcomm_command(uint8_t * payload, int len); -// Testig User Interface -static void show_usage(void){ - printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n"); - printf("---\n"); - printf("y - use PTS module as Audiogateway\n"); - printf("z - use iPhone as Audiogateway\n"); - - printf("h - establish HFP connection to device\n"); - printf("H - release HFP connection to device\n"); - - printf("a - establish Audio connection to device\n"); - printf("A - release Audio connection to device\n"); - - printf("b - establish AUDIO connection\n"); - printf("B - release AUDIO connection\n"); - - printf("d - enable registration status update\n"); - printf("D - disable registration status update\n"); - - printf("e - enable HFP AG registration status update for individual indicators\n"); - - printf("f - query network operator\n"); - - printf("g - enable reporting of the extended AG error result code\n"); - printf("G - disable reporting of the extended AG error result code\n"); - - printf("---\n"); - printf("Ctrl-c - exit\n"); - printf("---\n"); -} - -static int process(char cmd){ - switch (cmd){ - case 'a': - memcpy(device_addr, pts_addr, 6); - printf("Establish Audio connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr)); - hfp_hf_establish_audio_connection(device_addr); - break; - case 'A': - printf("Release Audio service level connection.\n"); - hfp_hf_release_audio_connection(device_addr); - break; - case 'h': - memcpy(device_addr, pts_addr, 6); - printf("Establish HFP service level connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr)); - hfp_hf_establish_service_level_connection(device_addr); - break; - case 'H': - printf("Release HFP service level connection.\n"); - hfp_hf_release_service_level_connection(device_addr); - break; - case 'b': - printf("Establish Audio connection %s...\n", bd_addr_to_str(device_addr)); - hfp_hf_establish_audio_connection(device_addr); - break; - case 'B': - printf("Release Audio connection.\n"); - hfp_hf_release_audio_connection(device_addr); - break; - case 'd': - printf("Enable HFP AG registration status update.\n"); - hfp_hf_enable_status_update_for_all_ag_indicators(device_addr, 1); - case 'D': - printf("Disable HFP AG registration status update.\n"); - hfp_hf_enable_status_update_for_all_ag_indicators(device_addr, 0); - break; - case 'e': - printf("Enable HFP AG registration status update for individual indicators.\n"); - hfp_hf_enable_status_update_for_individual_ag_indicators(device_addr, 63); - break; - case 'f': - printf("Query network operator.\n"); - hfp_hf_query_operator_selection(device_addr); - break; - case 'g': - printf("Enable reporting of the extended AG error result code.\n"); - hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr, 1); - break; - case 'G': - printf("Disable reporting of the extended AG error result code.\n"); - hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr, 0); - break; - - case 'y': - memcpy(device_addr, phone_addr, 6); - printf("Use iPhone %s as Audiogateway.\n", bd_addr_to_str(device_addr)); - break; - case 'z': - memcpy(device_addr, pts_addr, 6); - printf("Use PTS module %s as Audiogateway.\n", bd_addr_to_str(device_addr)); - break; - default: - show_usage(); - break; - } - return 0; -} - - void packet_handler(uint8_t * event, uint16_t event_size){ if (event[0] != HCI_EVENT_HFP_META) return; if (event[3] && event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR){ @@ -186,8 +87,12 @@ void packet_handler(uint8_t * event, uint16_t event_size){ } switch (event[2]) { case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED: - printf("Service level connection established.\n\n"); + service_level_connection_established = 1; break; + case HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE: + codecs_connection_established = 1; + break; + case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: printf("Service level connection released.\n\n"); break; @@ -211,7 +116,7 @@ void packet_handler(uint8_t * event, uint16_t event_size){ } static int expected_rfcomm_command(const char * cmd){ - printf("%s\n", get_rfcomm_payload()); + //printf("%s\n", get_rfcomm_payload()); return strcmp((char *)cmd, (char *)get_rfcomm_payload()); } @@ -219,24 +124,97 @@ static void verify_expected_rfcomm_command(const char * cmd){ CHECK_EQUAL(expected_rfcomm_command(cmd),0); } -const char hf_supported_features[] = "AT+BRSF=438\r\n"; -const char ag_supported_features[] = "\r\n+BRSF=1007\r\n\r\nOK\r\n"; +const char ag_ok[] = "\r\nOK\r\n"; + +/* START SERVICE LEVEL CONNECTION SEQUENCE */ +const char hf_supported_features[] = "AT+BRSF=438\r\n"; +const char ag_supported_features[] = "\r\n+BRSF:1007\r\n"; + +const char hf_supported_codecs[] = "AT+BAC=1\r\n"; + +const char hf_get_ag_indicators[] = "AT+CIND=?\r\n"; +const char ag_indicators[] = "\r\n+CIND:\"service\",(0,1),\"call\",(0,1),\"callsetup\",(0,3),\"battchg\",(0,5),\"signal\",(0,5),\"roam\",(0,1),\"callheld\",(0,2)\r\n"; + +const char hf_get_ag_indicators_status[] = "AT+CIND?\r\n"; +const char ag_indicators_status[] = "\r\n+CIND:1,0,0,3,5,0,0\r\n"; + +const char hf_enable_indicator_status[] = "AT+CMER=3,0,0,1\r\n"; + +const char hf_get_ag_call_and_multiparty_services[] = "AT+CHLD=?\r\n"; +const char ag_call_and_multiparty_services[] = "\r\n+CHLD:(1,1x,2,2x,3)\r\n"; +/* END SERVICE LEVEL CONNECTION SEQUENCE */ + + +/* START CODECS CONNECTION SEQUENCE */ +const char hf_trigger_codecs_connection[] = "AT+BCC\r\n"; + +const char ag_report_selected_codec[] = "\r\n+BCS:1\r\n"; +const char hf_confirm_selected_codec[] = "AT+BCS=1\r\n"; +/* END CODECS CONNECTION SEQUENCE */ - TEST_GROUP(HandsfreeClient){ void setup(void){ - process('y'); + service_level_connection_established = 0; + codecs_connection_established = 0; + audio_connection_established = 0; + } + + void test_hfp_service_level_connection_state_machine(){ + service_level_connection_established = 0; + hfp_hf_establish_service_level_connection(device_addr); + verify_expected_rfcomm_command(hf_supported_features); + inject_rfcomm_command((uint8_t*)ag_supported_features, strlen(ag_supported_features)); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + + verify_expected_rfcomm_command(hf_supported_codecs); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + + verify_expected_rfcomm_command(hf_get_ag_indicators); + inject_rfcomm_command((uint8_t*)ag_indicators, strlen(ag_indicators)); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + + verify_expected_rfcomm_command(hf_get_ag_indicators_status); + inject_rfcomm_command((uint8_t*)ag_indicators_status, strlen(ag_indicators_status)); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + + verify_expected_rfcomm_command(hf_enable_indicator_status); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + + verify_expected_rfcomm_command(hf_get_ag_call_and_multiparty_services); + inject_rfcomm_command((uint8_t*)ag_call_and_multiparty_services, strlen(ag_call_and_multiparty_services)); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + CHECK_EQUAL(service_level_connection_established, 1); + } + + void test_hfp_codecs_connection_state_machine(){ + codecs_connection_established = 0; + hfp_hf_negotiate_codecs(device_addr); + + verify_expected_rfcomm_command(hf_trigger_codecs_connection); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + + inject_rfcomm_command((uint8_t*)ag_report_selected_codec, strlen(ag_report_selected_codec)); + verify_expected_rfcomm_command(hf_confirm_selected_codec); + inject_rfcomm_command((uint8_t*)ag_ok, strlen(ag_ok)); + CHECK_EQUAL(codecs_connection_established, 1); + } + + void test_audio_connection_state_machine(){ + audio_connection_established = 0; + hfp_hf_establish_audio_connection(device_addr); } }; -TEST(HandsfreeClient, HFAudioConnection){ - process('a'); - verify_expected_rfcomm_command(hf_supported_features); - inject_rfcomm_command((uint8_t*)ag_supported_features, strlen(ag_supported_features)); - + +TEST(HandsfreeClient, HFCodecsConnectionEstablished1){ + test_hfp_service_level_connection_state_machine(); + // test_hfp_codecs_connection_state_machine(); + + // hfp_hf_set_codecs(codecs, 2); } + int main (int argc, const char * argv[]){ hfp_hf_init(rfcomm_channel_nr, 438, indicators, sizeof(indicators)/sizeof(uint16_t), 1); hfp_hf_set_codecs(codecs, sizeof(codecs)); diff --git a/test/hfp/mock.c b/test/hfp/mock.c index 26a594c31..b59b83374 100644 --- a/test/hfp/mock.c +++ b/test/hfp/mock.c @@ -38,11 +38,10 @@ void inject_rfcomm_command(uint8_t * data, int len){ } int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){ - // printf("rfcomm_send_internal\n"); + printf("rfcomm_send_internal %s\n", data); memset(&rfcomm_payload, 0, 200); rfcomm_payload_len = len; memcpy((char*)&rfcomm_payload, data, rfcomm_payload_len); - return 0; }