diff --git a/src/hfp.c b/src/hfp.c index 33df8e85c..3921d3af9 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -671,6 +671,10 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ return HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE; } + if (strncmp(line_buffer+offset, HFP_ENABLE_CLIP, strlen(HFP_ENABLE_CLIP)) == 0){ + return HFP_CMD_ENABLE_CLIP; + } + if (strncmp(line_buffer+offset, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){ return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES; } diff --git a/src/hfp.h b/src/hfp.h index 7e0f28cc0..7231a2c0b 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -106,6 +106,7 @@ extern "C" { #define HFP_AVAILABLE_CODECS "+BAC" #define HFP_INDICATOR "+CIND" #define HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS "+CMER" +#define HFP_ENABLE_CLIP "+CLIP" #define HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS "+BIA" // +BIA:,,,,, #define HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES "+CHLD" #define HFP_GENERIC_STATUS_INDICATOR "+BIND" @@ -142,6 +143,7 @@ typedef enum { HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE, HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, + HFP_CMD_ENABLE_CLIP, HFP_CMD_LIST_GENERIC_STATUS_INDICATORS, HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS, @@ -401,6 +403,9 @@ typedef struct hfp_connection { // Retrieved during service level connection establishment, not used yet uint8_t negotiated_codec; + // HF -> AG configuration + uint8_t clip_enabled; + // TODO: put these bit flags in a bitmap uint8_t ok_pending; // uint8_t send_ok; @@ -424,6 +429,7 @@ typedef struct hfp_connection { uint8_t run_call_state_machine; uint8_t change_in_band_ring_tone_setting; uint8_t ag_ring; + uint8_t ag_send_clip; timer_source_t hfp_timeout; } hfp_connection_t; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index bc549b33a..d9851c20b 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -80,6 +80,9 @@ 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; +// CLIP feature +static uint8_t clip_type; // 0 == not set +static char clip_number[25]; // 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); @@ -204,6 +207,12 @@ static int hfp_ag_ring(uint16_t cid){ return send_str_over_rfcomm(cid, (char *) "\r\nRING\r\n"); } +static int hfp_ag_send_clip(uint16_t cid){ + char buffer[50]; + sprintf(buffer, "\r\n+CLIP: \"%s\",%u\r\n", clip_number, clip_type); + return send_str_over_rfcomm(cid, buffer); +} + static int hfp_ag_error(uint16_t cid){ char buffer[10]; sprintf(buffer, "\r\nERROR\r\n"); @@ -647,6 +656,7 @@ static void hfp_timeout_handler(timer_source_t * timer){ log_info("HFP start ring timeout, con handle 0x%02x", context->con_handle); context->ag_ring = 1; + context->ag_send_clip = clip_type && context->clip_enabled; run_loop_set_timer(&context->hfp_timeout, 2000); // 5 seconds timeout run_loop_add_timer(&context->hfp_timeout); @@ -694,6 +704,7 @@ static int incoming_call_state_machine(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); @@ -705,6 +716,7 @@ static void hfp_ag_hf_start_ringing(hfp_connection_t * context){ static void hfp_ag_hf_stop_ringing(hfp_connection_t * context){ context->ag_ring = 0; + context->ag_send_clip = 0; hfp_timeout_stop(context); hfp_emit_event(hfp_callback, HFP_SUBEVENT_STOP_RINGINIG, 0); } @@ -764,7 +776,8 @@ static void hfp_ag_hf_accept_call(hfp_connection_t * source){ 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->call_state != HFP_CALL_RINGING) continue; + if (connection->call_state != HFP_CALL_RINGING && + connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING) continue; hfp_ag_hf_stop_ringing(connection); connection->run_call_state_machine = 1; @@ -786,7 +799,6 @@ static void hfp_ag_hf_accept_call(hfp_connection_t * source){ connection->call_state = HFP_CALL_IDLE; } hfp_run_for_context(connection); - break; // only single } } @@ -819,7 +831,8 @@ static void hfp_ag_trigger_reject_call(void){ 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->call_state != HFP_CALL_RINGING) continue; + if (connection->call_state != HFP_CALL_RINGING && + connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING) continue; hfp_ag_hf_stop_ringing(connection); connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); connection->run_call_state_machine = 0; @@ -897,6 +910,8 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; case HFP_AG_INCOMING_CALL_ACCEPTED_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){ @@ -915,6 +930,8 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect } break; case HFP_AG_INCOMING_CALL_ACCEPTED_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){ @@ -934,7 +951,9 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; case HFP_AG_TERMINATE_CALL_BY_HF: - switch (hfp_ag_call_state){ + // 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: @@ -962,7 +981,9 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; case HFP_AG_TERMINATE_CALL_BY_AG: - switch (hfp_ag_call_state){ + // 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: @@ -984,7 +1005,9 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect } break; case HFP_AG_CALL_DROPPED: - switch (hfp_ag_call_state){ + // clear CLIP + clip_type = 0; + switch (hfp_ag_call_state){ case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 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); @@ -1040,6 +1063,7 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE); hfp_ag_transfer_callsetup_state(); break; + case HFP_AG_OUTGOING_CALL_ESTABLISHED: switch (hfp_ag_call_state){ case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: @@ -1110,6 +1134,13 @@ static void hfp_run_for_context(hfp_connection_t *context){ hfp_ag_ring(context->rfcomm_cid); return; } + + if (context->ag_send_clip){ + context->ag_send_clip = 0; + context->command = HFP_CMD_NONE; + hfp_ag_send_clip(context->rfcomm_cid); + return; + } int done = hfp_ag_run_for_context_service_level_connection(context); if (!done){ @@ -1125,7 +1156,7 @@ static void hfp_run_for_context(hfp_connection_t *context){ } if (context->command == HFP_CMD_NONE && !done){ - log_info("context->command == HFP_CMD_NONE"); + // log_info("context->command == HFP_CMD_NONE"); switch(context->state){ case HFP_W2_DISCONNECT_RFCOMM: context->state = HFP_W4_RFCOMM_DISCONNECTED; @@ -1150,6 +1181,7 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ switch(context->command){ case HFP_CMD_CALL_ANSWERED: context->command = HFP_CMD_NONE; + printf("HFP: ATA\n"); hfp_ag_call_sm(HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF, context); break; case HFP_CMD_HANG_UP_CALL: @@ -1164,6 +1196,12 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ case HFP_CMD_REDIAL_LAST_NUMBER: context->command = HFP_CMD_NONE; hfp_ag_call_sm(HFP_AG_OUTGOING_REDIAL_INITIATED, context); + case HFP_CMD_ENABLE_CLIP: + context->command = HFP_CMD_NONE; + context->clip_enabled = context->line_buffer[8] != '0'; + log_info("hfp: clip set, now: %u", context->clip_enabled); + context->ok_pending = 1; + break; default: break; } @@ -1315,6 +1353,16 @@ void hfp_ag_incoming_call(void){ hfp_ag_call_sm(HFP_AG_INCOMING_CALL, NULL); } +/** + * @brief number is stored. + */ +void hfp_ag_set_clip(uint8_t type, const char * number){ + clip_type = type; + // copy and terminate + strncpy(clip_number, number, sizeof(clip_number)); + clip_number[sizeof(clip_number)-1] = '\0'; +} + void hfp_ag_call_dropped(void){ hfp_ag_call_sm(HFP_AG_CALL_DROPPED, NULL); } diff --git a/src/hfp_ag.h b/src/hfp_ag.h index ead08d727..62f99daa6 100644 --- a/src/hfp_ag.h +++ b/src/hfp_ag.h @@ -175,6 +175,11 @@ void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone); */ void hfp_ag_incoming_call(void); +/** + * @brief number is stored. + */ +void hfp_ag_set_clip(uint8_t type, const char * number); + /** * @brief */ diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 00b74450d..650197353 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -187,6 +187,7 @@ static int stdin_process(struct data_source *ds){ break; case 'c': printf("Simulate incoming call\n"); + hfp_ag_set_clip(129, "1234567"); hfp_ag_incoming_call(); break; case 'C': @@ -237,6 +238,14 @@ static int stdin_process(struct data_source *ds){ printf("Set battery level to 5\n"); hfp_ag_set_battery_level(5); break; + case 'j': + printf("Answering call on remote side\n"); + hfp_ag_outgoing_call_established(); + break; + case 'r': + printf("Disable in-band ring tone\n"); + hfp_ag_set_use_in_band_ring_tone(0); + break; case 'k': printf("Memory 1 cleared\n"); memory_1_enabled = 0; @@ -253,14 +262,6 @@ static int stdin_process(struct data_source *ds){ printf("Last dialed number set\n"); last_number_exists = 1; break; - case 'j': - printf("Answering call on remote side\n"); - hfp_ag_outgoing_call_established(); - break; - case 'r': - printf("Disable in-band ring tone\n"); - hfp_ag_set_use_in_band_ring_tone(0); - break; case 'R': printf("Enable in-band ring tone\n"); hfp_ag_set_use_in_band_ring_tone(1);