From a3f9e0cf2d3a9fafc77f586cbcaa3a5aec6051cd Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Wed, 25 Nov 2015 15:53:36 +0100 Subject: [PATCH 01/72] hfp: set/send subscriber numbers --- src/hfp.h | 10 +++++++--- src/hfp_ag.c | 34 ++++++++++++++++++++++++++++++++++ src/hfp_ag.h | 9 +++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index eb8f80104..9626b0d12 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -128,10 +128,9 @@ extern "C" { #define HFP_ACTIVATE_VOICE_RECOGNITION "+BVRA" // EC (Echo CAnceling), NR (Noise Reduction) #define HFP_SET_MICROPHONE_GAIN "+VGM" #define HFP_SET_SPEAKER_GAIN "+VGS" - #define HFP_PHONE_NUMBER_FOR_VOICE_TAG "+BINP" #define HFP_TRANSMIT_DTMF_CODES "+VTS" - +#define HFP_SUBSCRIBER_NUMBER_INFORMATION "+CNUM" #define HFP_OK "OK" #define HFP_ERROR "ERROR" @@ -186,7 +185,8 @@ typedef enum { HFP_CMD_AG_SEND_PHONE_NUMBER, HFP_CMD_TRANSMIT_DTMF_CODES, HFP_CMD_SET_MICROPHONE_GAIN, - HFP_CMD_SET_SPEAKER_GAIN + HFP_CMD_SET_SPEAKER_GAIN, + HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION } hfp_command_t; @@ -464,6 +464,10 @@ typedef struct hfp_connection { uint8_t send_speaker_gain; uint8_t send_phone_number_for_voice_tag; + + uint8_t send_subscriber_number; + int next_subscriber_number_to_send; + timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 5ecff870a..03d04667a 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -84,6 +84,10 @@ static hfp_callheld_status_t hfp_ag_callheld_state; static uint8_t clip_type; // 0 == not set static char clip_number[25]; // +// Subcriber information entries +static hfp_phone_number_t * subscriber_numbers = NULL; +static int subscriber_numbers_count = 0; + static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void hfp_run_for_context(hfp_connection_t *context); static void hfp_ag_setup_audio_connection(hfp_connection_t * connection); @@ -216,6 +220,12 @@ static int hfp_ag_send_clip(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_send_subscriber_number_cmd(uint16_t cid, uint8_t type, const char * number){ + char buffer[50]; + sprintf(buffer, "\r\n%s:,\"%s\",%u,\r\n", HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type); + return send_str_over_rfcomm(cid, buffer); +} + static int hfp_ag_send_phone_number_for_voice_tag_cmd(uint16_t cid){ char buffer[50]; sprintf(buffer, "\r\n%s:%s\r\n", HFP_PHONE_NUMBER_FOR_VOICE_TAG, clip_number); @@ -1285,6 +1295,18 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } + if (context->send_subscriber_number){ + if (context->next_subscriber_number_to_send < subscriber_numbers_count){ + hfp_phone_number_t phone = subscriber_numbers[context->next_subscriber_number_to_send++]; + hfp_send_subscriber_number_cmd(context->rfcomm_cid, phone.type, phone.number); + } else { + context->send_subscriber_number = 0; + context->next_subscriber_number_to_send = 0; + hfp_ag_ok(context->rfcomm_cid); + } + context->command = HFP_CMD_NONE; + } + if (context->send_microphone_gain){ context->send_microphone_gain = 0; context->command = HFP_CMD_NONE; @@ -1336,6 +1358,14 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ hfp_parse(context, packet[pos], 0); } switch(context->command){ + case HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION: + if (subscriber_numbers_count == 0){ + hfp_ag_ok(context->rfcomm_cid); + break; + } + context->next_subscriber_number_to_send = 0; + context->send_subscriber_number = 1; + break; case HFP_CMD_TRANSMIT_DTMF_CODES: context->command = HFP_CMD_NONE; hfp_emit_string_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_DTMF_CODES, (const char *) &context->line_buffer[0]); @@ -1765,3 +1795,7 @@ void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr){ connection->ok_pending = 1; } +void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count){ + subscriber_numbers = numbers; + subscriber_numbers_count = numbers_count; +} diff --git a/src/hfp_ag.h b/src/hfp_ag.h index 6e5823357..e59b8ad3e 100644 --- a/src/hfp_ag.h +++ b/src/hfp_ag.h @@ -54,6 +54,10 @@ extern "C" { #endif /* API_START */ +typedef struct { + uint8_t type; + const char * number; +} hfp_phone_number_t; /** * @brief Create HFP Audio Gateway (AG) SDP service record. @@ -266,6 +270,11 @@ void hfp_ag_reject_phone_number_for_voice_tag(bd_addr_t bd_addr); */ void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr); +/* + * @brief + */ +void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count); + /* API_END */ #if defined __cplusplus From 14738cc203390643f94d76303c31e110e2208abb Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Wed, 25 Nov 2015 17:11:04 +0100 Subject: [PATCH 02/72] hfp: send current call status --- include/btstack/hci_cmds.h | 2 +- src/hfp.c | 8 ++++++++ src/hfp.h | 33 ++++++++++++++++++++++++++++++++- src/hfp_ag.c | 34 +++++++++++++++++++++++++++++++++- src/hfp_ag.h | 9 +++++++++ 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index b60c50b79..4cf2a3efe 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -644,7 +644,7 @@ extern "C" { #define HFP_SUBEVENT_REDIAL_LAST_NUMBER 0x0E #define HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG 0x0F #define HFP_SUBEVENT_TRANSMIT_DTMF_CODES 0x10 - +#define HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL 0x11 // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp.c b/src/hfp.c index e63c73919..157e6971a 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -621,6 +621,14 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ int offset = isHandsFree ? 0 : 2; + if (strncmp(line_buffer+offset, HFP_LIST_CURRENT_CALLS, strlen(HFP_LIST_CURRENT_CALLS)) == 0){ + return HFP_CMD_LIST_CURRENT_CALLS; + } + + if (strncmp(line_buffer+offset, HFP_SUBSCRIBER_NUMBER_INFORMATION, strlen(HFP_SUBSCRIBER_NUMBER_INFORMATION)) == 0){ + return HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION; + } + if (strncmp(line_buffer+offset, HFP_PHONE_NUMBER_FOR_VOICE_TAG, strlen(HFP_PHONE_NUMBER_FOR_VOICE_TAG)) == 0){ if (isHandsFree) return HFP_CMD_AG_SEND_PHONE_NUMBER; return HFP_CMD_HF_REQUEST_PHONE_NUMBER; diff --git a/src/hfp.h b/src/hfp.h index 9626b0d12..23071cc62 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -131,6 +131,8 @@ extern "C" { #define HFP_PHONE_NUMBER_FOR_VOICE_TAG "+BINP" #define HFP_TRANSMIT_DTMF_CODES "+VTS" #define HFP_SUBSCRIBER_NUMBER_INFORMATION "+CNUM" +#define HFP_LIST_CURRENT_CALLS "+CLCC" + #define HFP_OK "OK" #define HFP_ERROR "ERROR" @@ -186,7 +188,9 @@ typedef enum { HFP_CMD_TRANSMIT_DTMF_CODES, HFP_CMD_SET_MICROPHONE_GAIN, HFP_CMD_SET_SPEAKER_GAIN, - HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION + HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION, + HFP_CMD_LIST_CURRENT_CALLS + } hfp_command_t; @@ -343,6 +347,32 @@ typedef enum { HFP_CALL_OUTGOING_RINGING } hfp_call_state_t; +typedef enum{ + HFP_ENHANCED_CALL_DIR_OUTGOING, + HFP_ENHANCED_CALL_DIR_INCOMING +} hfp_enhanced_call_dir_t; + +typedef enum{ + HFP_ENHANCED_CALL_STATUS_ACTIVE, + HFP_ENHANCED_CALL_STATUS_HELD, + HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING, + HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING, + HFP_ENHANCED_CALL_STATUS_INCOMING, + HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING, + HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD +} hfp_enhanced_call_status_t; + +typedef enum{ + HFP_ENHANCED_CALL_MODE_VOICE, + HFP_ENHANCED_CALL_MODE_DATA, + HFP_ENHANCED_CALL_MODE_FAX +} hfp_enhanced_call_mode_t; + +typedef enum{ + HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL, + HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL +} hfp_enhanced_call_mpty_t; + typedef enum{ HFP_NONE_SM, HFP_SLC_SM, @@ -468,6 +498,7 @@ typedef struct hfp_connection { uint8_t send_subscriber_number; int next_subscriber_number_to_send; + int send_status_of_current_calls; timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 03d04667a..e3cb52274 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -232,7 +232,6 @@ static int hfp_ag_send_phone_number_for_voice_tag_cmd(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } - static int hfp_ag_send_call_waiting_notification(uint16_t cid){ if (!clip_type){ clip_number[0] = 0; @@ -1239,6 +1238,11 @@ static void hfp_run_for_context(hfp_connection_t *context){ if (!context) return; if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return; + if (context->send_status_of_current_calls){ + hfp_emit_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL, 0); + return; + } + if (context->command == HFP_CMD_UNKNOWN){ context->ok_pending = 0; context->send_error = 0; @@ -1358,6 +1362,11 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ hfp_parse(context, packet[pos], 0); } switch(context->command){ + case HFP_CMD_LIST_CURRENT_CALLS: + context->command = HFP_CMD_NONE; + context->send_status_of_current_calls = 1; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL, 0); + break; case HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION: if (subscriber_numbers_count == 0){ hfp_ag_ok(context->rfcomm_cid); @@ -1799,3 +1808,26 @@ void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int n subscriber_numbers = numbers; subscriber_numbers_count = numbers_count; } + +void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_call_dir_t dir, + hfp_enhanced_call_status_t status, hfp_enhanced_call_mode_t mode, + hfp_enhanced_call_mpty_t mpty, uint8_t type, const char * number){ + + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + + char buffer[100]; + int offset = snprintf(buffer, sizeof(buffer), "\r\n%s:%d,%d,%d,%d,%d", HFP_LIST_CURRENT_CALLS, idx, dir, status, mode, mpty); + if (number){ + offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\"%s\",%u", number, type); + } + snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n"); + send_str_over_rfcomm(connection->rfcomm_cid, buffer); +} + + +void hfp_ag_send_current_call_status_done(bd_addr_t bd_addr){ + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + connection->ok_pending = 1; + connection->send_status_of_current_calls = 0; +} + diff --git a/src/hfp_ag.h b/src/hfp_ag.h index e59b8ad3e..22c8d2296 100644 --- a/src/hfp_ag.h +++ b/src/hfp_ag.h @@ -275,6 +275,15 @@ void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr); */ void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count); +/* + * @brief + */ +void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_call_dir_t dir, + hfp_enhanced_call_status_t status, hfp_enhanced_call_mode_t mode, + hfp_enhanced_call_mpty_t mpty, uint8_t type, const char * number); + +void hfp_ag_send_current_call_status_done(bd_addr_t bd_addr); + /* API_END */ #if defined __cplusplus From dcd562c5719c563ede646579a562e7c34511849c Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Wed, 25 Nov 2015 17:19:12 +0100 Subject: [PATCH 03/72] hfp: add a space in cmd --- src/hfp_ag.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index e3cb52274..ace7198fd 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -222,13 +222,13 @@ static int hfp_ag_send_clip(uint16_t cid){ static int hfp_send_subscriber_number_cmd(uint16_t cid, uint8_t type, const char * number){ char buffer[50]; - sprintf(buffer, "\r\n%s:,\"%s\",%u,\r\n", HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type); + sprintf(buffer, "\r\n%s: ,\"%s\",%u,\r\n", HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type); return send_str_over_rfcomm(cid, buffer); } static int hfp_ag_send_phone_number_for_voice_tag_cmd(uint16_t cid){ char buffer[50]; - sprintf(buffer, "\r\n%s:%s\r\n", HFP_PHONE_NUMBER_FOR_VOICE_TAG, clip_number); + sprintf(buffer, "\r\n%s: %s\r\n", HFP_PHONE_NUMBER_FOR_VOICE_TAG, clip_number); return send_str_over_rfcomm(cid, buffer); } @@ -1816,7 +1816,7 @@ void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_ca hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); char buffer[100]; - int offset = snprintf(buffer, sizeof(buffer), "\r\n%s:%d,%d,%d,%d,%d", HFP_LIST_CURRENT_CALLS, idx, dir, status, mode, mpty); + int offset = snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d,%d,%d,%d", HFP_LIST_CURRENT_CALLS, idx, dir, status, mode, mpty); if (number){ offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\"%s\",%u", number, type); } From c25cc90f4f0ade7d5dc65fd368f69b67d97f96ac Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 17:26:31 +0100 Subject: [PATCH 04/72] support DTMF and voice tag attachments --- test/pts/hfp_ag_test.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 4dc495e0f..012dd8d21 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -306,7 +306,11 @@ static 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_PLACE_CALL_WITH_NUMBER){ + if (event[3] + && event[2] != HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER + && event[2] != HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG + && event[2] != HFP_SUBEVENT_TRANSMIT_DTMF_CODES + && event[2] != HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL){ printf("ERROR, status: %u\n", event[3]); return; } @@ -357,6 +361,14 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ hfp_ag_outgoing_call_rejected(); } break; + case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG: + printf("\n** Attach number to voice tag. Sending '1234567\n"); + hfp_ag_send_phone_number_for_voice_tag(device_addr, "1234567"); + break; + case HFP_SUBEVENT_TRANSMIT_DTMF_CODES: + printf("\n** Send DTMF Codes: '%s'\n", &event[3]); + hfp_ag_send_dtmf_code_done(device_addr); + break; default: // printf("event not handled %u\n", event[2]); From f78627e31535b3f632e4ff6ca58b4bea09155a8a Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 19:18:08 +0100 Subject: [PATCH 05/72] add commands to set speaker/microphone gain in ag test --- src/hfp_ag.c | 2 ++ test/pts/hfp_ag_test.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index ace7198fd..48a7dfba6 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1772,6 +1772,7 @@ void hfp_ag_set_microphone_gain(bd_addr_t bd_addr, int gain){ connection->microphone_gain = gain; connection->send_microphone_gain = 1; } + hfp_run_for_context(connection); } /* @@ -1783,6 +1784,7 @@ void hfp_ag_set_speaker_gain(bd_addr_t bd_addr, int gain){ connection->speaker_gain = gain; connection->send_speaker_gain = 1; } + hfp_run_for_context(connection); } /* diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 012dd8d21..9548efbbf 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -155,6 +155,14 @@ static void show_usage(void){ printf("n - Disable Voice Regocnition\n"); printf("N - Enable Voice Recognition\n"); + printf("o - Set speaker volume to 0 (minimum)\n"); + printf("O - Set speaker volume to 9 (default)\n"); + printf("p - Set speaker volume to 12 (higher)\n"); + printf("P - Set speaker volume to 15 (maximum)\n"); + + printf("q - Set microphone gain to 9\n"); + printf("Q - Set microphone gain to 12\n"); + printf("t - terminate connection\n"); printf("---\n"); @@ -281,6 +289,30 @@ static int stdin_process(struct data_source *ds){ printf("Enable Voice Recognition\n"); hfp_ag_activate_voice_recognition(device_addr, 1); break; + case 'o': + printf("Set speaker gain to 0 (minimum)\n"); + hfp_ag_set_speaker_gain(device_addr, 0); + break; + case 'O': + printf("Set speaker gain to 9 (default)\n"); + hfp_ag_set_speaker_gain(device_addr, 9); + break; + case 'p': + printf("Set speaker gain to 12 (higher)\n"); + hfp_ag_set_speaker_gain(device_addr, 12); + break; + case 'P': + printf("Set speaker gain to 15 (maximum)\n"); + hfp_ag_set_speaker_gain(device_addr, 15); + break; + case 'q': + printf("Set microphone gain to 9\n"); + hfp_ag_set_microphone_gain(device_addr, 9); + break; + case 'Q': + printf("Set microphone gain to 12\n"); + hfp_ag_set_microphone_gain(device_addr, 12); + break; case 'R': printf("Enable in-band ring tone\n"); hfp_ag_set_use_in_band_ring_tone(1); From 2c83f1b764929865b4832553e338adf727a588d8 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 19:25:56 +0100 Subject: [PATCH 06/72] add commands to set microphone gain --- test/pts/hfp_ag_test.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 9548efbbf..d00042876 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -160,8 +160,10 @@ static void show_usage(void){ printf("p - Set speaker volume to 12 (higher)\n"); printf("P - Set speaker volume to 15 (maximum)\n"); - printf("q - Set microphone gain to 9\n"); - printf("Q - Set microphone gain to 12\n"); + printf("q - Set microphone gain to 0 (minimum)\n"); + printf("Q - Set microphone gain to 9 (default)\n"); + printf("s - Set microphone gain to 12 (higher)\n"); + printf("S - Set microphone gain to 15 (maximum)\n"); printf("t - terminate connection\n"); @@ -306,13 +308,21 @@ static int stdin_process(struct data_source *ds){ hfp_ag_set_speaker_gain(device_addr, 15); break; case 'q': + printf("Set microphone gain to 0\n"); + hfp_ag_set_microphone_gain(device_addr, 0); + break; + case 'Q': printf("Set microphone gain to 9\n"); hfp_ag_set_microphone_gain(device_addr, 9); break; - case 'Q': + case 's': printf("Set microphone gain to 12\n"); hfp_ag_set_microphone_gain(device_addr, 12); break; + case 'S': + printf("Set microphone gain to 15\n"); + hfp_ag_set_microphone_gain(device_addr, 15); + break; case 'R': printf("Enable in-band ring tone\n"); hfp_ag_set_use_in_band_ring_tone(1); From ae7b82612a392e038e7a59458b73abe8d9e47768 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 22:04:01 +0100 Subject: [PATCH 07/72] test enhanced call status --- src/hfp_ag.c | 2 +- test/pts/hfp_ag_test.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 48a7dfba6..0c2ce6295 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1820,7 +1820,7 @@ void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_ca char buffer[100]; int offset = snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d,%d,%d,%d", HFP_LIST_CURRENT_CALLS, idx, dir, status, mode, mpty); if (number){ - offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\"%s\",%u", number, type); + offset += snprintf(buffer+offset, sizeof(buffer)-offset, ", \"%s\",%u", number, type); } snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n"); send_str_over_rfcomm(connection->rfcomm_cid, buffer); diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index d00042876..5d3e02e9a 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -79,6 +79,12 @@ static uint16_t handle = -1; static int memory_1_enabled = 1; static int last_number_exists = 1; +static int current_call_info_available = 0; +static hfp_enhanced_call_dir_t current_call_dir; +static hfp_enhanced_call_status_t current_call_status; +static hfp_enhanced_call_mpty_t current_call_mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL; +static char * current_call_number = NULL; + static int ag_indicators_nr = 7; static hfp_ag_indicator_t ag_indicators[] = { // index, name, min range, max range, status, mandatory, enabled, status changed @@ -203,11 +209,19 @@ static int stdin_process(struct data_source *ds){ break; case 'c': printf("Simulate incoming call from 1234567\n"); + current_call_info_available = 1; + current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING; + current_call_status = HFP_ENHANCED_CALL_STATUS_INCOMING; + current_call_number = "1234567"; hfp_ag_set_clip(129, "1234567"); hfp_ag_incoming_call(); break; case 'm': printf("Simulate incoming call from 7654321\n"); + current_call_info_available = 1; + current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING; + current_call_status = HFP_ENHANCED_CALL_STATUS_INCOMING; + current_call_number = "7654321"; hfp_ag_set_clip(129, "7654321"); hfp_ag_incoming_call(); break; @@ -221,10 +235,12 @@ static int stdin_process(struct data_source *ds){ break; case 'e': printf("Answer call on AG\n"); + current_call_status = HFP_ENHANCED_CALL_STATUS_ACTIVE; hfp_ag_answer_incoming_call(); break; case 'E': printf("Reject call on AG\n"); + current_call_info_available = 0; hfp_ag_terminate_call(); break; case 'f': @@ -348,6 +364,7 @@ static 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_PLACE_CALL_WITH_NUMBER && event[2] != HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG @@ -411,9 +428,16 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ printf("\n** Send DTMF Codes: '%s'\n", &event[3]); hfp_ag_send_dtmf_code_done(device_addr); break; - + case HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL: + if (current_call_info_available){ + current_call_info_available = 0; + hfp_ag_send_current_call_status(device_addr, 1, current_call_dir, current_call_status, + HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, current_call_number); + } + hfp_ag_send_current_call_status_done(device_addr); + break; default: - // printf("event not handled %u\n", event[2]); + printf("Event not handled %u\n", event[2]); break; } } From 65f727c4404802d8dad0c54b0b348224c30c386c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 22:18:13 +0100 Subject: [PATCH 08/72] hfp: transfer call/callheld status when second calls gets accepted by AG --- src/hfp_ag.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 0c2ce6295..d3436d95f 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1033,10 +1033,8 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect printf("AG: current call is placed on hold, incoming call gets active\n"); hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED); - // TODO: update AG indicators for all connections - // context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); - // context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); - // context->call_state = HFP_CALL_ACTIVE; + hfp_ag_transfer_callsetup_state(); + hfp_ag_transfer_callheld_state(); break; default: break; From 7df18b41ebbdf05b106cbdc141ae11d3975e4994 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 22:25:21 +0100 Subject: [PATCH 09/72] send required optional params in +CNUM --- src/hfp_ag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index d3436d95f..93f608da5 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -222,7 +222,7 @@ static int hfp_ag_send_clip(uint16_t cid){ static int hfp_send_subscriber_number_cmd(uint16_t cid, uint8_t type, const char * number){ char buffer[50]; - sprintf(buffer, "\r\n%s: ,\"%s\",%u,\r\n", HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type); + sprintf(buffer, "\r\n%s: ,\"%s\",%u, , \r\n", HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type); return send_str_over_rfcomm(cid, buffer); } From 4f32727bc10dc872186aec374652003594baa8fb Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 22:56:16 +0100 Subject: [PATCH 10/72] provide subscriber number, support joining calls by AG --- src/hfp.h | 1 + src/hfp_ag.c | 23 +++++++++++++++++++++++ src/hfp_ag.h | 7 ++++++- test/pts/hfp_ag_test.c | 41 +++++++++++++++++++++++++++++------------ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index 23071cc62..17c3d767e 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -259,6 +259,7 @@ typedef enum { HFP_AG_OUTGOING_CALL_RINGING, HFP_AG_OUTGOING_CALL_ESTABLISHED, HFP_AG_OUTGOING_REDIAL_INITIATED, + HFP_AG_HELD_CALL_JOINED_BY_AG, HFP_AG_TERMINATE_CALL_BY_AG, HFP_AG_TERMINATE_CALL_BY_HF, HFP_AG_CALL_DROPPED, diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 93f608da5..dbd6b3751 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1042,6 +1042,25 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; } break; + + case HFP_AG_HELD_CALL_JOINED_BY_AG: + switch (hfp_ag_call_state){ + case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: + switch (hfp_ag_callheld_state){ + case HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED: + printf("AG: joining held call with active call\n"); + hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_NO_CALLS_HELD); + hfp_ag_transfer_callheld_state(); + break; + default: + break; + } + break; + default: + break; + } + break; + case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF: // clear CLIP clip_type = 0; @@ -1678,6 +1697,10 @@ void hfp_ag_answer_incoming_call(void){ hfp_ag_call_sm(HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG, NULL); } +void hfp_ag_join_held_call(void){ + hfp_ag_call_sm(HFP_AG_HELD_CALL_JOINED_BY_AG, NULL); +} + void hfp_ag_terminate_call(void){ hfp_ag_call_sm(HFP_AG_TERMINATE_CALL_BY_AG, NULL); } diff --git a/src/hfp_ag.h b/src/hfp_ag.h index 22c8d2296..7cd5c027a 100644 --- a/src/hfp_ag.h +++ b/src/hfp_ag.h @@ -212,7 +212,12 @@ void hfp_ag_call_dropped(void); /** * @brief */ - void hfp_ag_answer_incoming_call(void); +void hfp_ag_answer_incoming_call(void); + +/** + * @brief + */ +void hfp_ag_join_held_call(void); /** * @brief diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 5d3e02e9a..d14b50a64 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -79,11 +79,13 @@ static uint16_t handle = -1; static int memory_1_enabled = 1; static int last_number_exists = 1; -static int current_call_info_available = 0; +static int current_call_index = 0; static hfp_enhanced_call_dir_t current_call_dir; +static int current_call_exists_a = 0; +static int current_call_exists_b = 0; static hfp_enhanced_call_status_t current_call_status; static hfp_enhanced_call_mpty_t current_call_mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL; -static char * current_call_number = NULL; + static int ag_indicators_nr = 7; static hfp_ag_indicator_t ag_indicators[] = { @@ -172,7 +174,7 @@ static void show_usage(void){ printf("S - Set microphone gain to 15 (maximum)\n"); printf("t - terminate connection\n"); - + printf("u - join held call\n"); printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -209,19 +211,17 @@ static int stdin_process(struct data_source *ds){ break; case 'c': printf("Simulate incoming call from 1234567\n"); - current_call_info_available = 1; + current_call_exists_a = 1; current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING; current_call_status = HFP_ENHANCED_CALL_STATUS_INCOMING; - current_call_number = "1234567"; hfp_ag_set_clip(129, "1234567"); hfp_ag_incoming_call(); break; case 'm': printf("Simulate incoming call from 7654321\n"); - current_call_info_available = 1; + current_call_exists_b = 1; current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING; current_call_status = HFP_ENHANCED_CALL_STATUS_INCOMING; - current_call_number = "7654321"; hfp_ag_set_clip(129, "7654321"); hfp_ag_incoming_call(); break; @@ -240,7 +240,6 @@ static int stdin_process(struct data_source *ds){ break; case 'E': printf("Reject call on AG\n"); - current_call_info_available = 0; hfp_ag_terminate_call(); break; case 'f': @@ -346,6 +345,13 @@ static int stdin_process(struct data_source *ds){ case 't': printf("Terminate HCI connection.\n"); gap_disconnect(handle); + break; + case 'u': + printf("Join held call\n"); + current_call_mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL; + current_call_status = HFP_ENHANCED_CALL_STATUS_ACTIVE; + hfp_ag_join_held_call(); + break; default: show_usage(); break; @@ -429,10 +435,17 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ hfp_ag_send_dtmf_code_done(device_addr); break; case HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL: - if (current_call_info_available){ - current_call_info_available = 0; + if (current_call_index == 0 && current_call_exists_a){ hfp_ag_send_current_call_status(device_addr, 1, current_call_dir, current_call_status, - HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, current_call_number); + HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, "1234567"); + current_call_index = 1; + break; + } + if (current_call_index == 1 && current_call_exists_b){ + hfp_ag_send_current_call_status(device_addr, 2, current_call_dir, current_call_status, + HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, "7654321"); + current_call_index = 2; + break; } hfp_ag_send_current_call_status_done(device_addr); break; @@ -442,6 +455,10 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ } } +static hfp_phone_number_t subscriber_number = { + 129, "225577" +}; + int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]){ // init L2CAP @@ -452,7 +469,7 @@ int btstack_main(int argc, const char * argv[]){ ag_indicators, ag_indicators_nr, hf_indicators, hf_indicators_nr, call_hold_services, call_hold_services_nr); - + hfp_ag_set_subcriber_number_information(&subscriber_number, 1); hfp_ag_register_packet_handler(packet_handler); sdp_init(); From b6eb22e017af3be8dbb3012b8881802ffe24230c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Nov 2015 23:14:17 +0100 Subject: [PATCH 11/72] emit events for HP Answers and Conference Call --- include/btstack/hci_cmds.h | 2 ++ src/hfp_ag.c | 2 ++ test/pts/hfp_ag_test.c | 33 ++++++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 4cf2a3efe..05609c20c 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -645,6 +645,8 @@ extern "C" { #define HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG 0x0F #define HFP_SUBEVENT_TRANSMIT_DTMF_CODES 0x10 #define HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL 0x11 +#define HFP_SUBEVENT_CALL_ANSWERED 0x12 +#define HFP_SUBEVENT_CONFERENCE_CALL 0x13 // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp_ag.c b/src/hfp_ag.c index dbd6b3751..b1cbb981e 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1072,6 +1072,7 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT); hfp_ag_hf_accept_call(connection); printf("HF answers call, accept call by GSM\n"); + hfp_emit_event(hfp_callback, HFP_CMD_CALL_ANSWERED, 0); break; default: break; @@ -1476,6 +1477,7 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ printf("AG: Join 3-way-call\n"); hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_NO_CALLS_HELD); context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); + hfp_emit_event(hfp_callback, HFP_SUBEVENT_CONFERENCE_CALL, 0); } context->call_state = HFP_CALL_ACTIVE; break; diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index d14b50a64..0fca0e67a 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -83,7 +83,8 @@ static int current_call_index = 0; static hfp_enhanced_call_dir_t current_call_dir; static int current_call_exists_a = 0; static int current_call_exists_b = 0; -static hfp_enhanced_call_status_t current_call_status; +static hfp_enhanced_call_status_t current_call_status_a; +static hfp_enhanced_call_status_t current_call_status_b; static hfp_enhanced_call_mpty_t current_call_mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL; @@ -212,16 +213,16 @@ static int stdin_process(struct data_source *ds){ case 'c': printf("Simulate incoming call from 1234567\n"); current_call_exists_a = 1; + current_call_status_a = HFP_ENHANCED_CALL_STATUS_INCOMING; current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING; - current_call_status = HFP_ENHANCED_CALL_STATUS_INCOMING; hfp_ag_set_clip(129, "1234567"); hfp_ag_incoming_call(); break; case 'm': printf("Simulate incoming call from 7654321\n"); current_call_exists_b = 1; + current_call_status_b = HFP_ENHANCED_CALL_STATUS_INCOMING; current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING; - current_call_status = HFP_ENHANCED_CALL_STATUS_INCOMING; hfp_ag_set_clip(129, "7654321"); hfp_ag_incoming_call(); break; @@ -235,7 +236,12 @@ static int stdin_process(struct data_source *ds){ break; case 'e': printf("Answer call on AG\n"); - current_call_status = HFP_ENHANCED_CALL_STATUS_ACTIVE; + if (current_call_status_a == HFP_ENHANCED_CALL_STATUS_INCOMING){ + current_call_status_a = HFP_ENHANCED_CALL_STATUS_ACTIVE; + } + if (current_call_status_b == HFP_ENHANCED_CALL_STATUS_INCOMING){ + current_call_status_b = HFP_ENHANCED_CALL_STATUS_ACTIVE; + } hfp_ag_answer_incoming_call(); break; case 'E': @@ -349,7 +355,6 @@ static int stdin_process(struct data_source *ds){ case 'u': printf("Join held call\n"); current_call_mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL; - current_call_status = HFP_ENHANCED_CALL_STATUS_ACTIVE; hfp_ag_join_held_call(); break; default: @@ -436,19 +441,33 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ break; case HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL: if (current_call_index == 0 && current_call_exists_a){ - hfp_ag_send_current_call_status(device_addr, 1, current_call_dir, current_call_status, + hfp_ag_send_current_call_status(device_addr, 1, current_call_dir, current_call_status_a, HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, "1234567"); current_call_index = 1; break; } if (current_call_index == 1 && current_call_exists_b){ - hfp_ag_send_current_call_status(device_addr, 2, current_call_dir, current_call_status, + hfp_ag_send_current_call_status(device_addr, 2, current_call_dir, current_call_status_b, HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, "7654321"); current_call_index = 2; break; } hfp_ag_send_current_call_status_done(device_addr); break; + case HFP_CMD_CALL_ANSWERED: + printf("Call answered by HF\n"); + if (current_call_status_a == HFP_ENHANCED_CALL_STATUS_INCOMING){ + current_call_status_a = HFP_ENHANCED_CALL_STATUS_ACTIVE; + } + if (current_call_status_b == HFP_ENHANCED_CALL_STATUS_INCOMING){ + current_call_status_b = HFP_ENHANCED_CALL_STATUS_ACTIVE; + } + break; + case HFP_SUBEVENT_CONFERENCE_CALL: + current_call_mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL; + current_call_status_a = HFP_ENHANCED_CALL_STATUS_ACTIVE; + current_call_status_b = HFP_ENHANCED_CALL_STATUS_ACTIVE; + break; default: printf("Event not handled %u\n", event[2]); break; From c52e356b36fffeef23b22e84dc37b9c526f8632e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 10:40:45 +0100 Subject: [PATCH 12/72] add gap inquiry to hfp_ag_test --- src/hfp.c | 8 +- test/pts/hfp_ag_test.c | 185 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) diff --git a/src/hfp.c b/src/hfp.c index 157e6971a..a3be897af 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -612,7 +612,13 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t } break; - default: + case HCI_EVENT_INQUIRY_RESULT: + case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI: + case HCI_EVENT_INQUIRY_COMPLETE: + case BTSTACK_EVENT_REMOTE_NAME_CACHED: + case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: + // forward inquiry events to app - TODO: replace with new event handler architecture + (*callback)(packet, size); break; } } diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 0fca0e67a..eae35fe00 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -113,6 +113,168 @@ char cmd; // prototypes static void show_usage(); +// GAP INQUIRY + +#define MAX_DEVICES 10 +enum DEVICE_STATE { REMOTE_NAME_REQUEST, REMOTE_NAME_INQUIRED, REMOTE_NAME_FETCHED }; +struct device { + bd_addr_t address; + uint16_t clockOffset; + uint32_t classOfDevice; + uint8_t pageScanRepetitionMode; + uint8_t rssi; + enum DEVICE_STATE state; +}; + +#define INQUIRY_INTERVAL 5 +struct device devices[MAX_DEVICES]; +int deviceCount = 0; + + +enum STATE {INIT, W4_INQUIRY_MODE_COMPLETE, ACTIVE} ; +enum STATE state = INIT; + + +static int getDeviceIndexForAddress( bd_addr_t addr){ + int j; + for (j=0; j< deviceCount; j++){ + if (BD_ADDR_CMP(addr, devices[j].address) == 0){ + return j; + } + } + return -1; +} + +static void start_scan(void){ + printf("Starting inquiry scan..\n"); + hci_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0); +} + +static int has_more_remote_name_requests(void){ + int i; + for (i=0;i= 0) continue; // already in our list + memcpy(devices[deviceCount].address, addr, 6); + + devices[deviceCount].pageScanRepetitionMode = packet[offset]; + offset += 1; + + if (event == HCI_EVENT_INQUIRY_RESULT){ + offset += 2; // Reserved + Reserved + devices[deviceCount].classOfDevice = READ_BT_24(packet, offset); + offset += 3; + devices[deviceCount].clockOffset = READ_BT_16(packet, offset) & 0x7fff; + offset += 2; + devices[deviceCount].rssi = 0; + } else { + offset += 1; // Reserved + devices[deviceCount].classOfDevice = READ_BT_24(packet, offset); + offset += 3; + devices[deviceCount].clockOffset = READ_BT_16(packet, offset) & 0x7fff; + offset += 2; + devices[deviceCount].rssi = packet[offset]; + offset += 1; + } + devices[deviceCount].state = REMOTE_NAME_REQUEST; + printf("Device #%u found: %s with COD: 0x%06x, pageScan %d, clock offset 0x%04x, rssi 0x%02x\n", + deviceCount, bd_addr_to_str(addr), + devices[deviceCount].classOfDevice, devices[deviceCount].pageScanRepetitionMode, + devices[deviceCount].clockOffset, devices[deviceCount].rssi); + deviceCount++; + } + + break; + } + case HCI_EVENT_INQUIRY_COMPLETE: + for (i=0;i= 0) { + if (packet[2] == 0) { + printf("Name: '%s'\n", &packet[9]); + devices[index].state = REMOTE_NAME_FETCHED; + } else { + printf("Failed to get name: page timeout\n"); + } + } + continue_remote_names(); + break; + + default: + break; + } +} +// GAP INQUIRY END + // Testig User Interface static void show_usage(void){ printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n"); @@ -176,6 +338,8 @@ static void show_usage(void){ printf("t - terminate connection\n"); printf("u - join held call\n"); + printf("v - discover nearby HF units\n"); + printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -357,6 +521,9 @@ static int stdin_process(struct data_source *ds){ current_call_mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL; hfp_ag_join_held_call(); break; + case 'v': + start_scan(); + break; default: show_usage(); break; @@ -374,6 +541,24 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ return; } + switch (event[0]){ + case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: + handle = READ_BT_16(event, 9); + printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE received for handle 0x%04x\n", handle); + return; + + case HCI_EVENT_INQUIRY_RESULT: + case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI: + case HCI_EVENT_INQUIRY_COMPLETE: + case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: + inquiry_packet_handler(HCI_EVENT_PACKET, event, event_size); + break; + + default: + break; + } + + if (event[0] != HCI_EVENT_HFP_META) return; if (event[3] From 42dbbb66b4fd08aa2da2c0fa121e7e4f03e65ddd Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 11:07:15 +0100 Subject: [PATCH 13/72] hfp: list indicator name in log, don't send update if indicator disabled --- src/hfp.c | 6 +++++- src/hfp_ag.c | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/hfp.c b/src/hfp.c index a3be897af..ae1a6b877 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -1032,10 +1032,14 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ break; case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE: // AG parses new gen. ind. state - log_info("Parsed Enable ag indicator state: %s\n", context->line_buffer); value = atoi((char *)&context->line_buffer[0]); if (!context->ag_indicators[context->parser_item_index].mandatory){ context->ag_indicators[context->parser_item_index].enabled = value; + log_info("Parsed Enable AG indicator %u('%s'): %u\n", context->parser_item_index, + context->ag_indicators[context->parser_item_index].name, value); + } else { + log_info("Parsed Enable AG indicator %u('%s') - ignore (mandatory)\n", + context->parser_item_index, context->ag_indicators[context->parser_item_index].name); } context->parser_item_index++; break; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index b1cbb981e..bcada4635 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1727,10 +1727,16 @@ static void hfp_ag_set_ag_indicator(const char * name, int value){ if (indicator_index < 0) return; hfp_ag_indicators[indicator_index].status = value; + linked_list_iterator_t it; linked_list_iterator_init(&it, hfp_get_connections()); while (linked_list_iterator_has_next(&it)){ hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it); + if (!connection->ag_indicators[indicator_index].enabled) { + log_info("AG indicator '%s' changed to %u but not enabled", hfp_ag_indicators[indicator_index].name, value); + continue; + } + log_info("AG indicator '%s' changed to %u, request transfer statur", hfp_ag_indicators[indicator_index].name, value); connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, indicator_index, 1); hfp_run_for_context(connection); } From 63ffda6041e7257c6da6fb20da4be108fd61de43 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 26 Nov 2015 11:29:51 +0100 Subject: [PATCH 14/72] hfp: hf rewriten codecs connection sm --- src/hfp.h | 1 + src/hfp_hf.c | 258 +++++++++++++++++----------------- test/hfp/hfp_hf_client_test.c | 74 +++++++--- 3 files changed, 187 insertions(+), 146 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index 17c3d767e..e375d691a 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -330,6 +330,7 @@ typedef enum { HFP_CODECS_W4_AG_COMMON_CODEC, HFP_CODECS_AG_SENT_COMMON_CODEC, HFP_CODECS_AG_RESEND_COMMON_CODEC, + HFP_CODECS_HF_CONFIRMED_CODEC, HFP_CODECS_EXCHANGED, HFP_CODECS_ERROR } hfp_codecs_state_t; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index b4a59a60e..74f1700ab 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -300,67 +300,6 @@ static int hfp_hf_run_for_context_service_level_connection(hfp_connection_t * co return done; } -static void hfp_hf_handle_ok_service_level_connection_establishment(hfp_connection_t *context){ - if (context->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return; - switch (context->state){ - case HFP_W4_EXCHANGE_SUPPORTED_FEATURES: - if (has_codec_negotiation_feature(context)){ - context->state = HFP_NOTIFY_ON_CODECS; - break; - } - context->state = HFP_RETRIEVE_INDICATORS; - break; - - case HFP_W4_NOTIFY_ON_CODECS: - context->state = HFP_RETRIEVE_INDICATORS; - break; - - case HFP_W4_RETRIEVE_INDICATORS: - context->state = HFP_RETRIEVE_INDICATORS_STATUS; - break; - - case HFP_W4_RETRIEVE_INDICATORS_STATUS: - context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE; - break; - - case HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE: - if (has_call_waiting_and_3way_calling_feature(context)){ - context->state = HFP_RETRIEVE_CAN_HOLD_CALL; - break; - } - if (has_hf_indicators_feature(context)){ - context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; - break; - } - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); - break; - - case HFP_W4_RETRIEVE_CAN_HOLD_CALL: - if (has_hf_indicators_feature(context)){ - context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; - break; - } - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); - break; - - case HFP_W4_LIST_GENERIC_STATUS_INDICATORS: - context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS; - break; - - case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: - context->state = HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS; - break; - - case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); - break; - default: - break; - } -} static int hfp_hf_run_for_context_service_level_connection_queries(hfp_connection_t * context){ if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0; @@ -405,92 +344,58 @@ static int hfp_hf_run_for_context_service_level_connection_queries(hfp_connectio return done; } -static void hfp_hf_handle_ok_service_level_connection_queries(hfp_connection_t * context){ - if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return; - - if (context->enable_status_update_for_ag_indicators != 0xFF){ - context->enable_status_update_for_ag_indicators = 0xFF; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); - return; - }; - - if (context->change_status_update_for_individual_ag_indicators == 1){ - context->change_status_update_for_individual_ag_indicators = 0; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); - return; - } - - if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT){ - context->command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME; - return; - } - - if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME){ - hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator); - return; - } - if (context->enable_extended_audio_gateway_error_report){ - context->enable_extended_audio_gateway_error_report = 0; - return; - } -} - static int codecs_exchange_state_machine(hfp_connection_t * context){ + /* events ( == commands): + HFP_CMD_AVAILABLE_CODECS == received AT+BAC with list of codecs + HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP: + hf_trigger_codec_connection_setup == received BCC + ag_trigger_codec_connection_setup == received from AG to send BCS + HFP_CMD_HF_CONFIRMED_CODEC == received AT+BCS + */ + if (context->ok_pending) return 0; int done = 1; + printf(" -> State machine: CC\n"); - switch(context->command){ + switch (context->command){ case HFP_CMD_AVAILABLE_CODECS: - switch (context->codecs_state){ - case HFP_CODECS_W4_AG_COMMON_CODEC: - context->codec_confirmed = 0; - context->suggested_codec = 0; - context->negotiated_codec = 0; - break; - case HFP_CODECS_EXCHANGED: - context->negotiated_codec = 0; - context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC; - break; - default: - break; - } + if (context->codecs_state == HFP_CODECS_W4_AG_COMMON_CODEC) return 0; + + context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC; + context->ok_pending = 1; hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid); - break; + return 1; case HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP: + context->codec_confirmed = 0; + context->suggested_codec = 0; + context->negotiated_codec = 0; + context->codecs_state = HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE; + context->ok_pending = 1; hfp_hf_cmd_trigger_codec_connection_setup(context->rfcomm_cid); break; - case HFP_CMD_AG_SUGGESTED_CODEC: + case HFP_CMD_AG_SUGGESTED_CODEC: if (hfp_hf_supports_codec(context->suggested_codec)){ context->codec_confirmed = context->suggested_codec; + context->ok_pending = 1; + context->codecs_state = HFP_CODECS_HF_CONFIRMED_CODEC; hfp_hf_cmd_confirm_codec(context->rfcomm_cid, context->suggested_codec); } else { context->codec_confirmed = 0; context->suggested_codec = 0; context->negotiated_codec = 0; + context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC; + context->ok_pending = 1; hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid); + } break; - default: - return 0; - } - - if (done){ - context->ok_pending = 1; - } - return done; -} - -static void hfp_hf_handle_ok_codecs_connection(hfp_connection_t * context){ - // handle audio connection setup - switch (context->codecs_state){ - case HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE: - context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC; - break; + default: break; } + return 0; } static void hfp_run_for_context(hfp_connection_t * context){ @@ -519,12 +424,109 @@ static void hfp_run_for_context(hfp_connection_t * context){ } static void hfp_hf_switch_on_ok(hfp_connection_t *context){ - // printf("switch on ok\n"); context->ok_pending = 0; - - hfp_hf_handle_ok_service_level_connection_establishment(context); - hfp_hf_handle_ok_service_level_connection_queries(context); - hfp_hf_handle_ok_codecs_connection(context); + int done = 1; + switch (context->state){ + case HFP_W4_EXCHANGE_SUPPORTED_FEATURES: + if (has_codec_negotiation_feature(context)){ + context->state = HFP_NOTIFY_ON_CODECS; + break; + } + context->state = HFP_RETRIEVE_INDICATORS; + break; + + case HFP_W4_NOTIFY_ON_CODECS: + context->state = HFP_RETRIEVE_INDICATORS; + break; + + case HFP_W4_RETRIEVE_INDICATORS: + context->state = HFP_RETRIEVE_INDICATORS_STATUS; + break; + + case HFP_W4_RETRIEVE_INDICATORS_STATUS: + context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE; + break; + + case HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE: + if (has_call_waiting_and_3way_calling_feature(context)){ + context->state = HFP_RETRIEVE_CAN_HOLD_CALL; + break; + } + if (has_hf_indicators_feature(context)){ + context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; + break; + } + context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + break; + + case HFP_W4_RETRIEVE_CAN_HOLD_CALL: + if (has_hf_indicators_feature(context)){ + context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; + break; + } + context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + break; + + case HFP_W4_LIST_GENERIC_STATUS_INDICATORS: + context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS; + break; + + case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: + context->state = HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS; + break; + + case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + break; + case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: + if (context->enable_status_update_for_ag_indicators != 0xFF){ + context->enable_status_update_for_ag_indicators = 0xFF; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); + break; + } + + if (context->change_status_update_for_individual_ag_indicators == 1){ + context->change_status_update_for_individual_ag_indicators = 0; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); + break; + } + + if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT){ + context->command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME; + break; + } + + if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME){ + hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator); + break; + } + if (context->enable_extended_audio_gateway_error_report){ + context->enable_extended_audio_gateway_error_report = 0; + break; + } + printf(" CC received ok\n"); + + switch (context->codecs_state){ + case HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE: + context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC; + break; + case HFP_CODECS_HF_CONFIRMED_CODEC: + context->codecs_state = HFP_CODECS_EXCHANGED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE, 0); + break; + default: + done = 0; + break; + } + break; + default: + done = 0; + break; + } + // done context->command = HFP_CMD_NONE; } diff --git a/test/hfp/hfp_hf_client_test.c b/test/hfp/hfp_hf_client_test.c index 6f27a83e5..50f92590b 100644 --- a/test/hfp/hfp_hf_client_test.c +++ b/test/hfp/hfp_hf_client_test.c @@ -71,10 +71,18 @@ const uint8_t rfcomm_channel_nr = 1; static bd_addr_t device_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08}; static uint8_t codecs[2] = {1,2}; +static uint8_t default_codecs[2] = {1}; 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; +static uint8_t start_ringing = 0; +static uint8_t stop_ringing = 0; +static uint8_t call_termiated = 0; + +static int supported_features_with_codec_negotiation = 438; // 0001 1011 0110 +static int supported_features_without_codec_negotiation = 310; // 0001 0011 0110 int expected_rfcomm_command(const char * cmd){ char * ag_cmd = (char *)get_rfcomm_payload(); @@ -143,28 +151,37 @@ void packet_handler(uint8_t * event, uint16_t event_size){ case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED: printf("\n** SLC established **\n\n"); service_level_connection_established = 1; + codecs_connection_established = 0; + audio_connection_established = 0; break; case HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE: printf("\n** CC established **\n\n"); codecs_connection_established = 1; + audio_connection_established = 0; break; - case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: + printf("\n** SLC released **\n\n"); service_level_connection_established = 0; break; - - case HFP_SUBEVENT_COMPLETE: - printf("HFP_SUBEVENT_COMPLETE.\n\n"); + case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED: + printf("\n** AC established **\n\n"); + audio_connection_established = 1; break; - case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED: - printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]); + case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED: + printf("\n** AC released **\n\n"); + audio_connection_established = 0; break; - case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED: - printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]); + case HFP_SUBEVENT_START_RINGINIG: + printf("\n** Start ringing **\n\n"); + start_ringing = 1; break; - case HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR: - if (event[4]) - printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]); + case HFP_SUBEVENT_STOP_RINGINIG: + printf("\n** Stop ringing **\n\n"); + stop_ringing = 1; + start_ringing = 0; + break; + case HFP_SUBEVENT_CALL_TERMINATED: + call_termiated = 1; break; default: printf("event not handled %u\n", event[2]); @@ -179,14 +196,22 @@ TEST_GROUP(HFPClient){ void setup(void){ service_level_connection_established = 0; codecs_connection_established = 0; + audio_connection_established = 0; + start_ringing = 0; + stop_ringing = 0; + call_termiated = 0; + + hfp_hf_init(rfcomm_channel_nr, supported_features_with_codec_negotiation, indicators, sizeof(indicators)/sizeof(uint16_t), 1); + hfp_hf_set_codecs(codecs, sizeof(codecs)); } void teardown(void){ - if (service_level_connection_established){ - hfp_hf_release_service_level_connection(device_addr); - CHECK_EQUAL(service_level_connection_established, 0); - } + hfp_hf_release_audio_connection(device_addr); + hfp_hf_release_service_level_connection(device_addr); + + service_level_connection_established = 0; codecs_connection_established = 0; + audio_connection_established = 0; } void setup_hfp_service_level_connection(char ** test_steps, int nr_test_steps){ @@ -202,6 +227,22 @@ TEST_GROUP(HFPClient){ }; +TEST(HFPClient, HFAudioConnectionEstablishedWithoutCodecNegotiation){ + hfp_hf_init(rfcomm_channel_nr, supported_features_without_codec_negotiation, indicators, sizeof(indicators)/sizeof(uint16_t), 1); + hfp_hf_set_codecs(default_codecs, 1); + + setup_hfp_service_level_connection(hfp_slc_tests()[1].test, hfp_slc_tests()[1].len); + CHECK_EQUAL(service_level_connection_established, 1); + + setup_hfp_codecs_connection(default_cc_setup(), default_cc_setup_size()); + CHECK_EQUAL(codecs_connection_established, 1); + + // hfp_hf_establish_audio_connection(device_addr); + // CHECK_EQUAL(audio_connection_established, 1); + + // hfp_hf_release_audio_connection(device_addr); + // CHECK_EQUAL(audio_connection_established, 0); +} TEST(HFPClient, HFCodecsConnectionEstablished){ for (int i = 0; i < cc_tests_size(); i++){ @@ -231,9 +272,6 @@ TEST(HFPClient, HFServiceLevelConnectionEstablished){ 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)); hfp_hf_register_packet_handler(packet_handler); - return CommandLineTestRunner::RunAllTests(argc, argv); } From bf26b831dd080b430a201d2fecb50e0a7d328b1c Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 26 Nov 2015 11:55:35 +0100 Subject: [PATCH 15/72] hfp: fix parser --- src/hfp.c | 26 ++++++++++++++------------ test/hfp/hfp_ag_parser_test.c | 33 +++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index ae1a6b877..a59f415d3 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -897,15 +897,17 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ if (!hfp_parser_found_separator(context, byte)){ hfp_parser_store_byte(context, byte); return; - } - if (hfp_parser_is_end_of_line(byte)) { - if (hfp_parser_is_buffer_empty(context)){ - context->parser_state = HFP_PARSER_CMD_HEADER; + } + + if (context->command != HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){ + if (hfp_parser_is_end_of_line(byte)) { + if (hfp_parser_is_buffer_empty(context)){ + context->parser_state = HFP_PARSER_CMD_HEADER; + } } + if (hfp_parser_is_buffer_empty(context)) return; } - if (hfp_parser_is_buffer_empty(context)) return; - - + switch (context->parser_state){ case HFP_PARSER_CMD_HEADER: // header if (byte == '='){ @@ -950,7 +952,7 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ } break; - case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes + case HFP_PARSER_CMD_SEQUENCE: switch (context->command){ case HFP_CMD_SET_MICROPHONE_GAIN: value = atoi((char *)&context->line_buffer[0]); @@ -1046,7 +1048,7 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: // indicators are indexed starting with 1 context->parser_item_index = atoi((char *)&context->line_buffer[0]) - 1; - printf("Parsed status of the AG indicator %d, status ", context->parser_item_index); + log_info("Parsed status of the AG indicator %d, status ", context->parser_item_index); break; case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: context->network_operator.mode = atoi((char *)&context->line_buffer[0]); @@ -1078,11 +1080,11 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ case HFP_PARSER_SECOND_ITEM: switch (context->command){ case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: - printf("format %s, ", context->line_buffer); + log_info("format %s, ", context->line_buffer); context->network_operator.format = atoi((char *)&context->line_buffer[0]); break; case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: - printf("format %s \n", context->line_buffer); + log_info("format %s \n", context->line_buffer); context->network_operator.format = atoi((char *)&context->line_buffer[0]); break; case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: @@ -1092,7 +1094,7 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ break; case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: context->ag_indicators[context->parser_item_index].status = (uint8_t)atoi((char*)context->line_buffer); - printf("%d \n", context->ag_indicators[context->parser_item_index].status); + log_info("%d \n", context->ag_indicators[context->parser_item_index].status); context->ag_indicators[context->parser_item_index].status_changed = 1; break; case HFP_CMD_RETRIEVE_AG_INDICATORS: diff --git a/test/hfp/hfp_ag_parser_test.c b/test/hfp/hfp_ag_parser_test.c index 585296565..c4f88182e 100644 --- a/test/hfp/hfp_ag_parser_test.c +++ b/test/hfp/hfp_ag_parser_test.c @@ -167,19 +167,32 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){ CHECK_EQUAL(context.ag_indicators[pos].enabled, 0); } } +} - // sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n", - // HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); - // for (pos = 0; pos < strlen(packet); pos++){ - // hfp_parse(&context, packet[pos], 0); - // } +TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES){ + set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr); + context.ag_indicators_nr = hfp_ag_indicators_nr; + memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); + + for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ + CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index); + CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, hfp_ag_indicators[pos].enabled); + CHECK_EQUAL(context.ag_indicators[pos].index, hfp_ag_indicators[pos].index); + CHECK_EQUAL(context.ag_indicators[pos].enabled, hfp_ag_indicators[pos].enabled); + } + + sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n", + HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos], 0); + } - // CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command); + CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command); - // for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ - // CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1); - // CHECK_EQUAL(context.ag_indicators[pos].enabled, 1); - // } + for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ + CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1); + CHECK_EQUAL(context.ag_indicators[pos].enabled, 1); + } } TEST(HFPParser, HFP_AG_HF_QUERY_OPERATOR_SELECTION){ From c5fff38cd531632d789f2704f1f10cdd28f9758b Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 26 Nov 2015 12:14:06 +0100 Subject: [PATCH 16/72] hfp: add indicator value test, add audio con sm to hf --- src/hfp.c | 4 ++-- src/hfp_hf.c | 20 +++++++++++++++++++- test/hfp/hfp_ag_parser_test.c | 28 +++++++++++++++++++++++++++- test/hfp/hfp_hf_client_test.c | 8 +++----- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index a59f415d3..2adde34b6 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -1040,8 +1040,8 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ log_info("Parsed Enable AG indicator %u('%s'): %u\n", context->parser_item_index, context->ag_indicators[context->parser_item_index].name, value); } else { - log_info("Parsed Enable AG indicator %u('%s') - ignore (mandatory)\n", - context->parser_item_index, context->ag_indicators[context->parser_item_index].name); + log_info("Parsed Enable AG indicator %u('%s') - mandatory indicator, ignore value %u\n", + context->parser_item_index, context->ag_indicators[context->parser_item_index].name, value); } context->parser_item_index++; break; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 74f1700ab..ff04b2cc3 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -398,6 +398,24 @@ static int codecs_exchange_state_machine(hfp_connection_t * context){ return 0; } +static int hfp_hf_run_for_audio_connection(hfp_connection_t * context){ + if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED || + context->state > HFP_W2_DISCONNECT_SCO) return 0; + + + if (context->state == HFP_AUDIO_CONNECTION_ESTABLISHED && context->release_audio_connection){ + context->state = HFP_W4_SCO_DISCONNECTED; + context->release_audio_connection = 0; + gap_disconnect(context->sco_handle); + return 1; + } + + if (context->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; + + // run codecs exchange + return codecs_exchange_state_machine(context); +} + static void hfp_run_for_context(hfp_connection_t * context){ if (!context) return; if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return; @@ -407,7 +425,7 @@ static void hfp_run_for_context(hfp_connection_t * context){ done = hfp_hf_run_for_context_service_level_connection_queries(context); } if (!done){ - done = codecs_exchange_state_machine(context); + done = hfp_hf_run_for_audio_connection(context); } if (done) return; diff --git a/test/hfp/hfp_ag_parser_test.c b/test/hfp/hfp_ag_parser_test.c index c4f88182e..9ac142dfe 100644 --- a/test/hfp/hfp_ag_parser_test.c +++ b/test/hfp/hfp_ag_parser_test.c @@ -169,7 +169,7 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){ } } -TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES){ +TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES2){ set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr); context.ag_indicators_nr = hfp_ag_indicators_nr; memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); @@ -195,6 +195,32 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES){ } } +TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES1){ + set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr); + context.ag_indicators_nr = hfp_ag_indicators_nr; + memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); + + for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ + CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index); + CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, hfp_ag_indicators[pos].enabled); + CHECK_EQUAL(context.ag_indicators[pos].index, hfp_ag_indicators[pos].index); + CHECK_EQUAL(context.ag_indicators[pos].enabled, hfp_ag_indicators[pos].enabled); + } + + sprintf(packet, "\r\nAT%s=1,,,1,1,,1\r\n", + HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos], 0); + } + + CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command); + + for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ + CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1); + CHECK_EQUAL(context.ag_indicators[pos].enabled, 1); + } +} + TEST(HFPParser, HFP_AG_HF_QUERY_OPERATOR_SELECTION){ context.network_operator.format = 0xff; sprintf(packet, "\r\nAT%s=3,0\r\n", HFP_QUERY_OPERATOR_SELECTION); diff --git a/test/hfp/hfp_hf_client_test.c b/test/hfp/hfp_hf_client_test.c index 50f92590b..5bcb2e709 100644 --- a/test/hfp/hfp_hf_client_test.c +++ b/test/hfp/hfp_hf_client_test.c @@ -237,11 +237,9 @@ TEST(HFPClient, HFAudioConnectionEstablishedWithoutCodecNegotiation){ setup_hfp_codecs_connection(default_cc_setup(), default_cc_setup_size()); CHECK_EQUAL(codecs_connection_established, 1); - // hfp_hf_establish_audio_connection(device_addr); - // CHECK_EQUAL(audio_connection_established, 1); - - // hfp_hf_release_audio_connection(device_addr); - // CHECK_EQUAL(audio_connection_established, 0); + hfp_hf_establish_audio_connection(device_addr); + hfp_hf_release_audio_connection(device_addr); + CHECK_EQUAL(audio_connection_established, 0); } TEST(HFPClient, HFCodecsConnectionEstablished){ From 4ced71646d05aed5c2b6ffc9189003a84b3da60a Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 13:30:21 +0100 Subject: [PATCH 17/72] hfp: log indicator name and index --- src/hfp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index ae1a6b877..3ff1209e9 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -1035,10 +1035,10 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ value = atoi((char *)&context->line_buffer[0]); if (!context->ag_indicators[context->parser_item_index].mandatory){ context->ag_indicators[context->parser_item_index].enabled = value; - log_info("Parsed Enable AG indicator %u('%s'): %u\n", context->parser_item_index, + log_info("Parsed Enable AG indicator pos %u('%s'): %u\n", context->parser_item_index, context->ag_indicators[context->parser_item_index].name, value); } else { - log_info("Parsed Enable AG indicator %u('%s') - ignore (mandatory)\n", + log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n", context->parser_item_index, context->ag_indicators[context->parser_item_index].name); } context->parser_item_index++; From a0009460ff7a030e69f29024b47143f71a291d80 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 14:08:47 +0100 Subject: [PATCH 18/72] fix compile --- src/hfp_hf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 74f1700ab..c6badedb2 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -354,7 +354,6 @@ static int codecs_exchange_state_machine(hfp_connection_t * context){ */ if (context->ok_pending) return 0; - int done = 1; printf(" -> State machine: CC\n"); switch (context->command){ From a9fed164e42a2713a66714ecda998472d95a0c17 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 14:09:35 +0100 Subject: [PATCH 19/72] hfp: send ok on AT+CMER. Don't send AG indicator updates if status updates are disabled --- src/hfp_ag.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index bcada4635..47185d01f 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -658,8 +658,8 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio return 1; } case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: - printf("TODO\n"); - break; + hfp_ag_ok(context->rfcomm_cid); + return 1; default: break; } @@ -1288,6 +1288,10 @@ static void hfp_run_for_context(hfp_connection_t *context){ int i; for (i=0;iag_indicators_nr;i++){ if (get_bit(context->ag_indicators_status_update_bitmap, i)){ + if (!context->enable_status_update_for_ag_indicators) { + printf("+CMER:3,0,0,0 - not sending update for %s\n", hfp_ag_indicators[i].name); + break; + } context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, i, 0); hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, &hfp_ag_indicators[i]); return; From 95822773931c28cbbe8dd81387dc804ec8dba0de Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 26 Nov 2015 14:24:30 +0100 Subject: [PATCH 20/72] hfp: prepare hf answer inciming call --- src/hfp.h | 3 +++ src/hfp_hf.c | 74 +++++++++++++++++++++++++++++++++++++--------------- src/hfp_hf.h | 4 +++ 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index e375d691a..a20dd4ac7 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -501,6 +501,9 @@ typedef struct hfp_connection { int next_subscriber_number_to_send; int send_status_of_current_calls; + + uint8_t hf_answer_incoming_call; + timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index ff04b2cc3..eab130b90 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -73,6 +73,10 @@ static uint8_t hfp_indicators_status; static hfp_callback_t hfp_callback; +static hfp_call_status_t hfp_call_state; +static hfp_callsetup_status_t hfp_callsetup_state; +static hfp_callheld_status_t hfp_callheld_state; + void hfp_hf_register_packet_handler(hfp_callback_t callback){ hfp_callback = callback; if (callback == NULL){ @@ -226,6 +230,12 @@ static int hfp_hf_cmd_confirm_codec(uint16_t cid, uint8_t codec){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_cmd_ata(uint16_t cid){ + char buffer[10]; + sprintf(buffer, "%s\r\n", HFP_CALL_ANSWERED); + return send_str_over_rfcomm(cid, buffer); +} + static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; uint8_t event[6]; @@ -416,6 +426,15 @@ static int hfp_hf_run_for_audio_connection(hfp_connection_t * context){ return codecs_exchange_state_machine(context); } +static int call_setup_state_machine(hfp_connection_t * context){ + if (context->hf_answer_incoming_call){ + hfp_hf_cmd_ata(context->rfcomm_cid); + context->hf_answer_incoming_call = 0; + return 1; + } + return 0; +} + static void hfp_run_for_context(hfp_connection_t * context){ if (!context) return; if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return; @@ -427,7 +446,10 @@ static void hfp_run_for_context(hfp_connection_t * context){ if (!done){ done = hfp_hf_run_for_audio_connection(context); } - + if (!done){ + done = call_setup_state_machine(context); + } + if (done) return; // deal with disconnect switch (context->state){ @@ -559,31 +581,32 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 //printf("\nHF received: %s", packet+2); for (pos = 0; pos < size ; pos++){ hfp_parse(context, packet[pos], 1); - + } // emit indicators status changed - for (i = 0; i < context->ag_indicators_nr; i++){ - if (context->ag_indicators[i].status_changed) { - context->ag_indicators[i].status_changed = 0; - hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]); - break; - } + for (i = 0; i < context->ag_indicators_nr; i++){ + if (context->ag_indicators[i].status_changed) { + context->ag_indicators[i].status_changed = 0; + hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]); + break; } + } - if (context->command == HFP_CMD_ERROR){ - context->ok_pending = 0; - hfp_reset_context_flags(context); - hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1); - return; - } - if (context->command == HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR){ + switch (context->command){ + case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR: context->ok_pending = 0; context->extended_audio_gateway_error = 0; hfp_emit_event(hfp_callback, HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR, context->extended_audio_gateway_error); - return; - } - - if (context->command != HFP_CMD_OK) continue; - hfp_hf_switch_on_ok(context); + break; + case HFP_CMD_ERROR: + context->ok_pending = 0; + hfp_reset_context_flags(context); + hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1); + break; + case HFP_CMD_OK: + hfp_hf_switch_on_ok(context); + break; + default: + break; } } @@ -733,4 +756,13 @@ void hfp_hf_release_audio_connection(bd_addr_t bd_addr){ hfp_run_for_context(connection); } - +void hfp_hf_answer_incoming_call(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 (hfp_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ + connection->hf_answer_incoming_call = 1; + } else { + log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_state); + } +} diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 9529c3ab5..1582690a1 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -148,6 +148,10 @@ void hfp_hf_establish_audio_connection(bd_addr_t bd_addr); */ void hfp_hf_release_audio_connection(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_hf_answer_incoming_call(bd_addr_t bd_addr); /* API_END */ From c069e696742db5df6bea5c935bbc2dcaad1156b8 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 14:26:51 +0100 Subject: [PATCH 21/72] hfp: +BIA empty fields are -ignore- --- src/hfp.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index 3b58050b9..78a136fb0 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -1034,14 +1034,18 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ break; case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE: // AG parses new gen. ind. state - value = atoi((char *)&context->line_buffer[0]); - if (!context->ag_indicators[context->parser_item_index].mandatory){ + if (context->line_size<1){ + log_info("Parsed Enable AG indicator pos %u('%s') - unchanged\n", context->parser_item_index, + context->ag_indicators[context->parser_item_index].name); + } + else if (context->ag_indicators[context->parser_item_index].mandatory){ + log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n", + context->parser_item_index, context->ag_indicators[context->parser_item_index].name); + } else { + value = atoi((char *)&context->line_buffer[0]); context->ag_indicators[context->parser_item_index].enabled = value; log_info("Parsed Enable AG indicator pos %u('%s'): %u\n", context->parser_item_index, context->ag_indicators[context->parser_item_index].name, value); - } else { - log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n", - context->parser_item_index, context->ag_indicators[context->parser_item_index].name); } context->parser_item_index++; break; From 2ba203686e1ad6b329e6e905db33ee6c9fdbaaaf Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 14:58:31 +0100 Subject: [PATCH 22/72] hfp: answer +CIND after SLC established, too --- src/hfp.c | 1 + src/hfp.h | 3 +++ src/hfp_ag.c | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/src/hfp.c b/src/hfp.c index 78a136fb0..8d02a2d7e 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -695,6 +695,7 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ if (strncmp(line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){ + printf("HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS\n"); return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS; } diff --git a/src/hfp.h b/src/hfp.h index e375d691a..aa1121a3e 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -501,6 +501,9 @@ typedef struct hfp_connection { int next_subscriber_number_to_send; int send_status_of_current_calls; + + int send_ag_status_indicators; + timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 47185d01f..c86e09872 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1347,6 +1347,12 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } + if (context->send_ag_status_indicators){ + context->send_ag_status_indicators = 0; + hfp_ag_retrieve_indicators_cmd(context->rfcomm_cid, context); + return; + } + int done = hfp_ag_run_for_context_service_level_connection(context); if (!done){ done = hfp_ag_run_for_context_service_level_connection_queries(context); @@ -1384,6 +1390,11 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ hfp_parse(context, packet[pos], 0); } switch(context->command){ + case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: + // expected by SLC state machine + if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) break; + context->send_ag_status_indicators = 1; + break; case HFP_CMD_LIST_CURRENT_CALLS: context->command = HFP_CMD_NONE; context->send_status_of_current_calls = 1; From 11a5c01e22bb33048538a3523cf807cdddfa2f35 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 16:02:36 +0100 Subject: [PATCH 23/72] hfp: support HF indicator updates by AT+BIEV --- src/hfp.c | 24 +++++++++++++++++++----- src/hfp.h | 10 +++++++--- src/hfp_ag.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index 8d02a2d7e..826e55656 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -693,9 +693,22 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ return HFP_CMD_SUPPORTED_FEATURES; } + if (strncmp(line_buffer+offset, HFP_TRANSFER_HF_INDICATOR_STATUS, strlen(HFP_TRANSFER_HF_INDICATOR_STATUS)) == 0){ + return HFP_CMD_HF_INDICATOR_STATUS; + } + + if (strncmp(line_buffer+offset, HFP_RESPONSE_AND_HOLD, strlen(HFP_RESPONSE_AND_HOLD)) == 0){ + if (strncmp(line_buffer+strlen(HFP_RESPONSE_AND_HOLD)+offset, "?", 1) == 0){ + return HFP_CMD_RESPONSE_AND_HOLD_QUERY; + } + + if (strncmp(line_buffer+strlen(HFP_RESPONSE_AND_HOLD)+offset, "=", 1) == 0){ + return HFP_CMD_RESPONSE_AND_HOLD_COMMAND; + } + } + if (strncmp(line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){ - printf("HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS\n"); return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS; } @@ -851,12 +864,9 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){ case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: - context->parser_state = HFP_PARSER_SECOND_ITEM; - break; case HFP_CMD_RETRIEVE_AG_INDICATORS: - context->parser_state = HFP_PARSER_SECOND_ITEM; - break; case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE: + case HFP_CMD_HF_INDICATOR_STATUS: context->parser_state = HFP_PARSER_SECOND_ITEM; break; default: @@ -1033,6 +1043,10 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ log_info("Parsed List generic status indicator %s state: ", context->line_buffer); context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer); break; + case HFP_CMD_HF_INDICATOR_STATUS: + context->parser_indicator_index = (uint8_t)atoi((char*)context->line_buffer); + log_info("Parsed HF indicator index %u", context->parser_indicator_index); + break; case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE: // AG parses new gen. ind. state if (context->line_size<1){ diff --git a/src/hfp.h b/src/hfp.h index aa1121a3e..d6f136b75 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -114,6 +114,7 @@ extern "C" { #define HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES "+CHLD" #define HFP_GENERIC_STATUS_INDICATOR "+BIND" #define HFP_TRANSFER_AG_INDICATOR_STATUS "+CIEV" // +CIEV: , +#define HFP_TRANSFER_HF_INDICATOR_STATUS "+BIEV" // +BIEC: , #define HFP_QUERY_OPERATOR_SELECTION "+COPS" // +COPS: ,0, #define HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR "+CMEE" #define HFP_EXTENDED_AUDIO_GATEWAY_ERROR "+CME ERROR" @@ -132,7 +133,7 @@ extern "C" { #define HFP_TRANSMIT_DTMF_CODES "+VTS" #define HFP_SUBSCRIBER_NUMBER_INFORMATION "+CNUM" #define HFP_LIST_CURRENT_CALLS "+CLCC" - +#define HFP_RESPONSE_AND_HOLD "+BTRH" #define HFP_OK "OK" #define HFP_ERROR "ERROR" @@ -189,8 +190,10 @@ typedef enum { HFP_CMD_SET_MICROPHONE_GAIN, HFP_CMD_SET_SPEAKER_GAIN, HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION, - HFP_CMD_LIST_CURRENT_CALLS - + HFP_CMD_LIST_CURRENT_CALLS, + HFP_CMD_RESPONSE_AND_HOLD_QUERY, + HFP_CMD_RESPONSE_AND_HOLD_COMMAND, + HFP_CMD_HF_INDICATOR_STATUS } hfp_command_t; @@ -435,6 +438,7 @@ typedef struct hfp_connection { hfp_command_t command; hfp_parser_state_t parser_state; int parser_item_index; + int parser_indicator_index; uint8_t line_buffer[HFP_MAX_INDICATOR_DESC_SIZE]; int line_size; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index c86e09872..e63cf7170 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1381,6 +1381,16 @@ static void hfp_run_for_context(hfp_connection_t *context){ context->command = HFP_CMD_NONE; } } +static hfp_generic_status_indicator_t *get_hf_indicator_by_number(int number){ + int i; + for (i=0;i< get_hfp_generic_status_indicators_nr();i++){ + hfp_generic_status_indicator_t * indicator = &get_hfp_generic_status_indicators()[i]; + if (indicator->uuid == number){ + return indicator; + } + } + return NULL; +} static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ hfp_connection_t * context = get_hfp_connection_context_for_rfcomm_cid(channel); @@ -1389,7 +1399,39 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ for (pos = 0; pos < size ; pos++){ hfp_parse(context, packet[pos], 0); } + hfp_generic_status_indicator_t * indicator; + int value; switch(context->command){ + case HFP_CMD_HF_INDICATOR_STATUS: + context->command = HFP_CMD_NONE; + // find indicator by assigned number + indicator = get_hf_indicator_by_number(context->parser_indicator_index); + if (!indicator){ + context->send_error = 1; + break; + } + value = atoi((char *)&context->line_buffer[0]); + switch (indicator->uuid){ + case 1: // enhanced security + if (value > 1) { + context->send_error = 1; + return; + } + printf("HF Indicator 'enhanced security' set to %u\n", value); + break; + case 2: // battery level + if (value > 100){ + context->send_error = 1; + return; + } + printf("HF Indicator 'battery' set to %u\n", value); + break; + default: + printf("HF Indicator unknown set to %u\n", value); + break; + } + context->ok_pending = 1; + break; case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: // expected by SLC state machine if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) break; From 3e2ef1ed5ed9602838a887e39dfec67e3537689e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 16:55:15 +0100 Subject: [PATCH 24/72] hfp: Response and Hold by AG --- src/hfp.h | 11 +++++ src/hfp_ag.c | 95 ++++++++++++++++++++++++++++++++++++++++++ src/hfp_ag.h | 18 ++++++++ test/pts/hfp_ag_test.c | 15 +++++++ 4 files changed, 139 insertions(+) diff --git a/src/hfp.h b/src/hfp.h index d6f136b75..4ec05374b 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -266,6 +266,9 @@ typedef enum { HFP_AG_TERMINATE_CALL_BY_AG, HFP_AG_TERMINATE_CALL_BY_HF, HFP_AG_CALL_DROPPED, + HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG, + HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG, + HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG, } hfp_ag_call_event_t; @@ -378,6 +381,12 @@ typedef enum{ HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL } hfp_enhanced_call_mpty_t; +typedef enum { + HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD = 0, + HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED, + HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED +} hfp_response_and_hold_state_t; + typedef enum{ HFP_NONE_SM, HFP_SLC_SM, @@ -507,6 +516,8 @@ typedef struct hfp_connection { int send_status_of_current_calls; int send_ag_status_indicators; + int send_response_and_hold_active; + int send_response_and_hold_status; timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index e63cf7170..4cce98b71 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -79,6 +79,8 @@ static hfp_callback_t hfp_callback; static hfp_call_status_t hfp_ag_call_state; static hfp_callsetup_status_t hfp_ag_callsetup_state; static hfp_callheld_status_t hfp_ag_callheld_state; +static hfp_response_and_hold_state_t hfp_ag_response_and_hold_state; +static int hfp_ag_response_and_hold_active = 0; // CLIP feature static uint8_t clip_type; // 0 == not set @@ -431,6 +433,12 @@ static int hfp_ag_set_microphone_gain_cmd(uint16_t cid, uint8_t gain){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_ag_set_response_and_hold(uint16_t cid, int state){ + char buffer[30]; + sprintf(buffer, "\r\n%s: %d\r\n", HFP_RESPONSE_AND_HOLD, state); + return send_str_over_rfcomm(cid, buffer); +} + static uint8_t hfp_ag_suggest_codec(hfp_connection_t *context){ int i,j; @@ -956,6 +964,15 @@ static hfp_connection_t * hfp_ag_connection_for_call_state(hfp_call_state_t call return NULL; } +static void hfp_ag_send_response_and_hold_state(void){ + linked_list_iterator_t it; + linked_list_iterator_init(&it, hfp_get_connections()); + while (linked_list_iterator_has_next(&it)){ + hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it); + connection->send_response_and_hold_status = 1; + } +} + static int call_setup_state_machine(hfp_connection_t * connection){ int indicator_index; switch (connection->call_state){ @@ -1083,6 +1100,48 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect } break; + case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG: + // clear CLIP + clip_type = 0; + switch (hfp_ag_call_state){ + case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: + switch (hfp_ag_callsetup_state){ + case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: + hfp_ag_response_and_hold_active = 1; + hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD; + hfp_ag_send_response_and_hold_state(); + // as with regualr call + hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT); + hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + hfp_ag_ag_accept_call(); + printf("AG response and hold - hold by AG\n"); + break; + default: + break; + } + break; + default: + break; + } + break; + + case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG: + if (!hfp_ag_response_and_hold_active) break; + if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; + hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED; + hfp_ag_send_response_and_hold_state(); + break; + + case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG: + if (!hfp_ag_response_and_hold_active) break; + if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; + hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED; + hfp_ag_send_response_and_hold_state(); + // from terminate by ag + hfp_ag_set_call_state(HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS); + hfp_ag_trigger_terminate_call(); + break; + case HFP_AG_TERMINATE_CALL_BY_HF: // clear CLIP clip_type = 0; @@ -1247,6 +1306,7 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect hfp_ag_transfer_callheld_state(); } break; + default: break; } @@ -1283,6 +1343,14 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } + // note: before update AG indicators in case of HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED + if (context->send_response_and_hold_status){ + context->send_response_and_hold_status = 0; + hfp_ag_set_response_and_hold(context->rfcomm_cid, hfp_ag_response_and_hold_state); + return; + } + + // update AG indicators if (context->ag_indicators_status_update_bitmap){ int i; @@ -1353,6 +1421,13 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } + if (context->send_response_and_hold_active){ + context->send_response_and_hold_active = 0; + hfp_ag_set_response_and_hold(context->rfcomm_cid, 0); + context->ok_pending = 1; + return; + } + int done = hfp_ag_run_for_context_service_level_connection(context); if (!done){ done = hfp_ag_run_for_context_service_level_connection_queries(context); @@ -1402,6 +1477,13 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ hfp_generic_status_indicator_t * indicator; int value; switch(context->command){ + case HFP_CMD_RESPONSE_AND_HOLD_QUERY: + if (hfp_ag_response_and_hold_active){ + context->send_response_and_hold_active = 1; + } else { + context->ok_pending = 1; + } + break; case HFP_CMD_HF_INDICATOR_STATUS: context->command = HFP_CMD_NONE; // find indicator by assigned number @@ -1779,6 +1861,19 @@ void hfp_ag_outgoing_call_rejected(void){ void hfp_ag_outgoing_call_accepted(void){ hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_ACCEPTED, NULL); } + +void hfp_ag_hold_incoming_call(void){ + hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG, NULL); +} + +void hfp_ag_accept_held_incoming_call(void) { + hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG, NULL); +} + +void hfp_ag_reject_held_incoming_call(void){ + hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG, NULL); +} + static void hfp_ag_set_ag_indicator(const char * name, int value){ int indicator_index = get_ag_indicator_index_for_name(name); if (indicator_index < 0) return; diff --git a/src/hfp_ag.h b/src/hfp_ag.h index 7cd5c027a..863b2c40d 100644 --- a/src/hfp_ag.h +++ b/src/hfp_ag.h @@ -287,8 +287,26 @@ void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_ca hfp_enhanced_call_status_t status, hfp_enhanced_call_mode_t mode, hfp_enhanced_call_mpty_t mpty, uint8_t type, const char * number); +/* + * @brief + */ void hfp_ag_send_current_call_status_done(bd_addr_t bd_addr); +/* + * @brief + */ +void hfp_ag_hold_incoming_call(void); + +/* + * @brief + */ +void hfp_ag_accept_held_incoming_call(void); + +/* + * @brief + */ +void hfp_ag_reject_held_incoming_call(void); + /* API_END */ #if defined __cplusplus diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index eae35fe00..85a86216f 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -339,6 +339,9 @@ static void show_usage(void){ printf("t - terminate connection\n"); printf("u - join held call\n"); printf("v - discover nearby HF units\n"); + printf("w - put incoming call on hold (Response and Hold)\n"); + printf("x - accept held incoming call (Response and Hold)\n"); + printf("X - reject held incoming call (Response and Hold)\n"); printf("---\n"); printf("Ctrl-c - exit\n"); @@ -524,6 +527,18 @@ static int stdin_process(struct data_source *ds){ case 'v': start_scan(); break; + case 'w': + printf("AG: Put incoming call on hold (Response and Hold)\n"); + hfp_ag_hold_incoming_call(); + break; + case 'x': + printf("AG: Accept held incoming call (Response and Hold)\n"); + hfp_ag_accept_held_incoming_call(); + break; + case 'X': + printf("AG: Reject held incoming call (Response and Hold)\n"); + hfp_ag_reject_held_incoming_call(); + break; default: show_usage(); break; From b7cfbced6a345957f7dc5c8c561838a1f5572be1 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 17:26:16 +0100 Subject: [PATCH 25/72] hfp: response and hold from HF --- src/hfp.h | 3 ++ src/hfp_ag.c | 78 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index 4ec05374b..6d628a91f 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -269,6 +269,9 @@ typedef enum { HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG, HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG, HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG, + HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF, + HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF, + HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF, } hfp_ag_call_event_t; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 4cce98b71..11de245f2 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1124,8 +1124,34 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; } break; - + + case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF: + // clear CLIP + clip_type = 0; + switch (hfp_ag_call_state){ + case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: + switch (hfp_ag_callsetup_state){ + case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: + hfp_ag_response_and_hold_active = 1; + hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD; + hfp_ag_send_response_and_hold_state(); + // as with regualr call + hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT); + hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + hfp_ag_hf_accept_call(connection); + printf("AG response and hold - hold by HF\n"); + break; + default: + break; + } + break; + default: + break; + } + break; + case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG: + case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF: if (!hfp_ag_response_and_hold_active) break; if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED; @@ -1133,6 +1159,7 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG: + case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF: if (!hfp_ag_response_and_hold_active) break; if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED; @@ -1329,13 +1356,6 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } - if (context->ok_pending){ - context->ok_pending = 0; - context->command = HFP_CMD_NONE; - hfp_ag_ok(context->rfcomm_cid); - return; - } - if (context->send_error){ context->send_error = 0; context->command = HFP_CMD_NONE; @@ -1343,13 +1363,25 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } - // note: before update AG indicators in case of HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED + // note: before update AG indicators and ok_pending if (context->send_response_and_hold_status){ context->send_response_and_hold_status = 0; hfp_ag_set_response_and_hold(context->rfcomm_cid, hfp_ag_response_and_hold_state); return; } + if (context->send_response_and_hold_active){ + context->send_response_and_hold_active = 0; + hfp_ag_set_response_and_hold(context->rfcomm_cid, 0); + return; + } + + if (context->ok_pending){ + context->ok_pending = 0; + context->command = HFP_CMD_NONE; + hfp_ag_ok(context->rfcomm_cid); + return; + } // update AG indicators if (context->ag_indicators_status_update_bitmap){ @@ -1421,13 +1453,6 @@ static void hfp_run_for_context(hfp_connection_t *context){ return; } - if (context->send_response_and_hold_active){ - context->send_response_and_hold_active = 0; - hfp_ag_set_response_and_hold(context->rfcomm_cid, 0); - context->ok_pending = 1; - return; - } - int done = hfp_ag_run_for_context_service_level_connection(context); if (!done){ done = hfp_ag_run_for_context_service_level_connection_queries(context); @@ -1480,9 +1505,26 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ case HFP_CMD_RESPONSE_AND_HOLD_QUERY: if (hfp_ag_response_and_hold_active){ context->send_response_and_hold_active = 1; - } else { - context->ok_pending = 1; } + context->ok_pending = 1; + break; + case HFP_CMD_RESPONSE_AND_HOLD_COMMAND: + value = atoi((char *)&context->line_buffer[0]); + printf("HF Response and Hold: %u\n", value); + switch(value){ + case HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD: + hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF, context); + break; + case HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED: + hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF, context); + break; + case HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED: + hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF, context); + break; + default: + break; + } + context->ok_pending = 1; break; case HFP_CMD_HF_INDICATOR_STATUS: context->command = HFP_CMD_NONE; From 2d07c58eadf37116afcd57a457f7cd2a8c37687e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 18:26:05 +0100 Subject: [PATCH 26/72] hfp: fix HF response and hold commands --- src/hfp_ag.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 11de245f2..989685709 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1151,15 +1151,16 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG: - case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF: + case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF: if (!hfp_ag_response_and_hold_active) break; if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED; hfp_ag_send_response_and_hold_state(); + printf("Held Call accepted and active\n"); break; case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG: - case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF: + case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF: if (!hfp_ag_response_and_hold_active) break; if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED; From 4ac00ef135c10fd083a59048729c2c0865815909 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 20:59:02 +0100 Subject: [PATCH 27/72] hfp: send HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED on dropped call --- src/hfp_ag.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 989685709..cdd8e7771 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -1245,6 +1245,10 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect hfp_ag_transfer_callsetup_state(); break; case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: + if (hfp_ag_response_and_hold_active) { + hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED; + hfp_ag_send_response_and_hold_state(); + } hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); hfp_ag_set_call_state(HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS); hfp_ag_trigger_terminate_call(); From 65e641b0cc199282b2551e848ebce07e73ebfa37 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 21:35:31 +0100 Subject: [PATCH 28/72] hfp: release audio connection for dropped call --- src/hfp_ag.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index cdd8e7771..eaef44341 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -911,6 +911,7 @@ static void hfp_ag_trigger_terminate_call(void){ if (connection->call_state == HFP_CALL_IDLE) continue; connection->call_state = HFP_CALL_IDLE; connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); + connection->release_audio_connection = 1; hfp_run_for_context(connection); } hfp_emit_event(hfp_callback, HFP_SUBEVENT_CALL_TERMINATED, 0); @@ -1393,11 +1394,11 @@ static void hfp_run_for_context(hfp_connection_t *context){ int i; for (i=0;iag_indicators_nr;i++){ if (get_bit(context->ag_indicators_status_update_bitmap, i)){ + context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, i, 0); if (!context->enable_status_update_for_ag_indicators) { - printf("+CMER:3,0,0,0 - not sending update for %s\n", hfp_ag_indicators[i].name); + log_info("+CMER:3,0,0,0 - not sending update for '%s'", hfp_ag_indicators[i].name); break; } - context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, i, 0); hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, &hfp_ag_indicators[i]); return; } From 7bb7c9678ab573751595bc36e0573e71d7b61907 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 21:58:30 +0100 Subject: [PATCH 29/72] hfp: start ringing on new SLC if incoming call in progress --- src/hfp_ag.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index eaef44341..74a4cfe19 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -93,6 +93,7 @@ static int subscriber_numbers_count = 0; static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void hfp_run_for_context(hfp_connection_t *context); static void hfp_ag_setup_audio_connection(hfp_connection_t * connection); +static void hfp_ag_hf_start_ringing(hfp_connection_t * context); hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(); int get_hfp_generic_status_indicators_nr(); @@ -535,6 +536,11 @@ static void hfp_ag_slc_established(hfp_connection_t * context){ if (hfp_ag_call_state == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE; } + // if AG is ringing, also start ringing on the HF + if (hfp_ag_call_state == HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS && + hfp_ag_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ + hfp_ag_hf_start_ringing(context); + } } static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * context){ @@ -593,10 +599,11 @@ static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * co if (context->state != HFP_W4_RETRIEVE_CAN_HOLD_CALL) break; if (has_hf_indicators_feature(context)){ context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; - } else { - hfp_ag_slc_established(context); } hfp_ag_retrieve_can_hold_call_cmd(context->rfcomm_cid); + if (!has_hf_indicators_feature(context)){ + hfp_ag_slc_established(context); + } return 1; case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: From e74491ddf8a1cca771532cd314803bc82b11b831 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 26 Nov 2015 22:05:31 +0100 Subject: [PATCH 30/72] fix parser to ignore missing values --- src/hfp.c | 312 ++++++++++++++++++---------------- src/hfp.h | 4 +- test/hfp/hfp_ag_parser_test.c | 33 ++-- 3 files changed, 192 insertions(+), 157 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index 78a136fb0..d981797c6 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -101,6 +101,7 @@ static int hfp_generic_status_indicators_nr = 0; static hfp_generic_status_indicator_t hfp_generic_status_indicators[HFP_MAX_NUM_HF_INDICATORS]; static linked_list_t hfp_connections = NULL; +static void parse_sequence(hfp_connection_t * context); hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(void){ return (hfp_generic_status_indicator_t *) &hfp_generic_status_indicators; @@ -272,7 +273,7 @@ void hfp_reset_context_flags(hfp_connection_t * context){ context->ok_pending = 0; context->send_error = 0; - context->keep_separator = 0; + context->keep_byte = 0; context->change_status_update_for_individual_ag_indicators = 0; context->operator_name_changed = 0; @@ -822,7 +823,7 @@ static int hfp_parser_is_end_of_header(uint8_t byte){ } static int hfp_parser_found_separator(hfp_connection_t * context, uint8_t byte){ - if (context->keep_separator == 1) return 1; + if (context->keep_byte == 1) return 1; int found_separator = byte == ',' || byte == '\n'|| byte == '\r'|| byte == ')' || byte == '(' || byte == ':' || @@ -840,9 +841,9 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){ switch (context->parser_state){ case HFP_PARSER_CMD_HEADER: context->parser_state = HFP_PARSER_CMD_SEQUENCE; - if (context->keep_separator == 1){ + if (context->keep_byte == 1){ hfp_parser_store_byte(context, byte); - context->keep_separator = 0; + context->keep_byte = 0; } break; case HFP_PARSER_CMD_SEQUENCE: @@ -893,37 +894,49 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ // TODO: handle space inside word if (byte == ' ' && context->parser_state > HFP_PARSER_CMD_HEADER) return; + + if (byte == ',' && context->command == HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){ + if (context->line_size == 0){ + context->line_buffer[0] = 0; + context->ignore_value = 1; + parse_sequence(context); + return; + } + } if (!hfp_parser_found_separator(context, byte)){ hfp_parser_store_byte(context, byte); return; } - if (context->command != HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){ - if (hfp_parser_is_end_of_line(byte)) { - if (hfp_parser_is_buffer_empty(context)){ - context->parser_state = HFP_PARSER_CMD_HEADER; - } + if (hfp_parser_is_end_of_line(byte)) { + if (hfp_parser_is_buffer_empty(context)){ + context->parser_state = HFP_PARSER_CMD_HEADER; } - if (hfp_parser_is_buffer_empty(context)) return; } - + if (hfp_parser_is_buffer_empty(context)) return; + switch (context->parser_state){ case HFP_PARSER_CMD_HEADER: // header if (byte == '='){ - context->keep_separator = 1; + context->keep_byte = 1; hfp_parser_store_byte(context, byte); return; } if (byte == '?'){ - context->keep_separator = 0; + context->keep_byte = 0; hfp_parser_store_byte(context, byte); return; } - // printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator); - if (hfp_parser_is_end_of_header(byte) || context->keep_separator == 1){ - // printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator); + + if (byte == ','){ + context->resolve_byte = 1; + } + + // printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_byte); + if (hfp_parser_is_end_of_header(byte) || context->keep_byte == 1){ + // printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_byte); char * line_buffer = (char *)context->line_buffer; context->command = parse_command(line_buffer, isHandsFree); @@ -953,134 +966,8 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ break; case HFP_PARSER_CMD_SEQUENCE: - switch (context->command){ - case HFP_CMD_SET_MICROPHONE_GAIN: - value = atoi((char *)&context->line_buffer[0]); - context->microphone_gain = value; - log_info("hfp parse HFP_CMD_SET_MICROPHONE_GAIN %d\n", value); - break; - case HFP_CMD_SET_SPEAKER_GAIN: - value = atoi((char *)&context->line_buffer[0]); - context->speaker_gain = value; - log_info("hfp parse HFP_CMD_SET_SPEAKER_GAIN %d\n", value); - break; - case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION: - value = atoi((char *)&context->line_buffer[0]); - context->ag_activate_voice_recognition = value; - log_info("hfp parse HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION %d\n", value); - break; - case HFP_CMD_TURN_OFF_EC_AND_NR: - value = atoi((char *)&context->line_buffer[0]); - context->ag_echo_and_noise_reduction = value; - log_info("hfp parse HFP_CMD_TURN_OFF_EC_AND_NR %d\n", value); - break; - case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING: - value = atoi((char *)&context->line_buffer[0]); - context->remote_supported_features = store_bit(context->remote_supported_features, HFP_AGSF_IN_BAND_RING_TONE, value); - log_info("hfp parse HFP_CHANGE_IN_BAND_RING_TONE_SETTING %d\n", value); - break; - case HFP_CMD_HF_CONFIRMED_CODEC: - context->codec_confirmed = atoi((char*)context->line_buffer); - log_info("hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d\n", context->codec_confirmed); - break; - case HFP_CMD_AG_SUGGESTED_CODEC: - context->suggested_codec = atoi((char*)context->line_buffer); - log_info("hfp parse HFP_CMD_AG_SUGGESTED_CODEC %d\n", context->suggested_codec); - break; - case HFP_CMD_SUPPORTED_FEATURES: - context->remote_supported_features = atoi((char*)context->line_buffer); - log_info("Parsed supported feature %d\n", context->remote_supported_features); - break; - case HFP_CMD_AVAILABLE_CODECS: - log_info("Parsed codec %s\n", context->line_buffer); - context->remote_codecs[context->parser_item_index] = (uint16_t)atoi((char*)context->line_buffer); - context->parser_item_index++; - context->remote_codecs_nr = context->parser_item_index; - break; - case HFP_CMD_RETRIEVE_AG_INDICATORS: - strcpy((char *)context->ag_indicators[context->parser_item_index].name, (char *)context->line_buffer); - context->ag_indicators[context->parser_item_index].index = context->parser_item_index+1; - log_info("Indicator %d: %s (", context->ag_indicators_nr+1, context->line_buffer); - break; - case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: - log_info("Parsed Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer); - context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer); - context->parser_item_index++; - break; - case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: - context->parser_item_index++; - if (context->parser_item_index != 4) break; - log_info("Parsed Enable indicators: %s\n", context->line_buffer); - value = atoi((char *)&context->line_buffer[0]); - context->enable_status_update_for_ag_indicators = (uint8_t) value; - break; - case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES: - log_info("Parsed Support call hold: %s\n", context->line_buffer); - if (context->line_size > 2 ) break; - strcpy((char *)context->remote_call_services[context->remote_call_services_nr].name, (char *)context->line_buffer); - context->remote_call_services_nr++; - break; - case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: - case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS: - log_info("Parsed Generic status indicator: %s\n", context->line_buffer); - context->generic_status_indicators[context->parser_item_index].uuid = (uint16_t)atoi((char*)context->line_buffer); - context->parser_item_index++; - context->generic_status_indicators_nr = context->parser_item_index; - break; - case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE: - // HF parses inital AG gen. ind. state - log_info("Parsed List generic status indicator %s state: ", context->line_buffer); - context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer); - break; - case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE: - // AG parses new gen. ind. state - if (context->line_size<1){ - log_info("Parsed Enable AG indicator pos %u('%s') - unchanged\n", context->parser_item_index, - context->ag_indicators[context->parser_item_index].name); - } - else if (context->ag_indicators[context->parser_item_index].mandatory){ - log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n", - context->parser_item_index, context->ag_indicators[context->parser_item_index].name); - } else { - value = atoi((char *)&context->line_buffer[0]); - context->ag_indicators[context->parser_item_index].enabled = value; - log_info("Parsed Enable AG indicator pos %u('%s'): %u\n", context->parser_item_index, - context->ag_indicators[context->parser_item_index].name, value); - } - context->parser_item_index++; - break; - case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: - // indicators are indexed starting with 1 - context->parser_item_index = atoi((char *)&context->line_buffer[0]) - 1; - log_info("Parsed status of the AG indicator %d, status ", context->parser_item_index); - break; - case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: - context->network_operator.mode = atoi((char *)&context->line_buffer[0]); - log_info("Parsed network operator mode: %d, ", context->network_operator.mode); - break; - case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: - if (context->line_buffer[0] == '3'){ - log_info("Parsed Set network operator format : %s, ", context->line_buffer); - break; - } - // TODO emit ERROR, wrong format - log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer); - break; - case HFP_CMD_ERROR: - break; - case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR: - context->extended_audio_gateway_error = (uint8_t)atoi((char*)context->line_buffer); - break; - case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR: - context->enable_extended_audio_gateway_error_report = (uint8_t)atoi((char*)context->line_buffer); - context->ok_pending = 1; - context->extended_audio_gateway_error = 0; - break; - default: - break; - } + parse_sequence(context); break; - case HFP_PARSER_SECOND_ITEM: switch (context->command){ case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: @@ -1128,6 +1015,145 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ break; } hfp_parser_next_state(context, byte); + + if (context->resolve_byte && context->command == HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){ + context->resolve_byte = 0; + context->ignore_value = 1; + parse_sequence(context); + context->line_buffer[0] = 0; + context->line_size = 0; + } +} + +static void parse_sequence(hfp_connection_t * context){ + int value; + switch (context->command){ + case HFP_CMD_SET_MICROPHONE_GAIN: + value = atoi((char *)&context->line_buffer[0]); + context->microphone_gain = value; + log_info("hfp parse HFP_CMD_SET_MICROPHONE_GAIN %d\n", value); + break; + case HFP_CMD_SET_SPEAKER_GAIN: + value = atoi((char *)&context->line_buffer[0]); + context->speaker_gain = value; + log_info("hfp parse HFP_CMD_SET_SPEAKER_GAIN %d\n", value); + break; + case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION: + value = atoi((char *)&context->line_buffer[0]); + context->ag_activate_voice_recognition = value; + log_info("hfp parse HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION %d\n", value); + break; + case HFP_CMD_TURN_OFF_EC_AND_NR: + value = atoi((char *)&context->line_buffer[0]); + context->ag_echo_and_noise_reduction = value; + log_info("hfp parse HFP_CMD_TURN_OFF_EC_AND_NR %d\n", value); + break; + case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING: + value = atoi((char *)&context->line_buffer[0]); + context->remote_supported_features = store_bit(context->remote_supported_features, HFP_AGSF_IN_BAND_RING_TONE, value); + log_info("hfp parse HFP_CHANGE_IN_BAND_RING_TONE_SETTING %d\n", value); + break; + case HFP_CMD_HF_CONFIRMED_CODEC: + context->codec_confirmed = atoi((char*)context->line_buffer); + log_info("hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d\n", context->codec_confirmed); + break; + case HFP_CMD_AG_SUGGESTED_CODEC: + context->suggested_codec = atoi((char*)context->line_buffer); + log_info("hfp parse HFP_CMD_AG_SUGGESTED_CODEC %d\n", context->suggested_codec); + break; + case HFP_CMD_SUPPORTED_FEATURES: + context->remote_supported_features = atoi((char*)context->line_buffer); + log_info("Parsed supported feature %d\n", context->remote_supported_features); + break; + case HFP_CMD_AVAILABLE_CODECS: + log_info("Parsed codec %s\n", context->line_buffer); + context->remote_codecs[context->parser_item_index] = (uint16_t)atoi((char*)context->line_buffer); + context->parser_item_index++; + context->remote_codecs_nr = context->parser_item_index; + break; + case HFP_CMD_RETRIEVE_AG_INDICATORS: + strcpy((char *)context->ag_indicators[context->parser_item_index].name, (char *)context->line_buffer); + context->ag_indicators[context->parser_item_index].index = context->parser_item_index+1; + log_info("Indicator %d: %s (", context->ag_indicators_nr+1, context->line_buffer); + break; + case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: + log_info("Parsed Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer); + context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer); + context->parser_item_index++; + break; + case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: + context->parser_item_index++; + if (context->parser_item_index != 4) break; + log_info("Parsed Enable indicators: %s\n", context->line_buffer); + value = atoi((char *)&context->line_buffer[0]); + context->enable_status_update_for_ag_indicators = (uint8_t) value; + break; + case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES: + log_info("Parsed Support call hold: %s\n", context->line_buffer); + if (context->line_size > 2 ) break; + strcpy((char *)context->remote_call_services[context->remote_call_services_nr].name, (char *)context->line_buffer); + context->remote_call_services_nr++; + break; + case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: + case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS: + log_info("Parsed Generic status indicator: %s\n", context->line_buffer); + context->generic_status_indicators[context->parser_item_index].uuid = (uint16_t)atoi((char*)context->line_buffer); + context->parser_item_index++; + context->generic_status_indicators_nr = context->parser_item_index; + break; + case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE: + // HF parses inital AG gen. ind. state + log_info("Parsed List generic status indicator %s state: ", context->line_buffer); + context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer); + break; + case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE: + // AG parses new gen. ind. state + if (context->ignore_value){ + context->ignore_value = 0; + log_info("Parsed Enable AG indicator pos %u('%s') - unchanged\n", context->parser_item_index, + context->ag_indicators[context->parser_item_index].name); + } + else if (context->ag_indicators[context->parser_item_index].mandatory){ + log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n", + context->parser_item_index, context->ag_indicators[context->parser_item_index].name); + } else { + value = atoi((char *)&context->line_buffer[0]); + context->ag_indicators[context->parser_item_index].enabled = value; + log_info("Parsed Enable AG indicator pos %u('%s'): %u\n", context->parser_item_index, + context->ag_indicators[context->parser_item_index].name, value); + } + context->parser_item_index++; + break; + case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: + // indicators are indexed starting with 1 + context->parser_item_index = atoi((char *)&context->line_buffer[0]) - 1; + log_info("Parsed status of the AG indicator %d, status ", context->parser_item_index); + break; + case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: + context->network_operator.mode = atoi((char *)&context->line_buffer[0]); + log_info("Parsed network operator mode: %d, ", context->network_operator.mode); + break; + case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: + if (context->line_buffer[0] == '3'){ + log_info("Parsed Set network operator format : %s, ", context->line_buffer); + break; + } + // TODO emit ERROR, wrong format + log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer); + break; + case HFP_CMD_ERROR: + break; + case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR: + context->extended_audio_gateway_error = (uint8_t)atoi((char*)context->line_buffer); + break; + case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR: + context->enable_extended_audio_gateway_error_report = (uint8_t)atoi((char*)context->line_buffer); + context->ok_pending = 1; + context->extended_audio_gateway_error = 0; + break; + default: + break; + } } void hfp_init(uint16_t rfcomm_channel_nr){ diff --git a/src/hfp.h b/src/hfp.h index a20dd4ac7..3657c355e 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -468,7 +468,9 @@ typedef struct hfp_connection { // uint8_t send_ok; uint8_t send_error; - uint8_t keep_separator; + uint8_t keep_byte; + uint8_t ignore_value; + uint8_t resolve_byte; uint8_t change_status_update_for_individual_ag_indicators; uint8_t operator_name_changed; diff --git a/test/hfp/hfp_ag_parser_test.c b/test/hfp/hfp_ag_parser_test.c index 9ac142dfe..8cda02acc 100644 --- a/test/hfp/hfp_ag_parser_test.c +++ b/test/hfp/hfp_ag_parser_test.c @@ -91,7 +91,7 @@ TEST_GROUP(HFPParser){ TEST(HFPParser, HFP_AG_SUPPORTED_FEATURES){ sprintf(packet, "\r\nAT%s=159\r\n", HFP_SUPPORTED_FEATURES); - context.keep_separator = 0; + //context.keep_separator = 0; for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos], 0); } @@ -169,18 +169,32 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){ } } -TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES2){ +TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES3){ set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr); context.ag_indicators_nr = hfp_ag_indicators_nr; memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); - + + sprintf(packet, "\r\nAT%s=,1,,,,,1\r\n", + HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos], 0); + } + + CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command); + for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index); CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, hfp_ag_indicators[pos].enabled); CHECK_EQUAL(context.ag_indicators[pos].index, hfp_ag_indicators[pos].index); CHECK_EQUAL(context.ag_indicators[pos].enabled, hfp_ag_indicators[pos].enabled); } - +} + +TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES2){ + set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr); + context.ag_indicators_nr = hfp_ag_indicators_nr; + memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); + sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n", HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); for (pos = 0; pos < strlen(packet); pos++){ @@ -199,15 +213,8 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES1){ set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr); context.ag_indicators_nr = hfp_ag_indicators_nr; memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); - - for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ - CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index); - CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, hfp_ag_indicators[pos].enabled); - CHECK_EQUAL(context.ag_indicators[pos].index, hfp_ag_indicators[pos].index); - CHECK_EQUAL(context.ag_indicators[pos].enabled, hfp_ag_indicators[pos].enabled); - } - - sprintf(packet, "\r\nAT%s=1,,,1,1,,1\r\n", + + sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n", HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos], 0); From e63610b3a588bf7a6215538621be9f8ce20c1e44 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 22:39:04 +0100 Subject: [PATCH 31/72] fix compile --- src/hfp_hf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 0b91a2c4c..c637e0026 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -73,9 +73,9 @@ static uint8_t hfp_indicators_status; static hfp_callback_t hfp_callback; -static hfp_call_status_t hfp_call_state; +// static hfp_call_status_t hfp_call_state; static hfp_callsetup_status_t hfp_callsetup_state; -static hfp_callheld_status_t hfp_callheld_state; +// static hfp_callheld_status_t hfp_callheld_state; void hfp_hf_register_packet_handler(hfp_callback_t callback){ hfp_callback = callback; From ac3141d08e0b29695c2a1ee3bc080582c329196a Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 22:39:35 +0100 Subject: [PATCH 32/72] log AG indicator enabled state if unchanged by BIA --- src/hfp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index 56b0de2f9..0047f12cb 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -1123,8 +1123,8 @@ static void parse_sequence(hfp_connection_t * context){ // AG parses new gen. ind. state if (context->ignore_value){ context->ignore_value = 0; - log_info("Parsed Enable AG indicator pos %u('%s') - unchanged\n", context->parser_item_index, - context->ag_indicators[context->parser_item_index].name); + log_info("Parsed Enable AG indicator pos %u('%s') - unchanged (stays %u)\n", context->parser_item_index, + context->ag_indicators[context->parser_item_index].name, context->ag_indicators[context->parser_item_index].enabled); } else if (context->ag_indicators[context->parser_item_index].mandatory){ log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n", From 5a98a3d3560e682491563ea40980ee6472ea4ab5 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 22:41:18 +0100 Subject: [PATCH 33/72] enbale singal indicator --- test/pts/hfp_ag_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 85a86216f..633397443 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -95,7 +95,7 @@ static hfp_ag_indicator_t ag_indicators[] = { {2, "call", 0, 1, 0, 1, 1, 0}, {3, "callsetup", 0, 3, 0, 1, 1, 0}, {4, "battchg", 0, 5, 3, 0, 0, 0}, - {5, "signal", 0, 5, 5, 0, 0, 0}, + {5, "signal", 0, 5, 5, 0, 1, 0}, {6, "roam", 0, 1, 0, 0, 0, 0}, {7, "callheld", 0, 2, 0, 1, 1, 0} }; From 50a0b2d2389049c75d77b6083bd859b7fc052b17 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Nov 2015 22:44:42 +0100 Subject: [PATCH 34/72] enable roaming indicator --- test/pts/hfp_ag_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 633397443..65de797fd 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -96,7 +96,7 @@ static hfp_ag_indicator_t ag_indicators[] = { {3, "callsetup", 0, 3, 0, 1, 1, 0}, {4, "battchg", 0, 5, 3, 0, 0, 0}, {5, "signal", 0, 5, 5, 0, 1, 0}, - {6, "roam", 0, 1, 0, 0, 0, 0}, + {6, "roam", 0, 1, 0, 0, 1, 0}, {7, "callheld", 0, 2, 0, 1, 1, 0} }; From 93b133f6b503940193dccdfc68061619b311bd6d Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 10:12:15 +0100 Subject: [PATCH 35/72] hfp: include name in AG indicator update --- src/hfp_hf.c | 6 ++++-- test/pts/hfp_hf_test.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index c637e0026..f3ad3ca47 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -238,13 +238,15 @@ static int hfp_hf_cmd_ata(uint16_t cid){ static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; - uint8_t event[6]; + uint8_t event[6+HFP_MAX_INDICATOR_DESC_SIZE+1]; event[0] = HCI_EVENT_HFP_META; event[1] = sizeof(event) - 2; event[2] = HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED; event[3] = status; event[4] = indicator.index; - event[5] = indicator.status; + event[5] = indicator.status; + strncpy((char*)&event[6], indicator.name, HFP_MAX_INDICATOR_DESC_SIZE); + event[6+HFP_MAX_INDICATOR_DESC_SIZE] = 0; (*callback)(event, sizeof(event)); } diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 0262e43f2..610f339f5 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -209,7 +209,7 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ } break; case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED: - printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]); + printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[6], event[4], event[5]); break; case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED: printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]); From 82d4d268b2a74f2d920cc5052f117c5463acb5ba Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 27 Nov 2015 10:31:41 +0100 Subject: [PATCH 36/72] hfp hf: update test/api --- src/hfp_hf.c | 24 +++++++-- src/hfp_hf.h | 6 ++- test/pts/hfp_hf_test.c | 118 +++++++++++++++++++---------------------- 3 files changed, 80 insertions(+), 68 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 0b91a2c4c..2c1d79e59 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -684,7 +684,7 @@ void hfp_hf_release_service_level_connection(bd_addr_t bd_addr){ hfp_run_for_context(connection); } -void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable){ +static void hfp_hf_set_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable){ hfp_hf_establish_service_level_connection(bd_addr); hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); if (!connection){ @@ -695,8 +695,16 @@ void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_ hfp_run_for_context(connection); } +void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable){ + hfp_hf_set_status_update_for_all_ag_indicators(bd_addr, 1); +} + +void hfp_hf_disable_status_update_for_all_ag_indicators(bd_addr_t bd_addr){ + hfp_hf_set_status_update_for_all_ag_indicators(bd_addr, 0); +} + // TODO: returned ERROR - wrong format -void hfp_hf_enable_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap){ +void hfp_hf_set_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap){ hfp_hf_establish_service_level_connection(bd_addr); hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); if (!connection){ @@ -719,7 +727,7 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr){ hfp_run_for_context(connection); } -void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable){ +static void hfp_hf_set_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable){ hfp_hf_establish_service_level_connection(bd_addr); hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); if (!connection){ @@ -730,6 +738,16 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_ hfp_run_for_context(connection); } + +void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr){ + hfp_hf_set_report_extended_audio_gateway_error_result_code(bd_addr, 1); +} + +void hfp_hf_disable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr){ + hfp_hf_set_report_extended_audio_gateway_error_result_code(bd_addr, 0); +} + + void hfp_hf_establish_audio_connection(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); diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 1582690a1..12798b6a0 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -94,12 +94,13 @@ void hfp_hf_release_service_level_connection(bd_addr_t bd_addr); /** * @brief Deactivate/reactivate status update for all indicators in the AG. */ -void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable); +void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr); +void hfp_hf_disable_status_update_for_all_ag_indicators(bd_addr_t bd_addr); /** * @brief Deactivate/reactivate status update for the individual indicators in the AG using bitmap. */ -void hfp_hf_enable_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap); +void hfp_hf_set_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap); /** @@ -137,6 +138,7 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr); * - +CME ERROR: 32 - network not allowed – Emergency calls only */ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable); +void hfp_hf_disable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable); /** * @brief diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 0262e43f2..00b94c90e 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -1,3 +1,4 @@ + /* * Copyright (C) 2014 BlueKitchen GmbH * @@ -91,24 +92,22 @@ static void show_usage(void){ 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 SLC connection to device\n"); + printf("A - release SLC 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("b - establish AUDIO connection\n"); - printf("B - release AUDIO connection\n"); + printf("C - enable registration status update for all AG indicators\n"); + printf("c - disable registration status update for all AG indicators\n"); - printf("d - enable registration status update\n"); - printf("D - disable registration status update\n"); + printf("D - set HFP AG registration status update for individual indicators\n"); + printf("d - Query network operator.\n"); + + printf("E - enable reporting of the extended AG error result code\n"); + printf("e - disable reporting of the extended AG error result code\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("f - answer incoming call\n"); printf("---\n"); printf("Ctrl-c - exit\n"); @@ -118,54 +117,6 @@ static void show_usage(void){ static int stdin_process(struct data_source *ds){ read(ds->fd, &cmd, 1); switch (cmd){ - case 'a': - 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': - 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)); @@ -174,7 +125,45 @@ static int stdin_process(struct data_source *ds){ memcpy(device_addr, pts_addr, 6); printf("Use PTS module %s as Audiogateway.\n", bd_addr_to_str(device_addr)); break; - + case 'a': + printf("Establish 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 'A': + printf("Release Service level connection.\n"); + hfp_hf_release_service_level_connection(device_addr); + break; + case 'b': + 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 'B': + printf("Release Audio service level connection.\n"); + hfp_hf_release_audio_connection(device_addr); + break; + case 'C': + printf("Enable registration status update for all AG indicators.\n"); + hfp_hf_enable_status_update_for_all_ag_indicators(device_addr); + case 'c': + printf("Disable registration status update for all AG indicators.\n"); + hfp_hf_disable_status_update_for_all_ag_indicators(device_addr, 0); + break; + case 'D': + printf("Set HFP AG registration status update for individual indicators (0111111).\n"); + hfp_hf_set_status_update_for_individual_ag_indicators(device_addr, 63); + break; + case 'd': + printf("Query network operator.\n"); + hfp_hf_query_operator_selection(device_addr); + break; + case 'E': + printf("Enable reporting of the extended AG error result code.\n"); + hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr); + break; + case 'e': + printf("Disable reporting of the extended AG error result code.\n"); + hfp_hf_disable_report_extended_audio_gateway_error_result_code(device_addr); + break; default: show_usage(); break; @@ -242,6 +231,9 @@ int btstack_main(int argc, const char * argv[]){ hfp_hf_create_sdp_record((uint8_t *)hfp_service_buffer, rfcomm_channel_nr, hfp_hf_service_name, 0); sdp_register_service_internal(NULL, (uint8_t *)hfp_service_buffer); + // pre-select pts + memcpy(device_addr, pts_addr, 6); + // turn on! hci_power_control(HCI_POWER_ON); From 4bf53e078e78271bb34091964b5c877733159edf Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 11:15:04 +0100 Subject: [PATCH 37/72] hfp: work on audio setup from HF --- src/hfp_hf.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index f3ad3ca47..0eead9235 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -91,7 +91,7 @@ static int hfp_hf_supports_codec(uint8_t codec){ for (i = 0; i < hfp_codecs_nr; i++){ if (hfp_codecs[i] == codec) return 1; } - return 0; + return HFP_CODEC_CVSD; } static int has_codec_negotiation_feature(hfp_connection_t * connection){ int hf = get_bit(hfp_supported_features, HFP_HFSF_CODEC_NEGOTIATION); @@ -735,19 +735,30 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_ void hfp_hf_establish_audio_connection(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 (!has_codec_negotiation_feature(connection)) return; - connection->establish_audio_connection = 0; + connection->establish_audio_connection = 0; + if (connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return; if (connection->state >= HFP_W2_DISCONNECT_SCO) return; - + + // if (!has_codec_negotiation_feature(connection)){ + // log_info("hfp_ag_establish_audio_connection - no codec negotiation feature, using defaults"); + // connection->codecs_state = HFP_CODECS_EXCHANGED; + // } + connection->establish_audio_connection = 1; - switch (connection->codecs_state){ - case HFP_CODECS_W4_AG_COMMON_CODEC: - break; - default: - connection->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP; - break; - } + + if (has_codec_negotiation_feature(connection)){ + switch (connection->codecs_state){ + case HFP_CODECS_W4_AG_COMMON_CODEC: + break; + default: + connection->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP; + break; + } + } else { + // connection->command = HFP_CMD_AVAILABLE_CODECS; + } + hfp_run_for_context(connection); } From 136bf39001793fd55855203ae27af9988278b444 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 11:20:12 +0100 Subject: [PATCH 38/72] fix compile --- src/hfp_hf.c | 2 +- src/hfp_hf.h | 4 ++-- test/pts/hfp_hf_test.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 9fe85d6f1..6b8b32f5d 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -697,7 +697,7 @@ static void hfp_hf_set_status_update_for_all_ag_indicators(bd_addr_t bd_addr, ui hfp_run_for_context(connection); } -void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable){ +void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr){ hfp_hf_set_status_update_for_all_ag_indicators(bd_addr, 1); } diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 12798b6a0..4e2100431 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -137,8 +137,8 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr); * - +CME ERROR: 31 - network Timeout. * - +CME ERROR: 32 - network not allowed – Emergency calls only */ -void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable); -void hfp_hf_disable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable); +void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr); +void hfp_hf_disable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr); /** * @brief diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 359bdff9b..46a81e711 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -146,7 +146,7 @@ static int stdin_process(struct data_source *ds){ hfp_hf_enable_status_update_for_all_ag_indicators(device_addr); case 'c': printf("Disable registration status update for all AG indicators.\n"); - hfp_hf_disable_status_update_for_all_ag_indicators(device_addr, 0); + hfp_hf_disable_status_update_for_all_ag_indicators(device_addr); break; case 'D': printf("Set HFP AG registration status update for individual indicators (0111111).\n"); From a8b7ea9824ee8b0a4b6f81f595c6fd5566b8330b Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 11:36:49 +0100 Subject: [PATCH 39/72] add answer incoming call command --- test/pts/hfp_hf_test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 46a81e711..ef80a2107 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -164,6 +164,10 @@ static int stdin_process(struct data_source *ds){ printf("Disable reporting of the extended AG error result code.\n"); hfp_hf_disable_report_extended_audio_gateway_error_result_code(device_addr); break; + case 'f': + printf("Answer incoming call.\n"); + hfp_hf_answer_incoming_call(device_addr); + break; default: show_usage(); break; From c3590e248645828c2d8d8f225ae458b9dcd62be5 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 11:38:33 +0100 Subject: [PATCH 40/72] hfp: create SCO connection if Codec Exchange not supported --- src/hfp_hf.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 6b8b32f5d..e14cad56f 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -422,9 +422,19 @@ static int hfp_hf_run_for_audio_connection(hfp_connection_t * context){ } if (context->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; - + // run codecs exchange - return codecs_exchange_state_machine(context); + int done = codecs_exchange_state_machine(context); + if (done) return 1; + + if (context->establish_audio_connection){ + context->state = HFP_W4_SCO_CONNECTED; + context->establish_audio_connection = 0; + hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F); + return 1; + } + + return 0; } static int call_setup_state_machine(hfp_connection_t * context){ @@ -758,14 +768,11 @@ void hfp_hf_establish_audio_connection(bd_addr_t bd_addr){ if (connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return; if (connection->state >= HFP_W2_DISCONNECT_SCO) return; - // if (!has_codec_negotiation_feature(connection)){ - // log_info("hfp_ag_establish_audio_connection - no codec negotiation feature, using defaults"); - // connection->codecs_state = HFP_CODECS_EXCHANGED; - // } - - connection->establish_audio_connection = 1; - - if (has_codec_negotiation_feature(connection)){ + if (!has_codec_negotiation_feature(connection)){ + log_info("hfp_ag_establish_audio_connection - no codec negotiation feature, using defaults"); + connection->codecs_state = HFP_CODECS_EXCHANGED; + connection->establish_audio_connection = 1; + } else { switch (connection->codecs_state){ case HFP_CODECS_W4_AG_COMMON_CODEC: break; @@ -773,8 +780,6 @@ void hfp_hf_establish_audio_connection(bd_addr_t bd_addr){ connection->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP; break; } - } else { - // connection->command = HFP_CMD_AVAILABLE_CODECS; } hfp_run_for_context(connection); @@ -789,10 +794,13 @@ void hfp_hf_release_audio_connection(bd_addr_t bd_addr){ void hfp_hf_answer_incoming_call(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 (hfp_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ - connection->hf_answer_incoming_call = 1; - } else { - log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_state); - } + + // HACK - remove after hfp_callsetup_state is updated + connection->hf_answer_incoming_call = 1; + (void) hfp_callsetup_state; + // if (hfp_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ + // connection->hf_answer_incoming_call = 1; + // } else { + // log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_state); + // } } From 64f38c40ac54719b8265132f16766cd09c6f1e81 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 12:28:38 +0100 Subject: [PATCH 41/72] hfp: add 't'erminate to tester --- test/pts/hfp_hf_test.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index ef80a2107..5802abc06 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -77,6 +77,7 @@ 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; +static uint16_t handle = -1; static uint8_t codecs[] = {HFP_CODEC_CVSD, HFP_CODEC_MSBC}; static uint16_t indicators[1] = {0x01}; @@ -89,7 +90,7 @@ static void show_usage(); 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("a - establish SLC connection to device\n"); @@ -108,7 +109,8 @@ static void show_usage(void){ printf("e - disable reporting of the extended AG error result code\n"); printf("f - answer incoming call\n"); - + printf("t - terminate connection\n"); + printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -117,14 +119,6 @@ static void show_usage(void){ static int stdin_process(struct data_source *ds){ read(ds->fd, &cmd, 1); switch (cmd){ - 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; case 'a': printf("Establish Service level connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr)); hfp_hf_establish_service_level_connection(device_addr); @@ -168,6 +162,14 @@ static int stdin_process(struct data_source *ds){ printf("Answer incoming call.\n"); hfp_hf_answer_incoming_call(device_addr); break; + case 't': + printf("Terminate HCI connection.\n"); + gap_disconnect(handle); + break; + case 'y': + memcpy(device_addr, phone_addr, 6); + printf("Use iPhone %s as Audiogateway.\n", bd_addr_to_str(device_addr)); + break; default: show_usage(); break; @@ -178,6 +180,11 @@ static int stdin_process(struct data_source *ds){ static void packet_handler(uint8_t * event, uint16_t event_size){ + if (event[0] == RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE){ + handle = READ_BT_16(event, 9); + printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE received for handle 0x%04x\n", handle); + return; + } if (event[0] != HCI_EVENT_HFP_META) return; if (event[3] && event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR){ printf("ERROR, status: %u\n", event[3]); From 07e50473b0cdeac4a62a01ed141ce4345cbb9302 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 13:05:45 +0100 Subject: [PATCH 42/72] hfp: use (micro) state machine for HF query operator --- src/hfp.h | 12 +++++++++ src/hfp_hf.c | 58 ++++++++++++++++++++++++++---------------- test/pts/hfp_hf_test.c | 7 +++++ 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index d1deb2551..48fb8264a 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -390,6 +390,15 @@ typedef enum { HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED } hfp_response_and_hold_state_t; +typedef enum { + HFP_HF_QUERY_OPERATOR_FORMAT_NOT_SET = 0, + HFP_HF_QUERY_OPERATOR_SET_FORMAT, + HFP_HF_QUERY_OPERATOR_W4_SET_FORMAT_OK, + HFP_HF_QUERY_OPERATOR_FORMAT_SET, + HFP_HF_QUERY_OPERATOR_SEND_QUERY, + HPF_HF_QUERY_OPERATOR_W4_RESULT +} hfp_hf_query_operator_state_t; + typedef enum{ HFP_NONE_SM, HFP_SLC_SM, @@ -526,6 +535,9 @@ typedef struct hfp_connection { int send_response_and_hold_active; int send_response_and_hold_status; + // HF only + hfp_hf_query_operator_state_t hf_query_operator_state; + timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index e14cad56f..39b5b22db 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -333,17 +333,19 @@ static int hfp_hf_run_for_context_service_level_connection_queries(hfp_connectio return done; } - if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT){ - context->ok_pending = 1; - done = 1; - hfp_hf_cmd_query_operator_name_format(context->rfcomm_cid); - return done; - } - if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME){ - context->ok_pending = 1; - done = 1; - hfp_hf_cmd_query_operator_name(context->rfcomm_cid); - return done; + switch (context->hf_query_operator_state){ + case HFP_HF_QUERY_OPERATOR_SET_FORMAT: + context->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_W4_SET_FORMAT_OK; + context->ok_pending = 1; + hfp_hf_cmd_query_operator_name_format(context->rfcomm_cid); + return 1; + case HFP_HF_QUERY_OPERATOR_SEND_QUERY: + context->hf_query_operator_state = HPF_HF_QUERY_OPERATOR_W4_RESULT; + context->ok_pending = 1; + hfp_hf_cmd_query_operator_name(context->rfcomm_cid); + return 1; + default: + break; } if (context->enable_extended_audio_gateway_error_report){ @@ -366,7 +368,6 @@ static int codecs_exchange_state_machine(hfp_connection_t * context){ */ if (context->ok_pending) return 0; - printf(" -> State machine: CC\n"); switch (context->command){ case HFP_CMD_AVAILABLE_CODECS: @@ -545,20 +546,23 @@ static void hfp_hf_switch_on_ok(hfp_connection_t *context){ break; } - if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT){ - context->command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME; - break; - } - - if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME){ - hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator); - break; + switch (context->hf_query_operator_state){ + case HFP_HF_QUERY_OPERATOR_W4_SET_FORMAT_OK: + printf("Format set, querying name\n"); + context->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_SEND_QUERY; + break; + case HPF_HF_QUERY_OPERATOR_W4_RESULT: + context->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_FORMAT_SET; + hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator); + break; + default: + break; } + if (context->enable_extended_audio_gateway_error_report){ context->enable_extended_audio_gateway_error_report = 0; break; } - printf(" CC received ok\n"); switch (context->codecs_state){ case HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE: @@ -619,6 +623,7 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 default: break; } + hfp_run_for_context(context); } static void hfp_run(){ @@ -735,7 +740,16 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr){ log_error("HFP HF: connection doesn't exist."); return; } - connection->command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT; + switch (connection->hf_query_operator_state){ + case HFP_HF_QUERY_OPERATOR_FORMAT_NOT_SET: + connection->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_SET_FORMAT; + break; + case HFP_HF_QUERY_OPERATOR_FORMAT_SET: + connection->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_SEND_QUERY; + break; + default: + break; + } hfp_run_for_context(connection); } diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 5802abc06..52c658371 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -109,6 +109,9 @@ static void show_usage(void){ printf("e - disable reporting of the extended AG error result code\n"); printf("f - answer incoming call\n"); + + printf("g - query network operator name\n"); + printf("t - terminate connection\n"); printf("---\n"); @@ -162,6 +165,10 @@ static int stdin_process(struct data_source *ds){ printf("Answer incoming call.\n"); hfp_hf_answer_incoming_call(device_addr); break; + case 'g': + printf("Query operator.\n"); + hfp_hf_query_operator_selection(device_addr); + break; case 't': printf("Terminate HCI connection.\n"); gap_disconnect(handle); From dfbd9600be051031ccc3cdbe3b1f94ef07f0f8a6 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 13:27:31 +0100 Subject: [PATCH 43/72] hfp: use only HV1/H3 packet types to enforce mandatory SCO link type --- src/hfp_ag.c | 3 ++- src/hfp_hf.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 74a4cfe19..b48d19ea9 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -705,7 +705,8 @@ static int hfp_ag_run_for_audio_connection(hfp_connection_t * context){ if (context->establish_audio_connection){ context->state = HFP_W4_SCO_CONNECTED; context->establish_audio_connection = 0; - hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F); + // only support HV1 + HV3 to avoid eSCO + hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x03c5); return 1; } diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 39b5b22db..af986cedc 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -431,7 +431,8 @@ static int hfp_hf_run_for_audio_connection(hfp_connection_t * context){ if (context->establish_audio_connection){ context->state = HFP_W4_SCO_CONNECTED; context->establish_audio_connection = 0; - hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F); + // only support HV1 + HV3 to avoid eSCO + hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x03c5); return 1; } From 66ee7d3e0b8e2fdbad98ddd5b7f238d01a1dad53 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 13:46:41 +0100 Subject: [PATCH 44/72] hfp: support CLIP in HF --- src/hfp.h | 28 ++++++++++++++-------------- src/hfp_hf.c | 21 +++++++++++++++++++++ src/hfp_hf.h | 5 +++++ test/pts/hfp_hf_test.c | 11 +++++++++++ 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index 48fb8264a..fb76362a7 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -510,11 +510,7 @@ typedef struct hfp_connection { uint8_t establish_audio_connection; uint8_t release_audio_connection; - uint8_t change_in_band_ring_tone_setting; - uint8_t ag_ring; - uint8_t ag_send_clip; - uint8_t ag_echo_and_noise_reduction; - uint8_t ag_activate_voice_recognition; + timer_source_t hfp_timeout; uint8_t microphone_gain; uint8_t send_microphone_gain; @@ -523,22 +519,26 @@ typedef struct hfp_connection { uint8_t send_speaker_gain; uint8_t send_phone_number_for_voice_tag; - + uint8_t send_ag_status_indicators; + uint8_t send_response_and_hold_active; + uint8_t send_response_and_hold_status; + + // AG only + uint8_t change_in_band_ring_tone_setting; + uint8_t ag_ring; + uint8_t ag_send_clip; + uint8_t ag_echo_and_noise_reduction; + uint8_t ag_activate_voice_recognition; uint8_t send_subscriber_number; - int next_subscriber_number_to_send; + uint8_t next_subscriber_number_to_send; int send_status_of_current_calls; - - uint8_t hf_answer_incoming_call; - - int send_ag_status_indicators; - int send_response_and_hold_active; - int send_response_and_hold_status; // HF only hfp_hf_query_operator_state_t hf_query_operator_state; + uint8_t hf_answer_incoming_call; + uint8_t hf_send_clip_enable; - timer_source_t hfp_timeout; } hfp_connection_t; // UTILS_START : TODO move to utils diff --git a/src/hfp_hf.c b/src/hfp_hf.c index af986cedc..412abfcfe 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -236,6 +236,12 @@ static int hfp_hf_cmd_ata(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_send_clip_enable(uint16_t cid){ + char buffer[20]; + sprintf(buffer, "AT%s=1\r\n", HFP_ENABLE_CLIP); + return send_str_over_rfcomm(cid, buffer); +} + static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; uint8_t event[6+HFP_MAX_INDICATOR_DESC_SIZE+1]; @@ -333,6 +339,13 @@ static int hfp_hf_run_for_context_service_level_connection_queries(hfp_connectio return done; } + if (context->hf_send_clip_enable){ + context->hf_send_clip_enable = 0; + context->ok_pending = 1; + hfp_hf_send_clip_enable(context->rfcomm_cid); + return 1; + } + switch (context->hf_query_operator_state){ case HFP_HF_QUERY_OPERATOR_SET_FORMAT: context->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_W4_SET_FORMAT_OK; @@ -813,9 +826,17 @@ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr){ // HACK - remove after hfp_callsetup_state is updated connection->hf_answer_incoming_call = 1; (void) hfp_callsetup_state; + hfp_run_for_context(connection); // if (hfp_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ // connection->hf_answer_incoming_call = 1; // } else { // log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_state); // } } + +void hfp_hf_enable_calling_line_identification(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); + connection->hf_send_clip_enable = 1; + hfp_run_for_context(connection); +} diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 4e2100431..49f0d070c 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -155,6 +155,11 @@ void hfp_hf_release_audio_connection(bd_addr_t bd_addr); */ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_hf_enable_calling_line_identification(bd_addr_t bd_addr); + /* API_END */ #if defined __cplusplus diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 52c658371..3fbd262b9 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -111,6 +111,7 @@ static void show_usage(void){ printf("f - answer incoming call\n"); printf("g - query network operator name\n"); + printf("h - enable Calling Line Identification.\n"); printf("t - terminate connection\n"); @@ -169,6 +170,10 @@ static int stdin_process(struct data_source *ds){ printf("Query operator.\n"); hfp_hf_query_operator_selection(device_addr); break; + case 'h': + printf("Enable Calling Line Identification.\n"); + hfp_hf_enable_calling_line_identification(device_addr); + break; case 't': printf("Terminate HCI connection.\n"); gap_disconnect(handle); @@ -204,6 +209,12 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: printf("Service level connection released.\n\n"); break; + case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED: + printf("\n** Audio connection established **\n"); + break; + case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED: + printf("\n** Audio connection released **\n"); + break; case HFP_SUBEVENT_COMPLETE: switch (cmd){ case 'd': From 78747ec1550cfb7e2fcc73a82f909cae01de0ddd Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 14:08:11 +0100 Subject: [PATCH 45/72] hfp: add HF hangup call --- src/hfp.h | 1 + src/hfp_hf.c | 34 +++++++++++++++++++++++++++------- src/hfp_hf.h | 6 +++++- test/pts/hfp_hf_test.c | 5 +++++ 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index fb76362a7..8334aded2 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -538,6 +538,7 @@ typedef struct hfp_connection { hfp_hf_query_operator_state_t hf_query_operator_state; uint8_t hf_answer_incoming_call; uint8_t hf_send_clip_enable; + uint8_t hf_send_chup; } hfp_connection_t; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 412abfcfe..17770acfa 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -242,6 +242,12 @@ static int hfp_hf_send_clip_enable(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_send_chup(uint16_t cid){ + char buffer[20]; + sprintf(buffer, "AT%s\r\n", HFP_HANG_UP_CALL); + return send_str_over_rfcomm(cid, buffer); +} + static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; uint8_t event[6+HFP_MAX_INDICATOR_DESC_SIZE+1]; @@ -339,13 +345,6 @@ static int hfp_hf_run_for_context_service_level_connection_queries(hfp_connectio return done; } - if (context->hf_send_clip_enable){ - context->hf_send_clip_enable = 0; - context->ok_pending = 1; - hfp_hf_send_clip_enable(context->rfcomm_cid); - return 1; - } - switch (context->hf_query_operator_state){ case HFP_HF_QUERY_OPERATOR_SET_FORMAT: context->hf_query_operator_state = HFP_HF_QUERY_OPERATOR_W4_SET_FORMAT_OK; @@ -476,6 +475,20 @@ static void hfp_run_for_context(hfp_connection_t * context){ done = call_setup_state_machine(context); } + if (context->hf_send_clip_enable){ + context->hf_send_clip_enable = 0; + context->ok_pending = 1; + hfp_hf_send_clip_enable(context->rfcomm_cid); + return; + } + + if (context->hf_send_chup){ + context->hf_send_chup = 0; + context->ok_pending = 1; + hfp_hf_send_chup(context->rfcomm_cid); + return; + } + if (done) return; // deal with disconnect switch (context->state){ @@ -834,6 +847,13 @@ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr){ // } } +void hfp_hf_terminate_call(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); + connection->hf_send_chup = 1; + hfp_run_for_context(connection); +} + void hfp_hf_enable_calling_line_identification(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); diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 49f0d070c..da8febfe0 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -84,7 +84,6 @@ void hfp_hf_register_packet_handler(hfp_callback_t callback); */ void hfp_hf_establish_service_level_connection(bd_addr_t bd_addr); - /** * @brief Release the RFCOMM channel and the audio connection between the HF and the AG. * TODO: trigger release of the audio connection @@ -155,6 +154,11 @@ void hfp_hf_release_audio_connection(bd_addr_t bd_addr); */ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_hf_terminate_call(bd_addr_t bd_addr); + /** * @brief */ diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 3fbd262b9..6d9781066 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -109,6 +109,7 @@ static void show_usage(void){ printf("e - disable reporting of the extended AG error result code\n"); printf("f - answer incoming call\n"); + printf("F - Hangup call\n"); printf("g - query network operator name\n"); printf("h - enable Calling Line Identification.\n"); @@ -166,6 +167,10 @@ static int stdin_process(struct data_source *ds){ printf("Answer incoming call.\n"); hfp_hf_answer_incoming_call(device_addr); break; + case 'F': + printf("Hangup call.\n"); + hfp_hf_terminate_call(device_addr); + break; case 'g': printf("Query operator.\n"); hfp_hf_query_operator_selection(device_addr); From 44c3bf3b21c8691a448248e5b2644ca3663311f3 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 14:29:41 +0100 Subject: [PATCH 46/72] hfp: report RING in HF test --- include/btstack/hci_cmds.h | 2 +- src/hfp.c | 4 ++++ src/hfp.h | 2 ++ src/hfp_hf.c | 3 +++ test/pts/hfp_hf_test.c | 3 +++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 05609c20c..850e4c8af 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -624,7 +624,6 @@ extern "C" { #define HSP_SUBEVENT_HS_COMMAND 0x05 #define HSP_SUBEVENT_AG_INDICATION 0x06 #define HSP_SUBEVENT_ERROR 0x07 -#define HSP_SUBEVENT_RING 0x08 #define HCI_EVENT_HFP_META 0xE9 @@ -647,6 +646,7 @@ extern "C" { #define HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL 0x11 #define HFP_SUBEVENT_CALL_ANSWERED 0x12 #define HFP_SUBEVENT_CONFERENCE_CALL 0x13 +#define HFP_SUBEVENT_RING 0x14 // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp.c b/src/hfp.c index 0047f12cb..96c15a96d 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -686,6 +686,10 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ return HFP_CMD_ERROR; } + if (strncmp(line_buffer+offset, HFP_RING, strlen(HFP_RING)) == 0){ + return HFP_CMD_RING; + } + if (isHandsFree && strncmp(line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){ return HFP_CMD_OK; } diff --git a/src/hfp.h b/src/hfp.h index 8334aded2..b1d95c40e 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -137,6 +137,7 @@ extern "C" { #define HFP_OK "OK" #define HFP_ERROR "ERROR" +#define HFP_RING "RING" // Codecs #define HFP_CODEC_CVSD 0x01 @@ -147,6 +148,7 @@ typedef enum { HFP_CMD_ERROR, HFP_CMD_UNKNOWN, HFP_CMD_OK, + HFP_CMD_RING, HFP_CMD_SUPPORTED_FEATURES, HFP_CMD_AVAILABLE_CODECS, diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 17770acfa..da8decaa8 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -647,6 +647,9 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 case HFP_CMD_OK: hfp_hf_switch_on_ok(context); break; + case HFP_CMD_RING: + hfp_emit_event(hfp_callback, HFP_SUBEVENT_RING, 0); + break; default: break; } diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 6d9781066..228403b3a 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -241,6 +241,9 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ if (event[4]) printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]); break; + case HFP_SUBEVENT_RING: + printf("** Ring **\n"); + break; default: printf("event not handled %u\n", event[2]); break; From 1ae0319cd09a76cc0664f3a1c26fd2dfe4738200 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 15:31:46 +0100 Subject: [PATCH 47/72] hfp: prepare for dynamic Link Setting use --- src/hfp.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index b1d95c40e..4c14ee10b 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -71,7 +71,7 @@ extern "C" { #define HFP_HFSF_VOICE_RECOGNITION_FUNCTION 3 #define HFP_HFSF_CODEC_NEGOTIATION 7 #define HFP_HFSF_HF_INDICATORS 8 -#define HFP_HFSF_ESCO 9 +#define HFP_HFSF_ESCO_S4 9 /* AG Supported Features: 0: Three-way calling @@ -94,7 +94,7 @@ extern "C" { #define HFP_AGSF_IN_BAND_RING_TONE 3 #define HFP_AGSF_CODEC_NEGOTIATION 9 #define HFP_AGSF_HF_INDICATORS 10 -#define HFP_AGSF_ESCO 11 +#define HFP_AGSF_ESCO_S4 11 #define HFP_DEFAULT_HF_SUPPORTED_FEATURES 0x0000 #define HFP_DEFAULT_AG_SUPPORTED_FEATURES 0x0009 @@ -401,6 +401,17 @@ typedef enum { HPF_HF_QUERY_OPERATOR_W4_RESULT } hfp_hf_query_operator_state_t; +typedef enum { + HFP_LINK_SETTINGS_D0 = 0, + HFP_LINK_SETTINGS_D1, + HFP_LINK_SETTINGS_S1, + HFP_LINK_SETTINGS_S2, + HFP_LINK_SETTINGS_S3, + HFP_LINK_SETTINGS_S4, + HFP_LINK_SETTINGS_T1, + HFP_LINK_SETTINGS_T1 +} hfp_link_setttings_t; + typedef enum{ HFP_NONE_SM, HFP_SLC_SM, @@ -575,6 +586,9 @@ void hfp_reset_context_flags(hfp_connection_t * context); void hfp_release_audio_connection(hfp_connection_t * context); +void hfp_setup_synchronous_connection(uint16_t handle, hfp_link_setttings_t link_settings); +void hfp_accept_synchronous_connection(bd_addr_t addr, hfp_link_setttings_t link_settings); + const char * hfp_hf_feature(int index); const char * hfp_ag_feature(int index); From a14eb9fcccfbc9f37e910254b0688b1bf017cbaf Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 27 Nov 2015 15:50:06 +0100 Subject: [PATCH 48/72] hfp: track callsetup, callheld, call status --- src/hfp_hf.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index da8decaa8..6281dffae 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -73,9 +73,9 @@ static uint8_t hfp_indicators_status; static hfp_callback_t hfp_callback; -// static hfp_call_status_t hfp_call_state; -static hfp_callsetup_status_t hfp_callsetup_state; -// static hfp_callheld_status_t hfp_callheld_state; +static hfp_call_status_t hfp_call_status; +static hfp_callsetup_status_t hfp_callsetup_status; +static hfp_callheld_status_t hfp_callheld_status; void hfp_hf_register_packet_handler(hfp_callback_t callback){ hfp_callback = callback; @@ -624,14 +624,6 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 for (pos = 0; pos < size ; pos++){ hfp_parse(context, packet[pos], 1); } - // emit indicators status changed - for (i = 0; i < context->ag_indicators_nr; i++){ - if (context->ag_indicators[i].status_changed) { - context->ag_indicators[i].status_changed = 0; - hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]); - break; - } - } switch (context->command){ case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR: @@ -650,6 +642,22 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 case HFP_CMD_RING: hfp_emit_event(hfp_callback, HFP_SUBEVENT_RING, 0); break; + case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: + for (i = 0; i < context->ag_indicators_nr; i++){ + if (context->ag_indicators[i].status_changed) { + if (strcmp(context->ag_indicators[i].name, "callsetup") == 0){ + hfp_callsetup_status = (hfp_callsetup_status_t) context->ag_indicators[i].status; + } else if (strcmp(context->ag_indicators[i].name, "callheld") == 0){ + hfp_callheld_status = (hfp_callheld_status_t) context->ag_indicators[i].status; + } else if (strcmp(context->ag_indicators[i].name, "call") == 0){ + hfp_call_status = (hfp_call_status_t) context->ag_indicators[i].status; + } + context->ag_indicators[i].status_changed = 0; + hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]); + break; + } + } + break; default: break; } @@ -839,15 +847,12 @@ void hfp_hf_answer_incoming_call(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); - // HACK - remove after hfp_callsetup_state is updated - connection->hf_answer_incoming_call = 1; - (void) hfp_callsetup_state; + if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ + connection->hf_answer_incoming_call = 1; + } else { + log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_status); + } hfp_run_for_context(connection); - // if (hfp_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ - // connection->hf_answer_incoming_call = 1; - // } else { - // log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_state); - // } } void hfp_hf_terminate_call(bd_addr_t bd_addr){ From 0e1f35165fbb585abb221166675eed4421dca393 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 16:14:01 +0100 Subject: [PATCH 49/72] fix typo --- src/hfp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hfp.h b/src/hfp.h index 4c14ee10b..209bf8f7d 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -409,7 +409,7 @@ typedef enum { HFP_LINK_SETTINGS_S3, HFP_LINK_SETTINGS_S4, HFP_LINK_SETTINGS_T1, - HFP_LINK_SETTINGS_T1 + HFP_LINK_SETTINGS_T2 } hfp_link_setttings_t; typedef enum{ From 417c123e5cdb13f6e6dfac4c395f3f20294cfddd Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 16:15:32 +0100 Subject: [PATCH 50/72] hfp: add SCO E4 to testers --- test/pts/hfp_ag_test.c | 2 +- test/pts/hfp_hf_test.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 65de797fd..5c61e289e 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -684,7 +684,7 @@ int btstack_main(int argc, const char * argv[]){ l2cap_init(); rfcomm_init(); - hfp_ag_init(rfcomm_channel_nr, 0x3ef | (1< Date: Fri, 27 Nov 2015 16:18:14 +0100 Subject: [PATCH 51/72] hfp: terminate/reject incoming call --- src/hfp_hf.c | 23 +++++++++++++++++++---- src/hfp_hf.h | 5 +++++ test/pts/hfp_hf_test.c | 6 ++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 6281dffae..53a438e8c 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -849,17 +849,32 @@ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr){ if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ connection->hf_answer_incoming_call = 1; + hfp_run_for_context(connection); } else { - log_error("HFP HF: answering incoming call in wrong callsetup state %u", hfp_callsetup_status); + log_error("HFP HF: answering incoming call with wrong callsetup status %u", hfp_callsetup_status); } - hfp_run_for_context(connection); } void hfp_hf_terminate_call(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); - connection->hf_send_chup = 1; - hfp_run_for_context(connection); + + if (hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ + connection->hf_send_chup = 1; + hfp_run_for_context(connection); + } else { + log_error("HFP HF: terminating incoming call with wrong call status %u", hfp_call_status); + } +} + +void hfp_hf_reject_call(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 (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ + connection->hf_send_chup = 1; + hfp_run_for_context(connection); + } } void hfp_hf_enable_calling_line_identification(bd_addr_t bd_addr){ diff --git a/src/hfp_hf.h b/src/hfp_hf.h index da8febfe0..78a5a7714 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -154,6 +154,11 @@ void hfp_hf_release_audio_connection(bd_addr_t bd_addr); */ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_hf_reject_call(bd_addr_t bd_addr); + /** * @brief */ diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 228403b3a..081900ada 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -111,7 +111,9 @@ static void show_usage(void){ printf("f - answer incoming call\n"); printf("F - Hangup call\n"); + printf("G - Reject call.\n"); printf("g - query network operator name\n"); + printf("h - enable Calling Line Identification.\n"); printf("t - terminate connection\n"); @@ -171,6 +173,10 @@ static int stdin_process(struct data_source *ds){ printf("Hangup call.\n"); hfp_hf_terminate_call(device_addr); break; + case 'G': + printf("Reject call.\n"); + hfp_hf_reject_call(device_addr); + break; case 'g': printf("Query operator.\n"); hfp_hf_query_operator_selection(device_addr); From 1a77991a3fb149de3b84ce17423c7739770a5dc3 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 27 Nov 2015 17:34:54 +0100 Subject: [PATCH 52/72] hfp hf: dial, redial, memory dial --- src/hfp.h | 4 +++ src/hfp_hf.c | 68 ++++++++++++++++++++++++++++++++++++++++++ src/hfp_hf.h | 15 ++++++++++ test/pts/hfp_hf_test.c | 30 ++++++++++++++++--- 4 files changed, 113 insertions(+), 4 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index b1d95c40e..6e1481e16 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -539,6 +539,10 @@ typedef struct hfp_connection { // HF only hfp_hf_query_operator_state_t hf_query_operator_state; uint8_t hf_answer_incoming_call; + uint8_t hf_initiate_outgoing_call; + uint8_t hf_initiate_memory_dialing; + uint8_t hf_initiate_redial_last_number; + uint8_t hf_send_clip_enable; uint8_t hf_send_chup; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 53a438e8c..1b139057b 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -77,6 +77,8 @@ static hfp_call_status_t hfp_call_status; static hfp_callsetup_status_t hfp_callsetup_status; static hfp_callheld_status_t hfp_callheld_status; +static char phone_number[25]; + void hfp_hf_register_packet_handler(hfp_callback_t callback){ hfp_callback = callback; if (callback == NULL){ @@ -242,6 +244,24 @@ static int hfp_hf_send_clip_enable(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_initiate_outgoing_call_cmd(uint16_t cid){ + char buffer[40]; + sprintf(buffer, "%s%s;\r\n", HFP_CALL_PHONE_NUMBER, phone_number); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_hf_send_memory_dial_cmd(uint16_t cid){ + char buffer[40]; + sprintf(buffer, "%s>%s;\r\n", HFP_CALL_PHONE_NUMBER, phone_number); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_hf_send_redial_last_number_cmd(uint16_t cid){ + char buffer[20]; + sprintf(buffer, "%s\r\n", HFP_REDIAL_LAST_NUMBER); + return send_str_over_rfcomm(cid, buffer); +} + static int hfp_hf_send_chup(uint16_t cid){ char buffer[20]; sprintf(buffer, "AT%s\r\n", HFP_HANG_UP_CALL); @@ -475,6 +495,28 @@ static void hfp_run_for_context(hfp_connection_t * context){ done = call_setup_state_machine(context); } + + if (context->hf_initiate_outgoing_call){ + context->hf_initiate_outgoing_call = 0; + context->ok_pending = 1; + hfp_hf_initiate_outgoing_call_cmd(context->rfcomm_cid); + return; + } + + if (context->hf_initiate_memory_dialing){ + context->hf_initiate_memory_dialing = 0; + context->ok_pending = 1; + hfp_hf_send_memory_dial_cmd(context->rfcomm_cid); + return; + } + + if (context->hf_initiate_redial_last_number){ + context->hf_initiate_redial_last_number = 0; + context->ok_pending = 1; + hfp_hf_send_redial_last_number_cmd(context->rfcomm_cid); + return; + } + if (context->hf_send_clip_enable){ context->hf_send_clip_enable = 0; context->ok_pending = 1; @@ -883,3 +925,29 @@ void hfp_hf_enable_calling_line_identification(bd_addr_t bd_addr){ connection->hf_send_clip_enable = 1; hfp_run_for_context(connection); } + +void hfp_hf_dial_number(bd_addr_t bd_addr, char * number){ + hfp_hf_establish_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + + connection->hf_initiate_outgoing_call = 1; + snprintf(phone_number, sizeof(phone_number), "%s", number); + hfp_run_for_context(connection); +} + +void hfp_hf_dial_memory(bd_addr_t bd_addr, char * number){ + hfp_hf_establish_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + + connection->hf_initiate_memory_dialing = 1; + snprintf(phone_number, sizeof(phone_number), "%s", number); + hfp_run_for_context(connection); +} + +void hfp_hf_redial_last_number(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); + + connection->hf_initiate_redial_last_number = 1; + hfp_run_for_context(connection); +} diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 78a5a7714..43e2f862d 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -169,6 +169,21 @@ void hfp_hf_terminate_call(bd_addr_t bd_addr); */ void hfp_hf_enable_calling_line_identification(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_hf_dial_number(bd_addr_t bd_addr, char * number); + +/** + * @brief + */ +void hfp_hf_dial_memory(bd_addr_t bd_addr, char * number); + +/** + * @brief + */ +void hfp_hf_redial_last_number(bd_addr_t bd_addr); + /* API_END */ #if defined __cplusplus diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 081900ada..26f4931bb 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -99,23 +99,29 @@ static void show_usage(void){ printf("b - establish Audio connection\n"); printf("B - release Audio connection\n"); - printf("C - enable registration status update for all AG indicators\n"); printf("c - disable registration status update for all AG indicators\n"); + printf("C - enable registration status update for all AG indicators\n"); - printf("D - set HFP AG registration status update for individual indicators\n"); printf("d - Query network operator.\n"); + printf("D - set HFP AG registration status update for individual indicators\n"); - printf("E - enable reporting of the extended AG error result code\n"); printf("e - disable reporting of the extended AG error result code\n"); + printf("E - enable reporting of the extended AG error result code\n"); printf("f - answer incoming call\n"); printf("F - Hangup call\n"); - printf("G - Reject call.\n"); printf("g - query network operator name\n"); + printf("G - Reject call.\n"); printf("h - enable Calling Line Identification.\n"); + printf("i - dial 1234567\n"); + printf("I - redial\n"); + + printf("j - dial #1\n"); + printf("J - dial #99\n"); + printf("t - terminate connection\n"); printf("---\n"); @@ -193,6 +199,22 @@ static int stdin_process(struct data_source *ds){ memcpy(device_addr, phone_addr, 6); printf("Use iPhone %s as Audiogateway.\n", bd_addr_to_str(device_addr)); break; + case 'i': + printf("Dial 1234567\n"); + hfp_hf_dial_number(device_addr, "1234567"); + break; + case 'I': + printf("Redial\n"); + hfp_hf_redial_last_number(device_addr); + break; + case 'j': + printf("Dial #1\n"); + hfp_hf_dial_memory(device_addr,"#1"); + break; + case 'J': + printf("Dial #99\n"); + hfp_hf_dial_memory(device_addr,"#99"); + break; default: show_usage(); break; From 98a2fd1ca4b9043573a35fa7416e26b5293fc617 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 20:27:53 +0100 Subject: [PATCH 53/72] add hci_remote_eSCO_supported. use HFP S4 settings if eSCO supported --- src/hci.c | 20 ++++++++++++++++++-- src/hci.h | 5 +++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/hci.c b/src/hci.c index 1870ba678..7d2d9def9 100644 --- a/src/hci.c +++ b/src/hci.c @@ -1403,9 +1403,12 @@ static void event_handler(uint8_t *packet, int size){ if (features[6] & (1 << 3)){ conn->bonding_flags |= BONDING_REMOTE_SUPPORTS_SSP; } + if (features[3] & (1<<7)){ + conn->remote_supported_feature_eSCO = 1; + } } conn->bonding_flags |= BONDING_RECEIVED_REMOTE_FEATURES; - log_info("HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE, bonding flags %x", conn->bonding_flags); + log_info("HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE, bonding flags %x, eSCO %u", conn->bonding_flags, conn->remote_supported_feature_eSCO); if (conn->bonding_flags & BONDING_DEDICATED){ conn->bonding_flags |= BONDING_SEND_AUTHENTICATE_REQUEST; } @@ -2325,7 +2328,13 @@ void hci_run(void){ if (connection->address_type == BD_ADDR_TYPE_CLASSIC){ hci_send_cmd(&hci_accept_connection_request, connection->address, 1); } else { - hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0xFFFF, hci_stack->sco_voice_setting, 0xFF, 0x003F); + if (connection->remote_supported_feature_eSCO){ + // S4 - max latency == transmission interval = 0x000c == 12 ms + hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0x02, 0x003f); + } else { + // max latency, retransmission interval: N/A. any packet type + hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0xff, 0x003f); + } } return; @@ -2889,6 +2898,13 @@ void hci_emit_dedicated_bonding_result(bd_addr_t address, uint8_t status){ hci_stack->packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); } +// query if remote side supports eSCO +int hci_remote_eSCO_supported(hci_con_handle_t con_handle){ + hci_connection_t * connection = hci_connection_for_handle(con_handle); + if (!connection) return 0; + return connection->remote_supported_feature_eSCO; +} + // query if remote side supports SSP int hci_remote_ssp_supported(hci_con_handle_t con_handle){ hci_connection_t * connection = hci_connection_for_handle(con_handle); diff --git a/src/hci.h b/src/hci.h index 69b1a1276..1cd24df9a 100644 --- a/src/hci.h +++ b/src/hci.h @@ -513,6 +513,9 @@ typedef struct { // link_key_type_t link_key_type; + // remote supported features + uint8_t remote_supported_feature_eSCO; + // errands uint32_t authentication_flags; @@ -850,6 +853,8 @@ void hci_disconnect_security_block(hci_con_handle_t con_handle); // send complete CMD packet int hci_send_cmd_packet(uint8_t *packet, int size); +// query if remote side supports eSCO +int hci_remote_eSCO_supported(hci_con_handle_t con_handle); /* API_START */ From 2b5b557b38b94bd0be4768eeef5051911b0abcea Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 27 Nov 2015 22:12:07 +0100 Subject: [PATCH 54/72] hfp hf: activate/deactivate call waiting notification --- src/hfp.h | 4 +++- src/hfp_hf.c | 37 +++++++++++++++++++++++++++++++++++++ src/hfp_hf.h | 11 +++++++++++ test/pts/hfp_hf_test.c | 15 +++++++++++++-- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index 6e1481e16..8a84d5100 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -545,7 +545,9 @@ typedef struct hfp_connection { uint8_t hf_send_clip_enable; uint8_t hf_send_chup; - + uint8_t hf_activate_call_waiting_notification; + uint8_t hf_deactivate_call_waiting_notification; + } hfp_connection_t; // UTILS_START : TODO move to utils diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 1b139057b..3beaedc0f 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -244,6 +244,12 @@ static int hfp_hf_send_clip_enable(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_set_call_waiting_notification_cmd(uint16_t cid, uint8_t activate){ + char buffer[40]; + sprintf(buffer, "%s=%d\r\n", HFP_ENABLE_CALL_WAITING_NOTIFICATION, activate); + return send_str_over_rfcomm(cid, buffer); +} + static int hfp_hf_initiate_outgoing_call_cmd(uint16_t cid){ char buffer[40]; sprintf(buffer, "%s%s;\r\n", HFP_CALL_PHONE_NUMBER, phone_number); @@ -495,6 +501,19 @@ static void hfp_run_for_context(hfp_connection_t * context){ done = call_setup_state_machine(context); } + if (context->hf_deactivate_call_waiting_notification){ + context->hf_deactivate_call_waiting_notification = 0; + context->ok_pending = 1; + hfp_hf_set_call_waiting_notification_cmd(context->rfcomm_cid, 0); + return; + } + + if (context->hf_activate_call_waiting_notification){ + context->hf_activate_call_waiting_notification = 0; + context->ok_pending = 1; + hfp_hf_set_call_waiting_notification_cmd(context->rfcomm_cid, 1); + return; + } if (context->hf_initiate_outgoing_call){ context->hf_initiate_outgoing_call = 0; @@ -951,3 +970,21 @@ void hfp_hf_redial_last_number(bd_addr_t bd_addr){ connection->hf_initiate_redial_last_number = 1; hfp_run_for_context(connection); } + +void hfp_hf_activate_call_waiting_notification(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); + + connection->hf_activate_call_waiting_notification = 1; + hfp_run_for_context(connection); +} + + +void hfp_hf_deactivate_call_waiting_notification(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); + + connection->hf_deactivate_call_waiting_notification = 1; + hfp_run_for_context(connection); +} + diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 43e2f862d..1cf263748 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -184,6 +184,17 @@ void hfp_hf_dial_memory(bd_addr_t bd_addr, char * number); */ void hfp_hf_redial_last_number(bd_addr_t bd_addr); +/* + * @brief + */ +void hfp_hf_activate_call_waiting_notification(bd_addr_t bd_addr); + +/* + * @brief + */ +void hfp_hf_deactivate_call_waiting_notification(bd_addr_t bd_addr); + + /* API_END */ #if defined __cplusplus diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 26f4931bb..fdf115a4d 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -102,7 +102,7 @@ static void show_usage(void){ printf("c - disable registration status update for all AG indicators\n"); printf("C - enable registration status update for all AG indicators\n"); - printf("d - Query network operator.\n"); + printf("d - query network operator.\n"); printf("D - set HFP AG registration status update for individual indicators\n"); printf("e - disable reporting of the extended AG error result code\n"); @@ -112,7 +112,7 @@ static void show_usage(void){ printf("F - Hangup call\n"); printf("g - query network operator name\n"); - printf("G - Reject call.\n"); + printf("G - reject call\n"); printf("h - enable Calling Line Identification.\n"); @@ -122,6 +122,9 @@ static void show_usage(void){ printf("j - dial #1\n"); printf("J - dial #99\n"); + printf("k - deactivate call waiting notification\n"); + printf("K - activate call waiting notification\n"); + printf("t - terminate connection\n"); printf("---\n"); @@ -215,6 +218,14 @@ static int stdin_process(struct data_source *ds){ printf("Dial #99\n"); hfp_hf_dial_memory(device_addr,"#99"); break; + case 'k': + printf("Deactivate call waiting notification\n"); + hfp_hf_deactivate_call_waiting_notification(device_addr); + break; + case 'K': + printf("Activate call waiting notification\n"); + hfp_hf_activate_call_waiting_notification(device_addr); + break; default: show_usage(); break; From 94e4aaa2aeeec85f32a238213d1b04ea33f011ba Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 22:24:04 +0100 Subject: [PATCH 55/72] use remote supported feature 'eSCO' from parallel ACL connection --- src/hci.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/hci.c b/src/hci.c index 7d2d9def9..abe5b6c98 100644 --- a/src/hci.c +++ b/src/hci.c @@ -2322,18 +2322,20 @@ void hci_run(void){ return; case RECEIVED_CONNECTION_REQUEST: - log_info("sending hci_accept_connection_request"); + log_info("sending hci_accept_connection_request, remote eSCO %u", connection->remote_supported_feature_eSCO); connection->state = ACCEPTED_CONNECTION_REQUEST; connection->role = HCI_ROLE_SLAVE; if (connection->address_type == BD_ADDR_TYPE_CLASSIC){ hci_send_cmd(&hci_accept_connection_request, connection->address, 1); } else { - if (connection->remote_supported_feature_eSCO){ - // S4 - max latency == transmission interval = 0x000c == 12 ms + // remote supported features are not set for this hci_connection_t struct, but there must be an existing ACL connection already + hci_connection_t * base_acl_connection = hci_connection_for_bd_addr_and_type(connection->address, BD_ADDR_TYPE_CLASSIC); + if (!base_acl_connection || !base_acl_connection->remote_supported_feature_eSCO){ + // max latency, retransmission interval: N/A. any packet type hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0x02, 0x003f); } else { - // max latency, retransmission interval: N/A. any packet type - hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0xff, 0x003f); + // S4 - max latency == transmission interval = 0x000c == 12 ms, + hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0x02, 0x388); } } return; From 4d92c0d57cf5661284e11f1d0b8deea65e1eab18 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 22:32:01 +0100 Subject: [PATCH 56/72] hfp: support different link settings for audio connection, fallback to lower one on error. send ring only after audio connection is established when in-band ring is active --- src/hfp.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/hfp.h | 4 +++- src/hfp_ag.c | 25 ++++++++++++++++++------- 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index 96c15a96d..8c43c9fca 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -516,12 +516,38 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t break; case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{ + bt_flip_addr(event_addr, &packet[5]); int index = 2; uint8_t status = packet[index++]; if (status != 0){ - log_error("(e)SCO Connection is not established, status %u", status); + log_error("(e)SCO Connection failed status %u", status); + // if outgoing && link_setting != d0 && appropriate error + if (status != 0x11) break; // invalid params + context = get_hfp_connection_context_for_bd_addr(event_addr); + if (!context) break; + switch (context->link_setting){ + case HFP_LINK_SETTINGS_D0: + return; // no other option left + case HFP_LINK_SETTINGS_D1: + // context->link_setting = HFP_LINK_SETTINGS_D0; + // break; + case HFP_LINK_SETTINGS_S1: + // context->link_setting = HFP_LINK_SETTINGS_D1; + // break; + case HFP_LINK_SETTINGS_S2: + case HFP_LINK_SETTINGS_S3: + case HFP_LINK_SETTINGS_S4: + // context->link_setting = HFP_LINK_SETTINGS_S1; + // break; + case HFP_LINK_SETTINGS_T1: + case HFP_LINK_SETTINGS_T2: + // context->link_setting = HFP_LINK_SETTINGS_S3; + context->link_setting = HFP_LINK_SETTINGS_D0; + break; + } + context->establish_audio_connection = 1; break; } @@ -1238,4 +1264,24 @@ void hfp_release_audio_connection(hfp_connection_t * context){ context->release_audio_connection = 1; } +static const struct link_settings { + const uint16_t max_latency; + const uint8_t retransmission_effort; + const uint16_t packet_types; +} hfp_link_settings [] = { + { 0xffff, 0xff, 0x03c1 }, // HFP_LINK_SETTINGS_D0, HV1 + { 0xffff, 0xff, 0x03c4 }, // HFP_LINK_SETTINGS_D1, HV3 + { 0x0007, 0x01, 0x03c8 }, // HFP_LINK_SETTINGS_S1, EV3 + { 0x0007, 0x01, 0x0380 }, // HFP_LINK_SETTINGS_S2, 2-EV3 + { 0x000a, 0x01, 0x0380 }, // HFP_LINK_SETTINGS_S3, 2-EV3 + { 0x000c, 0x02, 0x0380 }, // HFP_LINK_SETTINGS_S4, 2-EV3 + { 0x0008, 0x02, 0x03c8 }, // HFP_LINK_SETTINGS_T1, EV3 + { 0x000d, 0x02, 0x0380 } // HFP_LINK_SETTINGS_T2, 2-EV3 +}; +void hfp_setup_synchronous_connection(hci_con_handle_t handle, hfp_link_setttings_t setting){ + // all packet types, fixed bandwidth + log_info("hfp_setup_synchronous_connection using setting nr %u", setting); + hci_send_cmd(&hci_setup_synchronous_connection, handle, 8000, 8000, hfp_link_settings[setting].max_latency, + hci_get_sco_voice_setting(), hfp_link_settings[setting].retransmission_effort, hfp_link_settings[setting].packet_types); // all types 0x003f, only 2-ev3 0x380 +} diff --git a/src/hfp.h b/src/hfp.h index 209bf8f7d..e847a1e0c 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -520,6 +520,8 @@ typedef struct hfp_connection { uint8_t suggested_codec; uint8_t codec_confirmed; + hfp_link_setttings_t link_setting; + uint8_t establish_audio_connection; uint8_t release_audio_connection; @@ -586,7 +588,7 @@ void hfp_reset_context_flags(hfp_connection_t * context); void hfp_release_audio_connection(hfp_connection_t * context); -void hfp_setup_synchronous_connection(uint16_t handle, hfp_link_setttings_t link_settings); +void hfp_setup_synchronous_connection(hci_con_handle_t handle, hfp_link_setttings_t link_settings); void hfp_accept_synchronous_connection(bd_addr_t addr, hfp_link_setttings_t link_settings); const char * hfp_hf_feature(int index); diff --git a/src/hfp_ag.c b/src/hfp_ag.c index b48d19ea9..82c944bbd 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -541,6 +541,16 @@ static void hfp_ag_slc_established(hfp_connection_t * context){ hfp_ag_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ hfp_ag_hf_start_ringing(context); } + + // determine highest possible link setting + context->link_setting = HFP_LINK_SETTINGS_D1; + if (hci_remote_eSCO_supported(context->con_handle)){ + context->link_setting = HFP_LINK_SETTINGS_S3; + if ((context->remote_supported_features & (1<link_setting = HFP_LINK_SETTINGS_S4; + } + } } static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * context){ @@ -681,7 +691,6 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio return 0; } - static int hfp_ag_run_for_audio_connection(hfp_connection_t * context){ if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED || context->state > HFP_W2_DISCONNECT_SCO) return 0; @@ -705,10 +714,8 @@ static int hfp_ag_run_for_audio_connection(hfp_connection_t * context){ if (context->establish_audio_connection){ context->state = HFP_W4_SCO_CONNECTED; context->establish_audio_connection = 0; - // only support HV1 + HV3 to avoid eSCO - hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x03c5); + hfp_setup_synchronous_connection(context->con_handle, context->link_setting); return 1; - } return 0; } @@ -757,13 +764,13 @@ static void hfp_timeout_stop(hfp_connection_t * context){ // static void hfp_ag_hf_start_ringing(hfp_connection_t * context){ - hfp_timeout_start(context); - context->ag_ring = 1; - context->ag_send_clip = clip_type && context->clip_enabled; if (use_in_band_tone()){ context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING; hfp_ag_establish_audio_connection(context->remote_addr); } else { + hfp_timeout_start(context); + context->ag_ring = 1; + context->ag_send_clip = clip_type && context->clip_enabled; context->call_state = HFP_CALL_RINGING; hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0); } @@ -988,6 +995,10 @@ static int call_setup_state_machine(hfp_connection_t * connection){ case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING: if (connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; // we got event: audio connection established + hfp_timeout_start(connection); + connection->ag_ring = 1; + connection->ag_send_clip = clip_type && connection->clip_enabled; + connection->call_state = HFP_CALL_RINGING; connection->call_state = HFP_CALL_RINGING; hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0); break; From f3a16b9a42c74a4fe242b2e94172927f8672c956 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 22:55:32 +0100 Subject: [PATCH 57/72] use incoming link type to decide on params for accept sync connection --- src/hci.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/hci.c b/src/hci.c index abe5b6c98..83d39b5d7 100644 --- a/src/hci.c +++ b/src/hci.c @@ -1330,6 +1330,10 @@ static void event_handler(uint8_t *packet, int size){ } conn->role = HCI_ROLE_SLAVE; conn->state = RECEIVED_CONNECTION_REQUEST; + // store info about eSCO + if (link_type == 0x02){ + conn->remote_supported_feature_eSCO = 1; + } hci_run(); break; @@ -2328,14 +2332,13 @@ void hci_run(void){ if (connection->address_type == BD_ADDR_TYPE_CLASSIC){ hci_send_cmd(&hci_accept_connection_request, connection->address, 1); } else { - // remote supported features are not set for this hci_connection_t struct, but there must be an existing ACL connection already - hci_connection_t * base_acl_connection = hci_connection_for_bd_addr_and_type(connection->address, BD_ADDR_TYPE_CLASSIC); - if (!base_acl_connection || !base_acl_connection->remote_supported_feature_eSCO){ - // max latency, retransmission interval: N/A. any packet type - hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0x02, 0x003f); - } else { - // S4 - max latency == transmission interval = 0x000c == 12 ms, + // remote supported feature eSCO is set if link type is eSCO + if (connection->remote_supported_feature_eSCO){ + // eSCO: S4 - max latency == transmission interval = 0x000c == 12 ms, hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0x02, 0x388); + } else { + // SCO: max latency, retransmission interval: N/A. any packet type + hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0xffff, hci_stack->sco_voice_setting, 0xff, 0x003f); } } return; From 65928b7817f40bc9515d26101300dc8d21e220c9 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:05:45 +0100 Subject: [PATCH 58/72] hfp: extract hfp_init_link_settings --- src/hfp_ag.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 82c944bbd..046bc536d 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -528,10 +528,24 @@ static int codecs_exchange_state_machine(hfp_connection_t * context){ return 0; } +static void hfp_init_link_settings(hfp_connection_t * context){ + // determine highest possible link setting + context->link_setting = HFP_LINK_SETTINGS_D1; + if (hci_remote_eSCO_supported(context->con_handle)){ + context->link_setting = HFP_LINK_SETTINGS_S3; + if ((context->remote_supported_features & (1<link_setting = HFP_LINK_SETTINGS_S4; + } + } +} + static void hfp_ag_slc_established(hfp_connection_t * context){ context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + hfp_init_link_settings(context); + // if active call exist, set per-connection state active, too (when audio is on) if (hfp_ag_call_state == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE; @@ -541,16 +555,6 @@ static void hfp_ag_slc_established(hfp_connection_t * context){ hfp_ag_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ hfp_ag_hf_start_ringing(context); } - - // determine highest possible link setting - context->link_setting = HFP_LINK_SETTINGS_D1; - if (hci_remote_eSCO_supported(context->con_handle)){ - context->link_setting = HFP_LINK_SETTINGS_S3; - if ((context->remote_supported_features & (1<link_setting = HFP_LINK_SETTINGS_S4; - } - } } static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * context){ From 400b045254eaf99d7b2510c71afdfadf5e6bf359 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 27 Nov 2015 23:09:59 +0100 Subject: [PATCH 59/72] hfp: activate/deactivate functions, set microphone and speaker gain --- src/hfp.c | 2 +- src/hfp.h | 9 +- src/hfp_hf.c | 197 +++++++++++++++++++++++++++++++++++++---- src/hfp_hf.h | 45 ++++++++-- test/pts/hfp_hf_test.c | 67 ++++++++++++++ 5 files changed, 294 insertions(+), 26 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index 96c15a96d..9e3c0ee3e 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -670,7 +670,7 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ return HFP_CMD_CALL_PHONE_NUMBER; } - if (strncmp(line_buffer, HFP_REDIAL_LAST_NUMBER, strlen(HFP_REDIAL_LAST_NUMBER)) == 0){ + if (strncmp(line_buffer+offset, HFP_REDIAL_LAST_NUMBER, strlen(HFP_REDIAL_LAST_NUMBER)) == 0){ return HFP_CMD_REDIAL_LAST_NUMBER; } diff --git a/src/hfp.h b/src/hfp.h index 8a84d5100..823c8fde5 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -124,7 +124,7 @@ extern "C" { #define HFP_HANG_UP_CALL "+CHUP" #define HFP_CHANGE_IN_BAND_RING_TONE_SETTING "+BSIR" #define HFP_CALL_PHONE_NUMBER "ATD" -#define HFP_REDIAL_LAST_NUMBER "AT+BLDN" +#define HFP_REDIAL_LAST_NUMBER "+BLDN" #define HFP_TURN_OFF_EC_AND_NR "+NREC" // EC (Echo CAnceling), NR (Noise Reduction) #define HFP_ACTIVATE_VOICE_RECOGNITION "+BVRA" // EC (Echo CAnceling), NR (Noise Reduction) #define HFP_SET_MICROPHONE_GAIN "+VGM" @@ -548,6 +548,13 @@ typedef struct hfp_connection { uint8_t hf_activate_call_waiting_notification; uint8_t hf_deactivate_call_waiting_notification; + uint8_t hf_activate_calling_line_notification; + uint8_t hf_deactivate_calling_line_notification; + uint8_t hf_activate_echo_canceling_and_noise_reduction; + uint8_t hf_deactivate_echo_canceling_and_noise_reduction; + uint8_t hf_activate_voice_recognition_notification; + uint8_t hf_deactivate_voice_recognition_notification; + } hfp_connection_t; // UTILS_START : TODO move to utils diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 3beaedc0f..47f5654a2 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -238,15 +238,39 @@ static int hfp_hf_cmd_ata(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } -static int hfp_hf_send_clip_enable(uint16_t cid){ - char buffer[20]; - sprintf(buffer, "AT%s=1\r\n", HFP_ENABLE_CLIP); +static int hfp_hf_set_microphone_gain_cmd(uint16_t cid, int gain){ + char buffer[40]; + sprintf(buffer, "AT%s=%d\r\n", HFP_SET_MICROPHONE_GAIN, gain); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_hf_set_speaker_gain_cmd(uint16_t cid, int gain){ + char buffer[40]; + sprintf(buffer, "AT%s=%d\r\n", HFP_SET_SPEAKER_GAIN, gain); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_hf_set_calling_line_notification_cmd(uint16_t cid, uint8_t activate){ + char buffer[40]; + sprintf(buffer, "AT%s=%d\r\n", HFP_ENABLE_CLIP, activate); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_hf_set_echo_canceling_and_noise_reduction_cmd(uint16_t cid, uint8_t activate){ + char buffer[40]; + sprintf(buffer, "AT%s=%d\r\n", HFP_TURN_OFF_EC_AND_NR, activate); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_hf_set_voice_recognition_notification_cmd(uint16_t cid, uint8_t activate){ + char buffer[40]; + sprintf(buffer, "AT%s=%d\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, activate); return send_str_over_rfcomm(cid, buffer); } static int hfp_hf_set_call_waiting_notification_cmd(uint16_t cid, uint8_t activate){ char buffer[40]; - sprintf(buffer, "%s=%d\r\n", HFP_ENABLE_CALL_WAITING_NOTIFICATION, activate); + sprintf(buffer, "AT%s=%d\r\n", HFP_ENABLE_CALL_WAITING_NOTIFICATION, activate); return send_str_over_rfcomm(cid, buffer); } @@ -264,7 +288,7 @@ static int hfp_hf_send_memory_dial_cmd(uint16_t cid){ static int hfp_hf_send_redial_last_number_cmd(uint16_t cid){ char buffer[20]; - sprintf(buffer, "%s\r\n", HFP_REDIAL_LAST_NUMBER); + sprintf(buffer, "AT%s\r\n", HFP_REDIAL_LAST_NUMBER); return send_str_over_rfcomm(cid, buffer); } @@ -500,7 +524,64 @@ static void hfp_run_for_context(hfp_connection_t * context){ if (!done){ done = call_setup_state_machine(context); } + + if (context->send_microphone_gain){ + context->send_microphone_gain = 0; + context->ok_pending = 1; + hfp_hf_set_microphone_gain_cmd(context->rfcomm_cid, context->microphone_gain); + return; + } + + if (context->send_speaker_gain){ + context->send_speaker_gain = 0; + context->ok_pending = 1; + hfp_hf_set_speaker_gain_cmd(context->rfcomm_cid, context->speaker_gain); + return; + } + if (context->hf_deactivate_calling_line_notification){ + context->hf_deactivate_calling_line_notification = 0; + context->ok_pending = 1; + hfp_hf_set_calling_line_notification_cmd(context->rfcomm_cid, 0); + return; + } + + if (context->hf_activate_calling_line_notification){ + context->hf_activate_calling_line_notification = 0; + context->ok_pending = 1; + hfp_hf_set_calling_line_notification_cmd(context->rfcomm_cid, 1); + return; + } + + if (context->hf_deactivate_echo_canceling_and_noise_reduction){ + context->hf_deactivate_echo_canceling_and_noise_reduction = 0; + context->ok_pending = 1; + hfp_hf_set_echo_canceling_and_noise_reduction_cmd(context->rfcomm_cid, 0); + return; + } + + if (context->hf_activate_echo_canceling_and_noise_reduction){ + context->hf_activate_echo_canceling_and_noise_reduction = 0; + context->ok_pending = 1; + hfp_hf_set_echo_canceling_and_noise_reduction_cmd(context->rfcomm_cid, 1); + return; + } + + if (context->hf_deactivate_voice_recognition_notification){ + context->hf_deactivate_voice_recognition_notification = 0; + context->ok_pending = 1; + hfp_hf_set_voice_recognition_notification_cmd(context->rfcomm_cid, 0); + return; + } + + if (context->hf_activate_voice_recognition_notification){ + context->hf_activate_voice_recognition_notification = 0; + context->ok_pending = 1; + hfp_hf_set_voice_recognition_notification_cmd(context->rfcomm_cid, 1); + return; + } + + if (context->hf_deactivate_call_waiting_notification){ context->hf_deactivate_call_waiting_notification = 0; context->ok_pending = 1; @@ -536,13 +617,6 @@ static void hfp_run_for_context(hfp_connection_t * context){ return; } - if (context->hf_send_clip_enable){ - context->hf_send_clip_enable = 0; - context->ok_pending = 1; - hfp_hf_send_clip_enable(context->rfcomm_cid); - return; - } - if (context->hf_send_chup){ context->hf_send_chup = 0; context->ok_pending = 1; @@ -696,6 +770,7 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 context->ok_pending = 0; hfp_reset_context_flags(context); hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1); + context->command = HFP_CMD_NONE; break; case HFP_CMD_OK: hfp_hf_switch_on_ok(context); @@ -938,13 +1013,6 @@ void hfp_hf_reject_call(bd_addr_t bd_addr){ } } -void hfp_hf_enable_calling_line_identification(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); - connection->hf_send_clip_enable = 1; - hfp_run_for_context(connection); -} - void hfp_hf_dial_number(bd_addr_t bd_addr, char * number){ hfp_hf_establish_service_level_connection(bd_addr); hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); @@ -988,3 +1056,94 @@ void hfp_hf_deactivate_call_waiting_notification(bd_addr_t bd_addr){ hfp_run_for_context(connection); } + +void hfp_hf_activate_calling_line_notification(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); + + connection->hf_deactivate_calling_line_notification = 1; + hfp_run_for_context(connection); +} + +/* + * @brief + */ +void hfp_hf_deactivate_calling_line_notification(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); + + connection->hf_deactivate_calling_line_notification = 1; + hfp_run_for_context(connection); +} + + +/* + * @brief + */ +void hfp_hf_activate_echo_canceling_and_noise_reduction(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); + + connection->hf_deactivate_echo_canceling_and_noise_reduction = 1; + hfp_run_for_context(connection); +} + +/* + * @brief + */ +void hfp_hf_deactivate_echo_canceling_and_noise_reduction(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); + + connection->hf_deactivate_echo_canceling_and_noise_reduction = 1; + hfp_run_for_context(connection); +} + +/* + * @brief + */ +void hfp_hf_activate_voice_recognition_notification(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); + + connection->hf_deactivate_voice_recognition_notification = 1; + hfp_run_for_context(connection); +} + +/* + * @brief + */ +void hfp_hf_deactivate_voice_recognition_notification(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); + + connection->hf_deactivate_voice_recognition_notification = 1; + hfp_run_for_context(connection); +} + +/* + * @brief + */ +void hfp_hf_set_microphone_gain(bd_addr_t bd_addr, int gain){ + hfp_hf_establish_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + if (connection->microphone_gain == gain) return; + + connection->microphone_gain = gain; + connection->send_microphone_gain = 1; + hfp_run_for_context(connection); +} + +/* + * @brief + */ +void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain){ + hfp_hf_establish_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + if (connection->speaker_gain == gain) return; + + connection->speaker_gain = gain; + connection->send_speaker_gain = 1; + hfp_run_for_context(connection); +} + diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 1cf263748..c04683273 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -164,11 +164,6 @@ void hfp_hf_reject_call(bd_addr_t bd_addr); */ void hfp_hf_terminate_call(bd_addr_t bd_addr); -/** - * @brief - */ -void hfp_hf_enable_calling_line_identification(bd_addr_t bd_addr); - /** * @brief */ @@ -194,6 +189,46 @@ void hfp_hf_activate_call_waiting_notification(bd_addr_t bd_addr); */ void hfp_hf_deactivate_call_waiting_notification(bd_addr_t bd_addr); +/* + * @brief + */ +void hfp_hf_activate_calling_line_notification(bd_addr_t bd_addr); + +/* + * @brief + */ +void hfp_hf_deactivate_calling_line_notification(bd_addr_t bd_addr); + + +/* + * @brief + */ +void hfp_hf_activate_echo_canceling_and_noise_reduction(bd_addr_t bd_addr); + +/* + * @brief + */ +void hfp_hf_deactivate_echo_canceling_and_noise_reduction(bd_addr_t bd_addr); + +/* + * @brief + */ +void hfp_hf_activate_voice_recognition_notification(bd_addr_t bd_addr); + +/* + * @brief + */ +void hfp_hf_deactivate_voice_recognition_notification(bd_addr_t bd_addr); + +/* + * @brief + */ +void hfp_hf_set_microphone_gain(bd_addr_t bd_addr, int gain); + +/* + * @brief + */ +void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain); /* API_END */ diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index fdf115a4d..5b7ec5056 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -125,6 +125,25 @@ static void show_usage(void){ printf("k - deactivate call waiting notification\n"); printf("K - activate call waiting notification\n"); + printf("l - deactivate calling line notification\n"); + printf("L - activate calling line notification\n"); + + printf("m - deactivate echo canceling and noise reduction\n"); + printf("M - activate echo canceling and noise reduction\n"); + + printf("n - deactivate voice recognition notification\n"); + printf("N - activate voice recognition notification\n"); + + printf("o - Set speaker volume to 0 (minimum)\n"); + printf("O - Set speaker volume to 9 (default)\n"); + printf("p - Set speaker volume to 12 (higher)\n"); + printf("P - Set speaker volume to 15 (maximum)\n"); + + printf("q - Set microphone gain to 0 (minimum)\n"); + printf("Q - Set microphone gain to 9 (default)\n"); + printf("s - Set microphone gain to 12 (higher)\n"); + printf("S - Set microphone gain to 15 (maximum)\n"); + printf("t - terminate connection\n"); printf("---\n"); @@ -226,6 +245,54 @@ static int stdin_process(struct data_source *ds){ printf("Activate call waiting notification\n"); hfp_hf_activate_call_waiting_notification(device_addr); break; + case 'l': + printf("Deactivate calling line notification\n"); + hfp_hf_deactivate_calling_line_notification(device_addr); + break; + case 'L': + printf("Activate calling line notification\n"); + hfp_hf_activate_calling_line_notification(device_addr); + break; + case 'm': + printf("Deactivate echo canceling and noise reduction\n"); + hfp_hf_deactivate_echo_canceling_and_noise_reduction(device_addr); + break; + case 'M': + printf("Activate echo canceling and noise reduction\n"); + hfp_hf_activate_echo_canceling_and_noise_reduction(device_addr); + break; + case 'n': + printf("Deactivate voice recognition notification\n"); + hfp_hf_deactivate_voice_recognition_notification(device_addr); + break; + case 'N': + printf("Activate voice recognition notification\n"); + hfp_hf_activate_voice_recognition_notification(device_addr); + break; + case 'o': + printf("Set speaker gain to 0 (minimum)\n"); + hfp_hf_set_speaker_gain(device_addr, 0); + break; + case 'O': + printf("Set speaker gain to 9 (default)\n"); + hfp_hf_set_speaker_gain(device_addr, 9); + break; + case 'p': + printf("Set speaker gain to 12 (higher)\n"); + hfp_hf_set_speaker_gain(device_addr, 12); + break; + case 'P': + printf("Set speaker gain to 15 (maximum)\n"); + hfp_hf_set_speaker_gain(device_addr, 15); + break; + case 'q': + printf("Set microphone gain to 0\n"); + hfp_hf_set_microphone_gain(device_addr, 0); + break; + case 'Q': + printf("Set microphone gain to 9\n"); + hfp_hf_set_microphone_gain(device_addr, 9); + break; default: show_usage(); break; From 490caf51a06d005bf345bbe39836864ea3b0321c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:10:44 +0100 Subject: [PATCH 60/72] hfp: extrct hfp_ag_slc_established --- src/hfp_hf.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index da8decaa8..04a65cc64 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -501,6 +501,10 @@ static void hfp_run_for_context(hfp_connection_t * context){ break; } } +static void hfp_ag_slc_established(hfp_connection_t * context){ + context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); +} static void hfp_hf_switch_on_ok(hfp_connection_t *context){ context->ok_pending = 0; @@ -535,8 +539,7 @@ static void hfp_hf_switch_on_ok(hfp_connection_t *context){ context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; break; } - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + hfp_ag_slc_established(context); break; case HFP_W4_RETRIEVE_CAN_HOLD_CALL: @@ -544,8 +547,7 @@ static void hfp_hf_switch_on_ok(hfp_connection_t *context){ context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; break; } - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + hfp_ag_slc_established(context); break; case HFP_W4_LIST_GENERIC_STATUS_INDICATORS: @@ -557,8 +559,7 @@ static void hfp_hf_switch_on_ok(hfp_connection_t *context){ break; case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + hfp_ag_slc_established(context); break; case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: if (context->enable_status_update_for_ag_indicators != 0xFF){ From 51506e16d29f4c9720711ea4bc184c194dd7cb9e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:24:20 +0100 Subject: [PATCH 61/72] also retry (e)sco connection setup on status 0x1f - unspecified error --- src/hfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hfp.c b/src/hfp.c index 8c43c9fca..13e134762 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -524,7 +524,7 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t if (status != 0){ log_error("(e)SCO Connection failed status %u", status); // if outgoing && link_setting != d0 && appropriate error - if (status != 0x11) break; // invalid params + if (status != 0x11 && status != 0x1f) break; // invalid params / unspecified error context = get_hfp_connection_context_for_bd_addr(event_addr); if (!context) break; switch (context->link_setting){ From 97a9051bb002ed6d5cf9b5611798099ceab96b9f Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:24:49 +0100 Subject: [PATCH 62/72] hfp: implement (e)SCO link setting fallbacks for HF --- src/hfp_hf.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 04a65cc64..633d123b8 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -443,8 +443,7 @@ static int hfp_hf_run_for_audio_connection(hfp_connection_t * context){ if (context->establish_audio_connection){ context->state = HFP_W4_SCO_CONNECTED; context->establish_audio_connection = 0; - // only support HV1 + HV3 to avoid eSCO - hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x03c5); + hfp_setup_synchronous_connection(context->con_handle, context->link_setting); return 1; } @@ -501,9 +500,23 @@ static void hfp_run_for_context(hfp_connection_t * context){ break; } } + +static void hfp_init_link_settings(hfp_connection_t * context){ + // determine highest possible link setting + context->link_setting = HFP_LINK_SETTINGS_D1; + if (hci_remote_eSCO_supported(context->con_handle)){ + context->link_setting = HFP_LINK_SETTINGS_S3; + if ((hfp_supported_features & (1<remote_supported_features & (1<link_setting = HFP_LINK_SETTINGS_S4; + } + } +} + static void hfp_ag_slc_established(hfp_connection_t * context){ context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + hfp_init_link_settings(context); } static void hfp_hf_switch_on_ok(hfp_connection_t *context){ From 5ea0bfe3aac091b8a032e06ddcf9ae27005105b4 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:28:41 +0100 Subject: [PATCH 63/72] remove unused prototype --- src/hfp.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hfp.h b/src/hfp.h index e847a1e0c..f4bb0b26b 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -589,7 +589,6 @@ void hfp_reset_context_flags(hfp_connection_t * context); void hfp_release_audio_connection(hfp_connection_t * context); void hfp_setup_synchronous_connection(hci_con_handle_t handle, hfp_link_setttings_t link_settings); -void hfp_accept_synchronous_connection(bd_addr_t addr, hfp_link_setttings_t link_settings); const char * hfp_hf_feature(int index); const char * hfp_ag_feature(int index); From f4fcbcbedead6eb6d28b610e23fbbf7ee340723a Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:32:17 +0100 Subject: [PATCH 64/72] fix compile --- test/pts/hfp_hf_test.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 017d1d084..67c3b2a32 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -114,8 +114,6 @@ static void show_usage(void){ printf("g - query network operator name\n"); printf("G - reject call\n"); - printf("h - enable Calling Line Identification.\n"); - printf("i - dial 1234567\n"); printf("I - redial\n"); @@ -209,10 +207,6 @@ static int stdin_process(struct data_source *ds){ printf("Query operator.\n"); hfp_hf_query_operator_selection(device_addr); break; - case 'h': - printf("Enable Calling Line Identification.\n"); - hfp_hf_enable_calling_line_identification(device_addr); - break; case 't': printf("Terminate HCI connection.\n"); gap_disconnect(handle); From d709d01d110b958f9229f716620cd1501e32f353 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 27 Nov 2015 23:47:35 +0100 Subject: [PATCH 65/72] hfp: also terminate outgoing call --- src/hfp_hf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index e6132a305..ba63a4495 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -1009,12 +1009,12 @@ void hfp_hf_terminate_call(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 (hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ - connection->hf_send_chup = 1; - hfp_run_for_context(connection); - } else { - log_error("HFP HF: terminating incoming call with wrong call status %u", hfp_call_status); - } + // if (hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ + connection->hf_send_chup = 1; + hfp_run_for_context(connection); + // } else { + // log_error("HFP HF: terminating incoming call with wrong call status %u", hfp_call_status); + // } } void hfp_hf_reject_call(bd_addr_t bd_addr){ From fc81baeef42571bf5097862a27abe69a81b232e4 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 28 Nov 2015 00:00:37 +0100 Subject: [PATCH 66/72] fix memory dial in hf test, set EC/NR feature --- test/pts/hfp_hf_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 67c3b2a32..5b4d95252 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -225,11 +225,11 @@ static int stdin_process(struct data_source *ds){ break; case 'j': printf("Dial #1\n"); - hfp_hf_dial_memory(device_addr,"#1"); + hfp_hf_dial_memory(device_addr,"1"); break; case 'J': printf("Dial #99\n"); - hfp_hf_dial_memory(device_addr,"#99"); + hfp_hf_dial_memory(device_addr,"99"); break; case 'k': printf("Deactivate call waiting notification\n"); @@ -357,7 +357,7 @@ int btstack_main(int argc, const char * argv[]){ rfcomm_init(); // hfp_hf_init(rfcomm_channel_nr, HFP_DEFAULT_HF_SUPPORTED_FEATURES, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); - hfp_hf_init(rfcomm_channel_nr, 438 | (1< Date: Sat, 28 Nov 2015 00:25:54 +0100 Subject: [PATCH 67/72] fix typo --- src/hfp_hf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hfp_hf.c b/src/hfp_hf.c index ba63a4495..1e644ebb2 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -1120,7 +1120,7 @@ void hfp_hf_activate_voice_recognition_notification(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); - connection->hf_deactivate_voice_recognition_notification = 1; + connection->hf_activate_voice_recognition_notification = 1; hfp_run_for_context(connection); } From 65323bbc4519b60953ffb1f1cb885ce9ed00fb50 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 28 Nov 2015 00:26:10 +0100 Subject: [PATCH 68/72] improve command text --- test/pts/hfp_hf_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 5b4d95252..3635d6cb0 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -129,8 +129,8 @@ static void show_usage(void){ printf("m - deactivate echo canceling and noise reduction\n"); printf("M - activate echo canceling and noise reduction\n"); - printf("n - deactivate voice recognition notification\n"); - printf("N - activate voice recognition notification\n"); + printf("n - deactivate voice recognition\n"); + printf("N - activate voice recognition\n"); printf("o - Set speaker volume to 0 (minimum)\n"); printf("O - Set speaker volume to 9 (default)\n"); @@ -256,11 +256,11 @@ static int stdin_process(struct data_source *ds){ hfp_hf_activate_echo_canceling_and_noise_reduction(device_addr); break; case 'n': - printf("Deactivate voice recognition notification\n"); + printf("Deactivate voice recognition\n"); hfp_hf_deactivate_voice_recognition_notification(device_addr); break; case 'N': - printf("Activate voice recognition notification\n"); + printf("Activate voice recognition\n"); hfp_hf_activate_voice_recognition_notification(device_addr); break; case 'o': From 26fa4657e85eb8ab0521418d86b8b82060acdb95 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 28 Nov 2015 11:13:50 +0100 Subject: [PATCH 69/72] hfp: support three-way calls in HF tester --- src/hfp.h | 5 +++ src/hfp_hf.c | 100 +++++++++++++++++++++++++++++++++++++++++ src/hfp_hf.h | 25 +++++++++++ test/pts/hfp_hf_test.c | 39 ++++++++++++++-- 4 files changed, 165 insertions(+), 4 deletions(-) diff --git a/src/hfp.h b/src/hfp.h index 2add3afd5..8d37895ba 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -558,6 +558,11 @@ typedef struct hfp_connection { uint8_t hf_send_clip_enable; uint8_t hf_send_chup; + uint8_t hf_send_chld_0; + uint8_t hf_send_chld_1; + uint8_t hf_send_chld_2; + uint8_t hf_send_chld_3; + uint8_t hf_send_chld_4; uint8_t hf_activate_call_waiting_notification; uint8_t hf_deactivate_call_waiting_notification; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 1e644ebb2..fd028c5d5 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -298,6 +298,12 @@ static int hfp_hf_send_chup(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_send_chld(uint16_t cid, int number){ + char buffer[20]; + sprintf(buffer, "AT%s=%u\r\n", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, number); + return send_str_over_rfcomm(cid, buffer); +} + static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; uint8_t event[6+HFP_MAX_INDICATOR_DESC_SIZE+1]; @@ -623,6 +629,41 @@ static void hfp_run_for_context(hfp_connection_t * context){ return; } + if (context->hf_send_chld_0){ + context->hf_send_chld_0 = 0; + context->ok_pending = 1; + hfp_hf_send_chld(context->rfcomm_cid, 0); + return; + } + + if (context->hf_send_chld_1){ + context->hf_send_chld_1 = 0; + context->ok_pending = 1; + hfp_hf_send_chld(context->rfcomm_cid, 1); + return; + } + + if (context->hf_send_chld_2){ + context->hf_send_chld_2 = 0; + context->ok_pending = 1; + hfp_hf_send_chld(context->rfcomm_cid, 2); + return; + } + + if (context->hf_send_chld_3){ + context->hf_send_chld_3 = 0; + context->ok_pending = 1; + hfp_hf_send_chld(context->rfcomm_cid, 3); + return; + } + + if (context->hf_send_chld_4){ + context->hf_send_chld_4 = 0; + context->ok_pending = 1; + hfp_hf_send_chld(context->rfcomm_cid, 4); + return; + } + if (done) return; // deal with disconnect switch (context->state){ @@ -1027,6 +1068,65 @@ void hfp_hf_reject_call(bd_addr_t bd_addr){ } } +void hfp_hf_user_busy(bd_addr_t addr){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); + + if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ + connection->hf_send_chld_0 = 1; + hfp_run_for_context(connection); + } +} + +void hfp_hf_end_active_and_accept_other(bd_addr_t addr){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); + + if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS || + hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ + connection->hf_send_chld_1 = 1; + hfp_run_for_context(connection); + } +} + +void hfp_hf_swap_calls(bd_addr_t addr){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); + + if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS || + hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ + connection->hf_send_chld_2 = 1; + hfp_run_for_context(connection); + } +} + +void hfp_hf_join_held_call(bd_addr_t addr){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); + + if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS || + hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ + connection->hf_send_chld_3 = 1; + hfp_run_for_context(connection); + } +} + +void hfp_hf_connect_calls(bd_addr_t addr){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); + + if (hfp_callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS || + hfp_call_status == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ + connection->hf_send_chld_4 = 1; + hfp_run_for_context(connection); + } +} + +/** + * @brief + */ +void hfp_hf_connect_calls(bd_addr_t addr); + void hfp_hf_dial_number(bd_addr_t bd_addr, char * number){ hfp_hf_establish_service_level_connection(bd_addr); hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); diff --git a/src/hfp_hf.h b/src/hfp_hf.h index c04683273..232ab26e4 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -159,6 +159,31 @@ void hfp_hf_answer_incoming_call(bd_addr_t bd_addr); */ void hfp_hf_reject_call(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_hf_user_busy(bd_addr_t addr); + +/** + * @brief + */ +void hfp_hf_end_active_and_accept_other(bd_addr_t addr); + +/** + * @brief + */ +void hfp_hf_swap_calls(bd_addr_t addr); + +/** + * @brief + */ +void hfp_hf_join_held_call(bd_addr_t addr); + +/** + * @brief + */ +void hfp_hf_connect_calls(bd_addr_t addr); + /** * @brief */ diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 3635d6cb0..0fdf1d11a 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -115,7 +115,7 @@ static void show_usage(void){ printf("G - reject call\n"); printf("i - dial 1234567\n"); - printf("I - redial\n"); + printf("I - dial 7654321\n"); printf("j - dial #1\n"); printf("J - dial #99\n"); @@ -141,9 +141,16 @@ static void show_usage(void){ printf("Q - Set microphone gain to 9 (default)\n"); printf("s - Set microphone gain to 12 (higher)\n"); printf("S - Set microphone gain to 15 (maximum)\n"); - + printf("t - terminate connection\n"); + printf("u - send 'user busy' (Three-Way Call 0)\n"); + printf("U - end active call and accept other call' (Three-Way Call 1)\n"); + printf("v - Swap active call and hold/waiting call (Three-Way Call 2)\n"); + printf("V - Join held call (Three-Way Call 3)\n"); + printf("w - Connect calls (Three-Way Call 4)\n"); + printf("W - redial\n"); + printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -220,8 +227,8 @@ static int stdin_process(struct data_source *ds){ hfp_hf_dial_number(device_addr, "1234567"); break; case 'I': - printf("Redial\n"); - hfp_hf_redial_last_number(device_addr); + printf("Dial 7654321\n"); + hfp_hf_dial_number(device_addr, "7654321"); break; case 'j': printf("Dial #1\n"); @@ -287,6 +294,30 @@ static int stdin_process(struct data_source *ds){ printf("Set microphone gain to 9\n"); hfp_hf_set_microphone_gain(device_addr, 9); break; + case 'u': + printf("Send 'user busy' (Three-Way Call 0)\n"); + hfp_hf_user_busy(device_addr); + break; + case 'U': + printf("End active call and accept waiting/held call (Three-Way Call 1)\n"); + hfp_hf_end_active_and_accept_other(device_addr); + break; + case 'v': + printf("Swap active call and hold/waiting call (Three-Way Call 2)\n"); + hfp_hf_swap_calls(device_addr); + break; + case 'V': + printf("Join hold call (Three-Way Call 3)\n"); + hfp_hf_join_held_call(device_addr); + break; + case 'w': + printf("Connect calls (Three-Way Call 4)\n"); + hfp_hf_connect_calls(device_addr); + break; + case 'W': + printf("Redial\n"); + hfp_hf_redial_last_number(device_addr); + break; default: show_usage(); break; From 6d6770b5f909cfd32e9588048c7b609b5bbce033 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 28 Nov 2015 11:25:10 +0100 Subject: [PATCH 70/72] hfp: send DTMF codes --- src/hfp.h | 1 + src/hfp_hf.c | 23 +++++++++++++++++++++++ src/hfp_hf.h | 5 +++++ test/pts/hfp_hf_test.c | 15 +++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/hfp.h b/src/hfp.h index 8d37895ba..85838bbd1 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -563,6 +563,7 @@ typedef struct hfp_connection { uint8_t hf_send_chld_2; uint8_t hf_send_chld_3; uint8_t hf_send_chld_4; + char hf_send_dtmf_code; uint8_t hf_activate_call_waiting_notification; uint8_t hf_deactivate_call_waiting_notification; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index fd028c5d5..e7b6e6ddc 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -304,6 +304,12 @@ static int hfp_hf_send_chld(uint16_t cid, int number){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_send_dtmf(uint16_t cid, char code){ + char buffer[20]; + sprintf(buffer, "AT%s=%c\r\n", HFP_TRANSMIT_DTMF_CODES, code); + return send_str_over_rfcomm(cid, buffer); +} + static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; uint8_t event[6+HFP_MAX_INDICATOR_DESC_SIZE+1]; @@ -664,6 +670,14 @@ static void hfp_run_for_context(hfp_connection_t * context){ return; } + if (context->hf_send_dtmf_code){ + char code = context->hf_send_dtmf_code; + context->hf_send_dtmf_code = 0; + context->ok_pending = 1; + hfp_hf_send_dtmf(context->rfcomm_cid, code); + return; + } + if (done) return; // deal with disconnect switch (context->state){ @@ -1261,3 +1275,12 @@ void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain){ hfp_run_for_context(connection); } +/* + * @brief + */ +void hfp_hf_send_dtmf_code(bd_addr_t bd_addr, char code){ + hfp_hf_establish_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + connection->hf_send_dtmf_code = code; + hfp_run_for_context(connection); +} \ No newline at end of file diff --git a/src/hfp_hf.h b/src/hfp_hf.h index 232ab26e4..af538f99c 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -255,6 +255,11 @@ void hfp_hf_set_microphone_gain(bd_addr_t bd_addr, int gain); */ void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain); +/* + * @brief + */ +void hfp_hf_send_dtmf_code(bd_addr_t bd_addr, char code); + /* API_END */ #if defined __cplusplus diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 0fdf1d11a..09977618f 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -150,6 +150,7 @@ static void show_usage(void){ printf("V - Join held call (Three-Way Call 3)\n"); printf("w - Connect calls (Three-Way Call 4)\n"); printf("W - redial\n"); + printf("0123456789#*-+ - send DTMF dial tones\n"); printf("---\n"); printf("Ctrl-c - exit\n"); @@ -158,7 +159,21 @@ static void show_usage(void){ static int stdin_process(struct data_source *ds){ read(ds->fd, &cmd, 1); + + if (cmd >= '0' && cmd <= '9'){ + printf("DTMF Code: %c\n", cmd); + hfp_hf_send_dtmf_code(device_addr, cmd); + return 0; + } + switch (cmd){ + case '#': + case '-': + case '+': + case '*': + printf("DTMF Code: %c\n", cmd); + hfp_hf_send_dtmf_code(device_addr, cmd); + break; case 'a': printf("Establish Service level connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr)); hfp_hf_establish_service_level_connection(device_addr); From f36add6afd26f18db99a9373b7e36f41700999c5 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 28 Nov 2015 14:14:55 +0100 Subject: [PATCH 71/72] hfp: support attach phone number to voice tag by HF --- include/btstack/hci_cmds.h | 11 ++++++----- src/hfp.c | 17 ++++++++++------- src/hfp.h | 8 ++++++-- src/hfp_hf.c | 39 +++++++++++++++++++++++++++++++++----- src/hfp_hf.h | 5 +++++ test/pts/hfp_hf_test.c | 13 ++++++++++++- 6 files changed, 73 insertions(+), 20 deletions(-) diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 850e4c8af..39046457a 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -642,11 +642,12 @@ extern "C" { #define HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER 0x0D #define HFP_SUBEVENT_REDIAL_LAST_NUMBER 0x0E #define HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG 0x0F -#define HFP_SUBEVENT_TRANSMIT_DTMF_CODES 0x10 -#define HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL 0x11 -#define HFP_SUBEVENT_CALL_ANSWERED 0x12 -#define HFP_SUBEVENT_CONFERENCE_CALL 0x13 -#define HFP_SUBEVENT_RING 0x14 +#define HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG 0x10 +#define HFP_SUBEVENT_TRANSMIT_DTMF_CODES 0x11 +#define HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL 0x12 +#define HFP_SUBEVENT_CALL_ANSWERED 0x13 +#define HFP_SUBEVENT_CONFERENCE_CALL 0x14 +#define HFP_SUBEVENT_RING 0x15 // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp.c b/src/hfp.c index c8a740743..fc537ca5a 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -663,7 +663,7 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ } if (strncmp(line_buffer+offset, HFP_PHONE_NUMBER_FOR_VOICE_TAG, strlen(HFP_PHONE_NUMBER_FOR_VOICE_TAG)) == 0){ - if (isHandsFree) return HFP_CMD_AG_SEND_PHONE_NUMBER; + if (isHandsFree) return HFP_CMD_AG_SENT_PHONE_NUMBER; return HFP_CMD_HF_REQUEST_PHONE_NUMBER; } @@ -843,13 +843,8 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ return HFP_CMD_NONE; } -#if 0 -uint32_t fromBinary(char *s) { - return (uint32_t) strtol(s, NULL, 2); -} -#endif - static void hfp_parser_store_byte(hfp_connection_t * context, uint8_t byte){ + // printf("hfp_parser_store_byte %c at pos %u\n", (char) byte, context->line_size); // TODO: add limit context->line_buffer[context->line_size++] = byte; context->line_buffer[context->line_size] = 0; @@ -892,6 +887,7 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){ break; case HFP_PARSER_CMD_SEQUENCE: switch (context->command){ + case HFP_CMD_AG_SENT_PHONE_NUMBER: case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: @@ -1031,6 +1027,9 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ context->ag_indicators[context->parser_item_index].min_range = atoi((char *)context->line_buffer); log_info("%s, ", context->line_buffer); break; + case HFP_CMD_AG_SENT_PHONE_NUMBER: + context->bnip_type = (uint8_t)atoi((char*)context->line_buffer); + break; default: break; } @@ -1194,6 +1193,10 @@ static void parse_sequence(hfp_connection_t * context){ context->ok_pending = 1; context->extended_audio_gateway_error = 0; break; + case HFP_CMD_AG_SENT_PHONE_NUMBER: + strncpy(context->bnip_number, (char *)context->line_buffer, sizeof(context->bnip_number)); + context->bnip_number[sizeof(context->bnip_number)-1] = 0; + break; default: break; } diff --git a/src/hfp.h b/src/hfp.h index 85838bbd1..57babe29f 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -187,7 +187,7 @@ typedef enum { HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION, HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION, HFP_CMD_HF_REQUEST_PHONE_NUMBER, - HFP_CMD_AG_SEND_PHONE_NUMBER, + HFP_CMD_AG_SENT_PHONE_NUMBER, HFP_CMD_TRANSMIT_DTMF_CODES, HFP_CMD_SET_MICROPHONE_GAIN, HFP_CMD_SET_SPEAKER_GAIN, @@ -564,6 +564,7 @@ typedef struct hfp_connection { uint8_t hf_send_chld_3; uint8_t hf_send_chld_4; char hf_send_dtmf_code; + uint8_t hf_send_binp; uint8_t hf_activate_call_waiting_notification; uint8_t hf_deactivate_call_waiting_notification; @@ -573,7 +574,10 @@ typedef struct hfp_connection { uint8_t hf_deactivate_echo_canceling_and_noise_reduction; uint8_t hf_activate_voice_recognition_notification; uint8_t hf_deactivate_voice_recognition_notification; - + + uint8_t bnip_type; // 0 == not set + char bnip_number[25]; // + } hfp_connection_t; // UTILS_START : TODO move to utils diff --git a/src/hfp_hf.c b/src/hfp_hf.c index e7b6e6ddc..085f3c190 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -310,6 +310,12 @@ static int hfp_hf_send_dtmf(uint16_t cid, char code){ return send_str_over_rfcomm(cid, buffer); } +static int hfp_hf_send_binp(uint16_t cid){ + char buffer[20]; + sprintf(buffer, "AT%s=1\r\n", HFP_PHONE_NUMBER_FOR_VOICE_TAG); + return send_str_over_rfcomm(cid, buffer); +} + static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; uint8_t event[6+HFP_MAX_INDICATOR_DESC_SIZE+1]; @@ -678,6 +684,13 @@ static void hfp_run_for_context(hfp_connection_t * context){ return; } + if (context->hf_send_binp){ + context->hf_send_binp = 0; + context->ok_pending = 1; + hfp_hf_send_binp(context->rfcomm_cid); + return; + } + if (done) return; // deal with disconnect switch (context->state){ @@ -830,16 +843,21 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 } switch (context->command){ + case HFP_CMD_AG_SENT_PHONE_NUMBER: + context->command = HFP_CMD_NONE; + hfp_emit_string_event(hfp_callback, HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG, context->bnip_number); + break; case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR: context->ok_pending = 0; context->extended_audio_gateway_error = 0; + context->command = HFP_CMD_NONE; hfp_emit_event(hfp_callback, HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR, context->extended_audio_gateway_error); break; case HFP_CMD_ERROR: context->ok_pending = 0; hfp_reset_context_flags(context); - hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1); context->command = HFP_CMD_NONE; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1); break; case HFP_CMD_OK: hfp_hf_switch_on_ok(context); @@ -1278,9 +1296,20 @@ void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain){ /* * @brief */ -void hfp_hf_send_dtmf_code(bd_addr_t bd_addr, char code){ - hfp_hf_establish_service_level_connection(bd_addr); - hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); +void hfp_hf_send_dtmf_code(bd_addr_t addr, char code){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); connection->hf_send_dtmf_code = code; hfp_run_for_context(connection); -} \ No newline at end of file +} + +/* + * @brief + */ +void hfp_hf_request_phone_number_for_voice_tag(bd_addr_t addr){ + hfp_hf_establish_service_level_connection(addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(addr); + connection->hf_send_binp = 1; + hfp_run_for_context(connection); +} + diff --git a/src/hfp_hf.h b/src/hfp_hf.h index af538f99c..7a95b219b 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -260,6 +260,11 @@ void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain); */ void hfp_hf_send_dtmf_code(bd_addr_t bd_addr, char code); +/* + * @brief + */ +void hfp_hf_request_phone_number_for_voice_tag(bd_addr_t addr); + /* API_END */ #if defined __cplusplus diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 09977618f..9b60b56a6 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -152,6 +152,8 @@ static void show_usage(void){ printf("W - redial\n"); printf("0123456789#*-+ - send DTMF dial tones\n"); + printf("x - request phone number for voice tag\n"); + printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -333,6 +335,10 @@ static int stdin_process(struct data_source *ds){ printf("Redial\n"); hfp_hf_redial_last_number(device_addr); break; + case 'x': + printf("Request phone number for voice tag\n"); + hfp_hf_request_phone_number_for_voice_tag(device_addr); + break; default: show_usage(); break; @@ -349,7 +355,9 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ return; } if (event[0] != HCI_EVENT_HFP_META) return; - if (event[3] && event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR){ + if (event[3] + && event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR + && event[2] != HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG){ printf("ERROR, status: %u\n", event[3]); return; } @@ -390,6 +398,9 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ case HFP_SUBEVENT_RING: printf("** Ring **\n"); break; + case HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG: + printf("Phone number for voice tag: %s\n", (const char *) &event[3]); + break; default: printf("event not handled %u\n", event[2]); break; From b45e664d79988f93d45e7000a066384abc7bc3f2 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 28 Nov 2015 14:38:47 +0100 Subject: [PATCH 72/72] hfp: report speaker and microphone gain changes on HF --- include/btstack/hci_cmds.h | 2 ++ src/hfp_hf.c | 8 ++++++++ test/pts/hfp_hf_test.c | 10 +++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 39046457a..52df1dfb6 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -648,6 +648,8 @@ extern "C" { #define HFP_SUBEVENT_CALL_ANSWERED 0x13 #define HFP_SUBEVENT_CONFERENCE_CALL 0x14 #define HFP_SUBEVENT_RING 0x15 +#define HFP_SUBEVENT_SPEAKER_VOLUME 0x16 +#define HFP_SUBEVENT_MICROPHONE_VOLUME 0x17 // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 085f3c190..e484e1e93 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -843,6 +843,14 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 } switch (context->command){ + case HFP_CMD_SET_SPEAKER_GAIN: + context->command = HFP_CMD_NONE; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SPEAKER_VOLUME, atoi((char*)context->line_buffer)); + break; + case HFP_CMD_SET_MICROPHONE_GAIN: + context->command = HFP_CMD_NONE; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_MICROPHONE_VOLUME, atoi((char*)context->line_buffer)); + break; case HFP_CMD_AG_SENT_PHONE_NUMBER: context->command = HFP_CMD_NONE; hfp_emit_string_event(hfp_callback, HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG, context->bnip_number); diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 9b60b56a6..7893dc0c0 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -357,7 +357,9 @@ static 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 - && event[2] != HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG){ + && event[2] != HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG + && event[2] != HFP_SUBEVENT_SPEAKER_VOLUME + && event[2] != HFP_SUBEVENT_MICROPHONE_VOLUME){ printf("ERROR, status: %u\n", event[3]); return; } @@ -401,6 +403,12 @@ static void packet_handler(uint8_t * event, uint16_t event_size){ case HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG: printf("Phone number for voice tag: %s\n", (const char *) &event[3]); break; + case HFP_SUBEVENT_SPEAKER_VOLUME: + printf("Speaker volume: %u\n", event[3]); + break; + case HFP_SUBEVENT_MICROPHONE_VOLUME: + printf("Microphone volume: %u\n", event[3]); + break; default: printf("event not handled %u\n", event[2]); break;