13deb3ec6SMatthias Ringwald /* 23deb3ec6SMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 33deb3ec6SMatthias Ringwald * 43deb3ec6SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 53deb3ec6SMatthias Ringwald * modification, are permitted provided that the following conditions 63deb3ec6SMatthias Ringwald * are met: 73deb3ec6SMatthias Ringwald * 83deb3ec6SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 93deb3ec6SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 103deb3ec6SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 113deb3ec6SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 123deb3ec6SMatthias Ringwald * documentation and/or other materials provided with the distribution. 133deb3ec6SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 143deb3ec6SMatthias Ringwald * contributors may be used to endorse or promote products derived 153deb3ec6SMatthias Ringwald * from this software without specific prior written permission. 163deb3ec6SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 173deb3ec6SMatthias Ringwald * personal benefit and not for any commercial purpose or for 183deb3ec6SMatthias Ringwald * monetary gain. 193deb3ec6SMatthias Ringwald * 203deb3ec6SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 213deb3ec6SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 223deb3ec6SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 253deb3ec6SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 263deb3ec6SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 273deb3ec6SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 283deb3ec6SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 293deb3ec6SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 303deb3ec6SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 313deb3ec6SMatthias Ringwald * SUCH DAMAGE. 323deb3ec6SMatthias Ringwald * 333deb3ec6SMatthias Ringwald * Please inquire about commercial licensing options at 343deb3ec6SMatthias Ringwald * [email protected] 353deb3ec6SMatthias Ringwald * 363deb3ec6SMatthias Ringwald */ 37ab2c6ae4SMatthias Ringwald 38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "hfp_ag.c" 393deb3ec6SMatthias Ringwald 403deb3ec6SMatthias Ringwald // ***************************************************************************** 413deb3ec6SMatthias Ringwald // 4266a048abSMatthias Ringwald // HFP Audio Gateway (AG) unit 433deb3ec6SMatthias Ringwald // 443deb3ec6SMatthias Ringwald // ***************************************************************************** 453deb3ec6SMatthias Ringwald 467907f069SMatthias Ringwald #include "btstack_config.h" 473deb3ec6SMatthias Ringwald 483deb3ec6SMatthias Ringwald #include <stdint.h> 493cfa4086SMatthias Ringwald #include <stdio.h> 503deb3ec6SMatthias Ringwald #include <string.h> 513deb3ec6SMatthias Ringwald 5256042629SMatthias Ringwald #include "hci_cmd.h" 5382636622SMatthias Ringwald #include "btstack_run_loop.h" 543deb3ec6SMatthias Ringwald 55235946f1SMatthias Ringwald #include "bluetooth_sdp.h" 563deb3ec6SMatthias Ringwald #include "hci.h" 573deb3ec6SMatthias Ringwald #include "btstack_memory.h" 583deb3ec6SMatthias Ringwald #include "hci_dump.h" 593deb3ec6SMatthias Ringwald #include "l2cap.h" 6016ece135SMatthias Ringwald #include "btstack_debug.h" 61e30a6a47SMatthias Ringwald #include "btstack_event.h" 6259c6af15SMatthias Ringwald #include "classic/core.h" 633edc84c5SMatthias Ringwald #include "classic/hfp.h" 643edc84c5SMatthias Ringwald #include "classic/hfp_ag.h" 65023f2764SMatthias Ringwald #include "classic/hfp_gsm_model.h" 66efda0b48SMatthias Ringwald #include "classic/sdp_client_rfcomm.h" 67023f2764SMatthias Ringwald #include "classic/sdp_server.h" 68023f2764SMatthias Ringwald #include "classic/sdp_util.h" 693deb3ec6SMatthias Ringwald 70c5b64319SMatthias Ringwald // private prototypes 71f0aeb307SMatthias Ringwald static void hfp_ag_run_for_context(hfp_connection_t *hfp_connection); 722ff1d7d7SMilanka Ringwald static void hfp_ag_hf_start_ringing_incoming(hfp_connection_t * hfp_connection); 73fcd59412SMilanka Ringwald static void hfp_ag_hf_start_ringing_outgoing(hfp_connection_t * hfp_connection); 74c5fa3c94SMilanka Ringwald static uint8_t hfp_ag_setup_audio_connection(hfp_connection_t * hfp_connection); 75c5b64319SMatthias Ringwald 76c5b64319SMatthias Ringwald // public prototypes 7735833313SMatthias Ringwald hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(void); 7835833313SMatthias Ringwald int get_hfp_generic_status_indicators_nr(void); 79c5b64319SMatthias Ringwald void set_hfp_generic_status_indicators(hfp_generic_status_indicator_t * indicators, int indicator_nr); 80c5b64319SMatthias Ringwald void set_hfp_ag_indicators(hfp_ag_indicator_t * indicators, int indicator_nr); 81c5b64319SMatthias Ringwald int get_hfp_ag_indicators_nr(hfp_connection_t * context); 82c5b64319SMatthias Ringwald hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context); 83c5b64319SMatthias Ringwald 8499af1e28SMilanka Ringwald #define HFP_SUBEVENT_INVALID 0xFFFF 8527950165SMatthias Ringwald 8620b2edb6SMatthias Ringwald // const 87aeb0f0feSMatthias Ringwald static const char hfp_ag_default_service_name[] = "Voice gateway"; 88aa4dd815SMatthias Ringwald 8920b2edb6SMatthias Ringwald // globals 90aeb0f0feSMatthias Ringwald 91aeb0f0feSMatthias Ringwald // higher layer callbacks 92aeb0f0feSMatthias Ringwald static btstack_packet_handler_t hfp_ag_callback; 93aeb0f0feSMatthias Ringwald 9481e25d0aSMatthias Ringwald static bool (*hfp_ag_custom_call_sm_handler)(hfp_ag_call_event_t event); 9581e25d0aSMatthias Ringwald 9620b2edb6SMatthias Ringwald static btstack_packet_callback_registration_t hfp_ag_hci_event_callback_registration; 97aa4dd815SMatthias Ringwald 98aeb0f0feSMatthias Ringwald static uint16_t hfp_ag_supported_features; 9920b2edb6SMatthias Ringwald 10040a8ee13SMatthias Ringwald // in-band ring tone is active on SLC if supported 10140a8ee13SMatthias Ringwald static bool hfp_ag_in_band_ring_tone_active; 10240a8ee13SMatthias Ringwald 103aeb0f0feSMatthias Ringwald // codecs 104aeb0f0feSMatthias Ringwald static uint8_t hfp_ag_codecs_nr; 105aeb0f0feSMatthias Ringwald static uint8_t hfp_ag_codecs[HFP_MAX_NUM_CODECS]; 1063deb3ec6SMatthias Ringwald 107aeb0f0feSMatthias Ringwald // AG indicators 108e58f738aSMatthias Ringwald static uint16_t hfp_ag_indicators_nr; 10925789943SMilanka Ringwald static hfp_ag_indicator_t hfp_ag_indicators[HFP_MAX_NUM_INDICATORS]; 1103deb3ec6SMatthias Ringwald 111aeb0f0feSMatthias Ringwald // generic status indicators 112e58f738aSMatthias Ringwald static uint16_t hfp_ag_generic_status_indicators_nr; 113aeb0f0feSMatthias Ringwald static hfp_generic_status_indicator_t hfp_ag_generic_status_indicators[HFP_MAX_NUM_INDICATORS]; 114a0ffb263SMatthias Ringwald 115e58f738aSMatthias Ringwald static uint16_t hfp_ag_call_hold_services_nr; 1163deb3ec6SMatthias Ringwald static char * hfp_ag_call_hold_services[6]; 1173deb3ec6SMatthias Ringwald 118ce263fc8SMatthias Ringwald static hfp_response_and_hold_state_t hfp_ag_response_and_hold_state; 11927bb1817SMatthias Ringwald static bool hfp_ag_response_and_hold_active = false; 120aa4dd815SMatthias Ringwald 12120b2edb6SMatthias Ringwald // Subscriber information entries 122aeb0f0feSMatthias Ringwald static hfp_phone_number_t * hfp_ag_subscriber_numbers; 123aeb0f0feSMatthias Ringwald static int hfp_ag_subscriber_numbers_count; 124ce263fc8SMatthias Ringwald 125a4f85bd2SMilanka Ringwald // call state 126a4f85bd2SMilanka Ringwald static btstack_timer_source_t hfp_ag_ring_timeout; 127a4f85bd2SMilanka Ringwald 128*edc46354SMatthias Ringwald // Apple extension 129*edc46354SMatthias Ringwald static const char * hfp_ag_apple_device; 130*edc46354SMatthias Ringwald static uint8_t hfp_ag_apple_features; 131*edc46354SMatthias Ringwald 132*edc46354SMatthias Ringwald 13320b2edb6SMatthias Ringwald // code 134a0ffb263SMatthias Ringwald static int hfp_ag_get_ag_indicators_nr(hfp_connection_t * hfp_connection){ 135a0ffb263SMatthias Ringwald if (hfp_connection->ag_indicators_nr != hfp_ag_indicators_nr){ 136a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_nr = hfp_ag_indicators_nr; 1376535961aSMatthias Ringwald (void)memcpy(hfp_connection->ag_indicators, hfp_ag_indicators, 1386535961aSMatthias Ringwald hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); 1393deb3ec6SMatthias Ringwald } 140a0ffb263SMatthias Ringwald return hfp_connection->ag_indicators_nr; 141a0ffb263SMatthias Ringwald } 142a0ffb263SMatthias Ringwald 143a0ffb263SMatthias Ringwald hfp_ag_indicator_t * hfp_ag_get_ag_indicators(hfp_connection_t * hfp_connection){ 144a0ffb263SMatthias Ringwald // TODO: save only value, and value changed in the hfp_connection? 145a0ffb263SMatthias Ringwald if (hfp_connection->ag_indicators_nr != hfp_ag_indicators_nr){ 146a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_nr = hfp_ag_indicators_nr; 1476535961aSMatthias Ringwald (void)memcpy(hfp_connection->ag_indicators, hfp_ag_indicators, 1486535961aSMatthias Ringwald hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t)); 149a0ffb263SMatthias Ringwald } 150a0ffb263SMatthias Ringwald return (hfp_ag_indicator_t *)&(hfp_connection->ag_indicators); 1513deb3ec6SMatthias Ringwald } 1523deb3ec6SMatthias Ringwald 153aa4dd815SMatthias Ringwald static hfp_ag_indicator_t * get_ag_indicator_for_name(const char * name){ 154aa4dd815SMatthias Ringwald int i; 155aa4dd815SMatthias Ringwald for (i = 0; i < hfp_ag_indicators_nr; i++){ 156aa4dd815SMatthias Ringwald if (strcmp(hfp_ag_indicators[i].name, name) == 0){ 157aa4dd815SMatthias Ringwald return &hfp_ag_indicators[i]; 158aa4dd815SMatthias Ringwald } 159aa4dd815SMatthias Ringwald } 160aa4dd815SMatthias Ringwald return NULL; 161aa4dd815SMatthias Ringwald } 162aa4dd815SMatthias Ringwald 163aa4dd815SMatthias Ringwald static int get_ag_indicator_index_for_name(const char * name){ 164aa4dd815SMatthias Ringwald int i; 165aa4dd815SMatthias Ringwald for (i = 0; i < hfp_ag_indicators_nr; i++){ 166aa4dd815SMatthias Ringwald if (strcmp(hfp_ag_indicators[i].name, name) == 0){ 167aa4dd815SMatthias Ringwald return i; 168aa4dd815SMatthias Ringwald } 169aa4dd815SMatthias Ringwald } 170aa4dd815SMatthias Ringwald return -1; 171aa4dd815SMatthias Ringwald } 172aa4dd815SMatthias Ringwald 1739c9c64c1SMatthias Ringwald static hfp_connection_t * get_hfp_ag_connection_context_for_acl_handle(uint16_t handle){ 1749c9c64c1SMatthias Ringwald btstack_linked_list_iterator_t it; 1759c9c64c1SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1769c9c64c1SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1779c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 1789c9c64c1SMatthias Ringwald if (hfp_connection->acl_handle != handle) continue; 1799c9c64c1SMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1809c9c64c1SMatthias Ringwald return hfp_connection; 1819c9c64c1SMatthias Ringwald } 1829c9c64c1SMatthias Ringwald return NULL; 1839c9c64c1SMatthias Ringwald } 1843deb3ec6SMatthias Ringwald 18540a8ee13SMatthias Ringwald static bool has_in_band_ring_tone(void){ 18640a8ee13SMatthias Ringwald return get_bit(hfp_ag_supported_features, HFP_AGSF_IN_BAND_RING_TONE) != 0; 18740a8ee13SMatthias Ringwald } 18840a8ee13SMatthias Ringwald 189f197e761SMatthias Ringwald static int use_in_band_tone(hfp_connection_t *hfp_connection) { 19040a8ee13SMatthias Ringwald return hfp_connection->ag_in_band_ring_tone_active ? 1 : 0; 1913deb3ec6SMatthias Ringwald } 1923deb3ec6SMatthias Ringwald 193a0ffb263SMatthias Ringwald static int has_codec_negotiation_feature(hfp_connection_t * hfp_connection){ 194a0ffb263SMatthias Ringwald int hf = get_bit(hfp_connection->remote_supported_features, HFP_HFSF_CODEC_NEGOTIATION); 195aeb0f0feSMatthias Ringwald int ag = get_bit(hfp_ag_supported_features, HFP_AGSF_CODEC_NEGOTIATION); 1963deb3ec6SMatthias Ringwald return hf && ag; 1973deb3ec6SMatthias Ringwald } 1983deb3ec6SMatthias Ringwald 199a0ffb263SMatthias Ringwald static int has_call_waiting_and_3way_calling_feature(hfp_connection_t * hfp_connection){ 200a0ffb263SMatthias Ringwald int hf = get_bit(hfp_connection->remote_supported_features, HFP_HFSF_THREE_WAY_CALLING); 201aeb0f0feSMatthias Ringwald int ag = get_bit(hfp_ag_supported_features, HFP_AGSF_THREE_WAY_CALLING); 2023deb3ec6SMatthias Ringwald return hf && ag; 2033deb3ec6SMatthias Ringwald } 2043deb3ec6SMatthias Ringwald 205a0ffb263SMatthias Ringwald static int has_hf_indicators_feature(hfp_connection_t * hfp_connection){ 206a0ffb263SMatthias Ringwald int hf = get_bit(hfp_connection->remote_supported_features, HFP_HFSF_HF_INDICATORS); 207aeb0f0feSMatthias Ringwald int ag = get_bit(hfp_ag_supported_features, HFP_AGSF_HF_INDICATORS); 2083deb3ec6SMatthias Ringwald return hf && ag; 2093deb3ec6SMatthias Ringwald } 2103deb3ec6SMatthias Ringwald 21176cc1527SMatthias Ringwald /* unsolicited responses */ 212aa4dd815SMatthias Ringwald 213f197e761SMatthias Ringwald static int hfp_ag_send_change_in_band_ring_tone_setting_cmd(hfp_connection_t * hfp_connection){ 2148d860c8fSMatthias Ringwald char buffer[40]; 215ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: %d\r\n", 216f197e761SMatthias Ringwald HFP_CHANGE_IN_BAND_RING_TONE_SETTING, use_in_band_tone(hfp_connection)); 217ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 218f197e761SMatthias Ringwald return send_str_over_rfcomm(hfp_connection->rfcomm_cid, buffer); 2193deb3ec6SMatthias Ringwald } 2203deb3ec6SMatthias Ringwald 2213deb3ec6SMatthias Ringwald static int hfp_ag_exchange_supported_features_cmd(uint16_t cid){ 2223deb3ec6SMatthias Ringwald char buffer[40]; 223ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s:%d\r\n\r\nOK\r\n", 224aeb0f0feSMatthias Ringwald HFP_SUPPORTED_FEATURES, hfp_ag_supported_features); 225ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 2263deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 2273deb3ec6SMatthias Ringwald } 2283deb3ec6SMatthias Ringwald 229485ac19eSMilanka Ringwald static int hfp_ag_send_ok(uint16_t cid){ 2303deb3ec6SMatthias Ringwald char buffer[10]; 231ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\nOK\r\n"); 232ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 2333deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 2343deb3ec6SMatthias Ringwald } 2353deb3ec6SMatthias Ringwald 236485ac19eSMilanka Ringwald static int hfp_ag_send_ring(uint16_t cid){ 237aa4dd815SMatthias Ringwald return send_str_over_rfcomm(cid, (char *) "\r\nRING\r\n"); 238aa4dd815SMatthias Ringwald } 239aa4dd815SMatthias Ringwald 240245852b7SMilanka Ringwald static int hfp_ag_send_no_carrier(uint16_t cid){ 241245852b7SMilanka Ringwald char buffer[15]; 242245852b7SMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\nNO CARRIER\r\n"); 243245852b7SMilanka Ringwald buffer[sizeof(buffer) - 1] = 0; 244245852b7SMilanka Ringwald return send_str_over_rfcomm(cid, buffer); 245245852b7SMilanka Ringwald } 246245852b7SMilanka Ringwald 247aa4dd815SMatthias Ringwald static int hfp_ag_send_clip(uint16_t cid){ 248aa4dd815SMatthias Ringwald char buffer[50]; 249ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: \"%s\",%u\r\n", HFP_ENABLE_CLIP, 250ff7d6aeaSMatthias Ringwald hfp_gsm_clip_number(), hfp_gsm_clip_type()); 251ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 252aa4dd815SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 253aa4dd815SMatthias Ringwald } 254aa4dd815SMatthias Ringwald 255ce263fc8SMatthias Ringwald static int hfp_send_subscriber_number_cmd(uint16_t cid, uint8_t type, const char * number){ 256ce263fc8SMatthias Ringwald char buffer[50]; 257ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: ,\"%s\",%u, , \r\n", 258ff7d6aeaSMatthias Ringwald HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type); 259ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 260ce263fc8SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 261ce263fc8SMatthias Ringwald } 262ce263fc8SMatthias Ringwald 263c1797c7dSMatthias Ringwald static int hfp_ag_send_phone_number_for_voice_tag_cmd(uint16_t cid){ 264aa4dd815SMatthias Ringwald char buffer[50]; 265ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: %s\r\n", 266ff7d6aeaSMatthias Ringwald HFP_PHONE_NUMBER_FOR_VOICE_TAG, hfp_gsm_clip_number()); 267ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 268aa4dd815SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 269aa4dd815SMatthias Ringwald } 270aa4dd815SMatthias Ringwald 271aa4dd815SMatthias Ringwald static int hfp_ag_send_call_waiting_notification(uint16_t cid){ 272aa4dd815SMatthias Ringwald char buffer[50]; 273ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: \"%s\",%u\r\n", 274ff7d6aeaSMatthias Ringwald HFP_ENABLE_CALL_WAITING_NOTIFICATION, hfp_gsm_clip_number(), 275ff7d6aeaSMatthias Ringwald hfp_gsm_clip_type()); 276ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 277aa4dd815SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 278aa4dd815SMatthias Ringwald } 279aa4dd815SMatthias Ringwald 280485ac19eSMilanka Ringwald static int hfp_ag_send_error(uint16_t cid){ 2813deb3ec6SMatthias Ringwald char buffer[10]; 282ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\nERROR\r\n"); 283ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 2843deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 2853deb3ec6SMatthias Ringwald } 2863deb3ec6SMatthias Ringwald 287485ac19eSMilanka Ringwald static int hfp_ag_send_report_extended_audio_gateway_error(uint16_t cid, uint8_t error){ 2883deb3ec6SMatthias Ringwald char buffer[20]; 289ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s=%d\r\n", 290ff7d6aeaSMatthias Ringwald HFP_EXTENDED_AUDIO_GATEWAY_ERROR, error); 291ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 2923deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 2933deb3ec6SMatthias Ringwald } 2943deb3ec6SMatthias Ringwald 295*edc46354SMatthias Ringwald static int hfp_ag_send_apple_information(uint16_t cid){ 296*edc46354SMatthias Ringwald char buffer[50]; 297*edc46354SMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s:%s,%d\r\n", 298*edc46354SMatthias Ringwald HFP_APPLE_ACCESSORY_INFORMATION, hfp_ag_apple_device, hfp_ag_apple_features); 299*edc46354SMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 300*edc46354SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 301*edc46354SMatthias Ringwald } 302*edc46354SMatthias Ringwald 303ad902e3dSMatthias Ringwald // get size for indicator string 304a0ffb263SMatthias Ringwald static int hfp_ag_indicators_string_size(hfp_connection_t * hfp_connection, int i){ 305ad902e3dSMatthias Ringwald // template: ("$NAME",($MIN,$MAX)) 30645718b6fSMatthias Ringwald return 8 + (int) strlen(hfp_ag_get_ag_indicators(hfp_connection)[i].name) 307a0ffb263SMatthias Ringwald + string_len_for_uint32(hfp_ag_get_ag_indicators(hfp_connection)[i].min_range) 308a0ffb263SMatthias Ringwald + string_len_for_uint32(hfp_ag_get_ag_indicators(hfp_connection)[i].min_range); 309ad902e3dSMatthias Ringwald } 310ad902e3dSMatthias Ringwald 311ad902e3dSMatthias Ringwald // store indicator 3121167ff83SMatthias Ringwald static void hfp_ag_indicators_string_store(hfp_connection_t * hfp_connection, int i, uint8_t * buffer, uint16_t buffer_size){ 3131167ff83SMatthias Ringwald snprintf((char *)buffer, buffer_size, "(\"%s\",(%d,%d)),", 314a0ffb263SMatthias Ringwald hfp_ag_get_ag_indicators(hfp_connection)[i].name, 315a0ffb263SMatthias Ringwald hfp_ag_get_ag_indicators(hfp_connection)[i].min_range, 316a0ffb263SMatthias Ringwald hfp_ag_get_ag_indicators(hfp_connection)[i].max_range); 3171167ff83SMatthias Ringwald ((char *)buffer)[buffer_size - 1] = 0; 3183deb3ec6SMatthias Ringwald } 319ad902e3dSMatthias Ringwald 320ad902e3dSMatthias Ringwald // structure: header [indicator [comma indicator]] footer 321a0ffb263SMatthias Ringwald static int hfp_ag_indicators_cmd_generator_num_segments(hfp_connection_t * hfp_connection){ 322a0ffb263SMatthias Ringwald int num_indicators = hfp_ag_get_ag_indicators_nr(hfp_connection); 323ad902e3dSMatthias Ringwald if (!num_indicators) return 2; 324c1ab6cc1SMatthias Ringwald return 3 + ((num_indicators-1) * 2); 3253deb3ec6SMatthias Ringwald } 326ad902e3dSMatthias Ringwald 327ad902e3dSMatthias Ringwald // get size of individual segment for hfp_ag_retrieve_indicators_cmd 328a0ffb263SMatthias Ringwald static int hfp_ag_indicators_cmd_generator_get_segment_len(hfp_connection_t * hfp_connection, int index){ 329ad902e3dSMatthias Ringwald if (index == 0) { 330ab2445a0SMatthias Ringwald return (uint16_t) strlen(HFP_INDICATOR) + 3; // "\n\r%s:"" 331ad902e3dSMatthias Ringwald } 332ad902e3dSMatthias Ringwald index--; 333a0ffb263SMatthias Ringwald int num_indicators = hfp_ag_get_ag_indicators_nr(hfp_connection); 334ad902e3dSMatthias Ringwald int indicator_index = index >> 1; 335ad902e3dSMatthias Ringwald if ((index & 1) == 0){ 336a0ffb263SMatthias Ringwald return hfp_ag_indicators_string_size(hfp_connection, indicator_index); 337ad902e3dSMatthias Ringwald } 338c1ab6cc1SMatthias Ringwald if (indicator_index == (num_indicators - 1)){ 339ad902e3dSMatthias Ringwald return 8; // "\r\n\r\nOK\r\n" 340ad902e3dSMatthias Ringwald } 341ad902e3dSMatthias Ringwald return 1; // comma 342ad902e3dSMatthias Ringwald } 343ad902e3dSMatthias Ringwald 3441167ff83SMatthias Ringwald static void hfp_ag_indicators_cmd_generator_store_segment(hfp_connection_t * hfp_connection, int index, uint8_t * buffer, uint16_t buffer_size){ 345ad902e3dSMatthias Ringwald if (index == 0){ 346ad902e3dSMatthias Ringwald *buffer++ = '\r'; 34774386ee0SMatthias Ringwald *buffer++ = '\n'; 348ab2445a0SMatthias Ringwald int len = (uint16_t) strlen(HFP_INDICATOR); 3496535961aSMatthias Ringwald (void)memcpy(buffer, HFP_INDICATOR, len); 350ad902e3dSMatthias Ringwald buffer += len; 351ad902e3dSMatthias Ringwald *buffer++ = ':'; 352ad902e3dSMatthias Ringwald return; 353ad902e3dSMatthias Ringwald } 354ad902e3dSMatthias Ringwald index--; 355a0ffb263SMatthias Ringwald int num_indicators = hfp_ag_get_ag_indicators_nr(hfp_connection); 356ad902e3dSMatthias Ringwald int indicator_index = index >> 1; 357ad902e3dSMatthias Ringwald if ((index & 1) == 0){ 3581167ff83SMatthias Ringwald hfp_ag_indicators_string_store(hfp_connection, indicator_index, buffer, buffer_size); 359ad902e3dSMatthias Ringwald return; 360ad902e3dSMatthias Ringwald } 361c1ab6cc1SMatthias Ringwald if (indicator_index == (num_indicators-1)){ 3626535961aSMatthias Ringwald (void)memcpy(buffer, "\r\n\r\nOK\r\n", 8); 363ad902e3dSMatthias Ringwald return; 364ad902e3dSMatthias Ringwald } 365ad902e3dSMatthias Ringwald *buffer = ','; 3663deb3ec6SMatthias Ringwald } 3673deb3ec6SMatthias Ringwald 368aeb0f0feSMatthias Ringwald static int hfp_ag_generic_indicators_join(char * buffer, int buffer_size){ 369c1ab6cc1SMatthias Ringwald if (buffer_size < (hfp_ag_indicators_nr * 3)) return 0; 3703deb3ec6SMatthias Ringwald int i; 3713deb3ec6SMatthias Ringwald int offset = 0; 372aeb0f0feSMatthias Ringwald for (i = 0; i < (hfp_ag_generic_status_indicators_nr - 1); i++) { 373aeb0f0feSMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "%d,", hfp_ag_generic_status_indicators[i].uuid); 3743deb3ec6SMatthias Ringwald } 375aeb0f0feSMatthias Ringwald if (i < hfp_ag_generic_status_indicators_nr){ 376aeb0f0feSMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "%d", hfp_ag_generic_status_indicators[i].uuid); 3773deb3ec6SMatthias Ringwald } 3783deb3ec6SMatthias Ringwald return offset; 3793deb3ec6SMatthias Ringwald } 3803deb3ec6SMatthias Ringwald 381aeb0f0feSMatthias Ringwald static int hfp_ag_generic_indicators_initial_status_join(char * buffer, int buffer_size){ 382aeb0f0feSMatthias Ringwald if (buffer_size < (hfp_ag_generic_status_indicators_nr * 3)) return 0; 3833deb3ec6SMatthias Ringwald int i; 3843deb3ec6SMatthias Ringwald int offset = 0; 385aeb0f0feSMatthias Ringwald for (i = 0; i < hfp_ag_generic_status_indicators_nr; i++) { 386aeb0f0feSMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "\r\n%s:%d,%d\r\n", HFP_GENERIC_STATUS_INDICATOR, hfp_ag_generic_status_indicators[i].uuid, hfp_ag_generic_status_indicators[i].state); 3873deb3ec6SMatthias Ringwald } 3883deb3ec6SMatthias Ringwald return offset; 3893deb3ec6SMatthias Ringwald } 3903deb3ec6SMatthias Ringwald 3913deb3ec6SMatthias Ringwald static int hfp_ag_indicators_status_join(char * buffer, int buffer_size){ 392c1ab6cc1SMatthias Ringwald if (buffer_size < (hfp_ag_indicators_nr * 3)) return 0; 3933deb3ec6SMatthias Ringwald int i; 3943deb3ec6SMatthias Ringwald int offset = 0; 395c1ab6cc1SMatthias Ringwald for (i = 0; i < (hfp_ag_indicators_nr-1); i++) { 3963deb3ec6SMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "%d,", hfp_ag_indicators[i].status); 3973deb3ec6SMatthias Ringwald } 3983deb3ec6SMatthias Ringwald if (i<hfp_ag_indicators_nr){ 3993deb3ec6SMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "%d", hfp_ag_indicators[i].status); 4003deb3ec6SMatthias Ringwald } 4013deb3ec6SMatthias Ringwald return offset; 4023deb3ec6SMatthias Ringwald } 4033deb3ec6SMatthias Ringwald 4043deb3ec6SMatthias Ringwald static int hfp_ag_call_services_join(char * buffer, int buffer_size){ 405c1ab6cc1SMatthias Ringwald if (buffer_size < (hfp_ag_call_hold_services_nr * 3)) return 0; 4063deb3ec6SMatthias Ringwald int i; 4073deb3ec6SMatthias Ringwald int offset = snprintf(buffer, buffer_size, "("); 408c1ab6cc1SMatthias Ringwald for (i = 0; i < (hfp_ag_call_hold_services_nr-1); i++) { 4093deb3ec6SMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "%s,", hfp_ag_call_hold_services[i]); 4103deb3ec6SMatthias Ringwald } 4113deb3ec6SMatthias Ringwald if (i<hfp_ag_call_hold_services_nr){ 4123deb3ec6SMatthias Ringwald offset += snprintf(buffer+offset, buffer_size-offset, "%s)", hfp_ag_call_hold_services[i]); 4133deb3ec6SMatthias Ringwald } 4143deb3ec6SMatthias Ringwald return offset; 4153deb3ec6SMatthias Ringwald } 4163deb3ec6SMatthias Ringwald 417485ac19eSMilanka Ringwald static int hfp_ag_send_cmd_via_generator(uint16_t cid, hfp_connection_t * hfp_connection, 418ad902e3dSMatthias Ringwald int start_segment, int num_segments, 419a0ffb263SMatthias Ringwald int (*get_segment_len)(hfp_connection_t * hfp_connection, int segment), 4201167ff83SMatthias Ringwald void (*store_segment) (hfp_connection_t * hfp_connection, int segment, uint8_t * buffer, uint16_t buffer_size)){ 4213deb3ec6SMatthias Ringwald 422ad902e3dSMatthias Ringwald // assumes: can send now == true 423ad902e3dSMatthias Ringwald // assumes: num segments > 0 424aba39421SMatthias Ringwald // assumes: individual segments are smaller than MTU 425ad902e3dSMatthias Ringwald rfcomm_reserve_packet_buffer(); 426ad902e3dSMatthias Ringwald int mtu = rfcomm_get_max_frame_size(cid); 427ad902e3dSMatthias Ringwald uint8_t * data = rfcomm_get_outgoing_buffer(); 428ad902e3dSMatthias Ringwald int offset = 0; 429ad902e3dSMatthias Ringwald int segment = start_segment; 430ad902e3dSMatthias Ringwald while (segment < num_segments){ 431a0ffb263SMatthias Ringwald int segment_len = get_segment_len(hfp_connection, segment); 4321167ff83SMatthias Ringwald if ((offset + segment_len + 1) > mtu) break; 4331167ff83SMatthias Ringwald // append segment. As it appends a '\0', we provide a buffer one byte larger 4341167ff83SMatthias Ringwald store_segment(hfp_connection, segment, data+offset, segment_len + 1); 435ad902e3dSMatthias Ringwald offset += segment_len; 436ad902e3dSMatthias Ringwald segment++; 437ad902e3dSMatthias Ringwald } 438ad902e3dSMatthias Ringwald rfcomm_send_prepared(cid, offset); 439f20dd2aeSBjoern Hartmann #ifdef ENABLE_HFP_AT_MESSAGES 440f20dd2aeSBjoern Hartmann hfp_emit_string_event(hfp_connection, HFP_SUBEVENT_AT_MESSAGE_SENT, (char *) data); 441f20dd2aeSBjoern Hartmann #endif 442ad902e3dSMatthias Ringwald return segment; 443ad902e3dSMatthias Ringwald } 4443deb3ec6SMatthias Ringwald 445ad902e3dSMatthias Ringwald // returns next segment to store 446485ac19eSMilanka Ringwald static int hfp_ag_send_retrieve_indicators_cmd_via_generator(uint16_t cid, hfp_connection_t * hfp_connection, int start_segment){ 447a0ffb263SMatthias Ringwald int num_segments = hfp_ag_indicators_cmd_generator_num_segments(hfp_connection); 448485ac19eSMilanka Ringwald return hfp_ag_send_cmd_via_generator(cid, hfp_connection, start_segment, num_segments, 449894d499cSMatthias Ringwald hfp_ag_indicators_cmd_generator_get_segment_len, 450894d499cSMatthias Ringwald hfp_ag_indicators_cmd_generator_store_segment); 4513deb3ec6SMatthias Ringwald } 4523deb3ec6SMatthias Ringwald 453485ac19eSMilanka Ringwald static int hfp_ag_send_retrieve_indicators_status_cmd(uint16_t cid){ 4543deb3ec6SMatthias Ringwald char buffer[40]; 45589425bfcSMilanka Ringwald const int size = sizeof(buffer); 45689425bfcSMilanka Ringwald int offset = snprintf(buffer, size, "\r\n%s:", HFP_INDICATOR); 45789425bfcSMilanka Ringwald offset += hfp_ag_indicators_status_join(buffer+offset, size-offset-9); 45889425bfcSMilanka Ringwald offset += snprintf(buffer+offset, size-offset, "\r\n\r\nOK\r\n"); 4593deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 4603deb3ec6SMatthias Ringwald } 4613deb3ec6SMatthias Ringwald 462485ac19eSMilanka Ringwald static int hfp_ag_send_retrieve_can_hold_call_cmd(uint16_t cid){ 463ad902e3dSMatthias Ringwald char buffer[40]; 46489425bfcSMilanka Ringwald const int size = sizeof(buffer); 46589425bfcSMilanka Ringwald int offset = snprintf(buffer, size, "\r\n%s:", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES); 46689425bfcSMilanka Ringwald offset += hfp_ag_call_services_join(buffer+offset, size-offset-9); 46789425bfcSMilanka Ringwald offset += snprintf(buffer+offset, size-offset, "\r\n\r\nOK\r\n"); 4683deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 4693deb3ec6SMatthias Ringwald } 4703deb3ec6SMatthias Ringwald 4713deb3ec6SMatthias Ringwald 472485ac19eSMilanka Ringwald static int hfp_ag_send_list_supported_generic_status_indicators_cmd(uint16_t cid){ 473485ac19eSMilanka Ringwald return hfp_ag_send_ok(cid); 4743deb3ec6SMatthias Ringwald } 4753deb3ec6SMatthias Ringwald 476485ac19eSMilanka Ringwald static int hfp_ag_send_retrieve_supported_generic_status_indicators_cmd(uint16_t cid){ 4773deb3ec6SMatthias Ringwald char buffer[40]; 47889425bfcSMilanka Ringwald const int size = sizeof(buffer); 47989425bfcSMilanka Ringwald int offset = snprintf(buffer, size, "\r\n%s:(", HFP_GENERIC_STATUS_INDICATOR); 480aeb0f0feSMatthias Ringwald offset += hfp_ag_generic_indicators_join(buffer + offset, size - offset - 10); 48189425bfcSMilanka Ringwald offset += snprintf(buffer+offset, size-offset, ")\r\n\r\nOK\r\n"); 4823deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 4833deb3ec6SMatthias Ringwald } 4843deb3ec6SMatthias Ringwald 485485ac19eSMilanka Ringwald static int hfp_ag_send_retrieve_initital_supported_generic_status_indicators_cmd(uint16_t cid){ 4863deb3ec6SMatthias Ringwald char buffer[40]; 487aeb0f0feSMatthias Ringwald int offset = hfp_ag_generic_indicators_initial_status_join(buffer, sizeof(buffer) - 7); 4881cc1d9e9SMilanka Ringwald snprintf(buffer+offset, sizeof(buffer)-offset, "\r\nOK\r\n"); 4893deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 4903deb3ec6SMatthias Ringwald } 4913deb3ec6SMatthias Ringwald 492485ac19eSMilanka Ringwald static int hfp_ag_send_transfer_ag_indicators_status_cmd(uint16_t cid, hfp_ag_indicator_t * indicator){ 4933deb3ec6SMatthias Ringwald char buffer[20]; 494ff7d6aeaSMatthias Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s:%d,%d\r\n", 495ff7d6aeaSMatthias Ringwald HFP_TRANSFER_AG_INDICATOR_STATUS, indicator->index, 496ff7d6aeaSMatthias Ringwald indicator->status); 497ff7d6aeaSMatthias Ringwald buffer[sizeof(buffer) - 1] = 0; 4983deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 4993deb3ec6SMatthias Ringwald } 5003deb3ec6SMatthias Ringwald 501485ac19eSMilanka Ringwald static int hfp_ag_send_report_network_operator_name_cmd(uint16_t cid, hfp_network_opearator_t op){ 5025e71456cSMilanka Ringwald char buffer[41]; 5033deb3ec6SMatthias Ringwald if (strlen(op.name) == 0){ 50489425bfcSMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s:%d,,\r\n\r\nOK\r\n", HFP_QUERY_OPERATOR_SELECTION, op.mode); 5053deb3ec6SMatthias Ringwald } else { 50689425bfcSMilanka Ringwald int offset = snprintf(buffer, sizeof(buffer), "\r\n%s:%d,%d,", HFP_QUERY_OPERATOR_SELECTION, op.mode, op.format); 5075e71456cSMilanka Ringwald offset += snprintf(buffer+offset, 16, "%s", op.name); 50889425bfcSMilanka Ringwald snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n\r\nOK\r\n"); 5093deb3ec6SMatthias Ringwald } 5103deb3ec6SMatthias Ringwald return send_str_over_rfcomm(cid, buffer); 5113deb3ec6SMatthias Ringwald } 5123deb3ec6SMatthias Ringwald 513a04aecb1SMilanka Ringwald static inline int hfp_ag_send_cmd_with_space_and_int(uint16_t cid, const char * cmd, uint8_t value){ 514a04aecb1SMilanka Ringwald char buffer[30]; 515a04aecb1SMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: %d\r\n", cmd, value); 516a04aecb1SMilanka Ringwald return send_str_over_rfcomm(cid, buffer); 517a04aecb1SMilanka Ringwald } 518a04aecb1SMilanka Ringwald 519a04aecb1SMilanka Ringwald 5201cc1d9e9SMilanka Ringwald static inline int hfp_ag_send_cmd_with_int(uint16_t cid, const char * cmd, uint8_t value){ 5211cc1d9e9SMilanka Ringwald char buffer[30]; 52289425bfcSMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s:%d\r\n", cmd, value); 5231cc1d9e9SMilanka Ringwald return send_str_over_rfcomm(cid, buffer); 5241cc1d9e9SMilanka Ringwald } 5253deb3ec6SMatthias Ringwald 526485ac19eSMilanka Ringwald static int hfp_ag_send_suggest_codec_cmd(uint16_t cid, uint8_t codec){ 5271cc1d9e9SMilanka Ringwald return hfp_ag_send_cmd_with_int(cid, HFP_CONFIRM_COMMON_CODEC, codec); 5283deb3ec6SMatthias Ringwald } 5293deb3ec6SMatthias Ringwald 530485ac19eSMilanka Ringwald static int hfp_ag_send_set_speaker_gain_cmd(uint16_t cid, uint8_t gain){ 5311cc1d9e9SMilanka Ringwald return hfp_ag_send_cmd_with_int(cid, HFP_SET_SPEAKER_GAIN, gain); 532aa4dd815SMatthias Ringwald } 533aa4dd815SMatthias Ringwald 534485ac19eSMilanka Ringwald static int hfp_ag_send_set_microphone_gain_cmd(uint16_t cid, uint8_t gain){ 5351cc1d9e9SMilanka Ringwald return hfp_ag_send_cmd_with_int(cid, HFP_SET_MICROPHONE_GAIN, gain); 536aa4dd815SMatthias Ringwald } 537aa4dd815SMatthias Ringwald 538485ac19eSMilanka Ringwald static int hfp_ag_send_set_response_and_hold(uint16_t cid, int state){ 53966191aa0SMilanka Ringwald return hfp_ag_send_cmd_with_space_and_int(cid, HFP_RESPONSE_AND_HOLD, state); 540ce263fc8SMatthias Ringwald } 541ce263fc8SMatthias Ringwald 542cf75be85SMilanka Ringwald static int hfp_ag_send_enhanced_voice_recognition_state_cmd(hfp_connection_t * hfp_connection){ 543cf75be85SMilanka Ringwald char buffer[30]; 544fcf4ede6SMilanka Ringwald uint8_t evra_enabled = hfp_connection->enhanced_voice_recognition_enabled ? 1 : 0; 545fcf4ede6SMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, evra_enabled, hfp_connection->ag_vra_state); 546cf75be85SMilanka Ringwald return send_str_over_rfcomm(hfp_connection->rfcomm_cid, buffer); 547cf75be85SMilanka Ringwald } 548cf75be85SMilanka Ringwald 549d1b50446SMilanka Ringwald static int hfp_ag_send_voice_recognition_cmd(hfp_connection_t * hfp_connection, uint8_t activate_voice_recognition){ 55045796ff1SMilanka Ringwald char buffer[30]; 551d1b50446SMilanka Ringwald if (hfp_connection->enhanced_voice_recognition_enabled){ 552d1b50446SMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, activate_voice_recognition, hfp_connection->ag_vra_state); 553d1b50446SMilanka Ringwald } else { 554d1b50446SMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: %d\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, activate_voice_recognition); 555d1b50446SMilanka Ringwald } 556d1b50446SMilanka Ringwald return send_str_over_rfcomm(hfp_connection->rfcomm_cid, buffer); 55745796ff1SMilanka Ringwald } 55845796ff1SMilanka Ringwald 55945796ff1SMilanka Ringwald static int hfp_ag_send_enhanced_voice_recognition_msg_cmd(hfp_connection_t * hfp_connection){ 560a2e701c0SMilanka Ringwald char buffer[HFP_VR_TEXT_HEADER_SIZE + HFP_MAX_VR_TEXT_SIZE]; 56145796ff1SMilanka Ringwald snprintf(buffer, sizeof(buffer), "\r\n%s: 1,%d,%X,%d,%d,\"%s\"\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, 56245796ff1SMilanka Ringwald hfp_connection->ag_vra_state, 56345796ff1SMilanka Ringwald hfp_connection->ag_msg.text_id, 56445796ff1SMilanka Ringwald hfp_connection->ag_msg.text_type, 56545796ff1SMilanka Ringwald hfp_connection->ag_msg.text_operation, 56645796ff1SMilanka Ringwald hfp_connection->ag_msg.text); 56745796ff1SMilanka Ringwald return send_str_over_rfcomm(hfp_connection->rfcomm_cid, buffer); 56845796ff1SMilanka Ringwald } 56945796ff1SMilanka Ringwald 570a0ffb263SMatthias Ringwald static uint8_t hfp_ag_suggest_codec(hfp_connection_t *hfp_connection){ 571bc1b1537SMilanka Ringwald if (hfp_connection->sco_for_msbc_failed) return HFP_CODEC_CVSD; 572bc1b1537SMilanka Ringwald 5732085619dSMatthias Ringwald #ifdef ENABLE_HFP_SUPER_WIDE_BAND_SPEECH 5742085619dSMatthias Ringwald if (hfp_supports_codec(HFP_CODEC_LC3_SWB, hfp_ag_codecs_nr, hfp_ag_codecs)){ 5752085619dSMatthias Ringwald if (hfp_supports_codec(HFP_CODEC_LC3_SWB, hfp_connection->remote_codecs_nr, hfp_connection->remote_codecs)){ 5762085619dSMatthias Ringwald return HFP_CODEC_LC3_SWB; 5772085619dSMatthias Ringwald } 5782085619dSMatthias Ringwald } 5792085619dSMatthias Ringwald #endif 5802085619dSMatthias Ringwald 5812085619dSMatthias Ringwald #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 582aeb0f0feSMatthias Ringwald if (hfp_supports_codec(HFP_CODEC_MSBC, hfp_ag_codecs_nr, hfp_ag_codecs)){ 5836a7f44bdSMilanka Ringwald if (hfp_supports_codec(HFP_CODEC_MSBC, hfp_connection->remote_codecs_nr, hfp_connection->remote_codecs)){ 584df327ff3SMilanka Ringwald return HFP_CODEC_MSBC; 5853deb3ec6SMatthias Ringwald } 5863deb3ec6SMatthias Ringwald } 5872085619dSMatthias Ringwald #endif 5882085619dSMatthias Ringwald 589df327ff3SMilanka Ringwald return HFP_CODEC_CVSD; 5903deb3ec6SMatthias Ringwald } 5913deb3ec6SMatthias Ringwald 59276cc1527SMatthias Ringwald /* state machines */ 59376cc1527SMatthias Ringwald 594afac2a04SMilanka Ringwald static uint8_t hfp_ag_esco_s4_supported(hfp_connection_t * hfp_connection){ 595aeb0f0feSMatthias Ringwald return (hfp_connection->remote_supported_features & (1<<HFP_HFSF_ESCO_S4)) && (hfp_ag_supported_features & (1 << HFP_AGSF_ESCO_S4)); 5967522e673SMatthias Ringwald } 5977522e673SMatthias Ringwald 598a0ffb263SMatthias Ringwald static int codecs_exchange_state_machine(hfp_connection_t * hfp_connection){ 599aa4dd815SMatthias Ringwald /* events ( == commands): 600aa4dd815SMatthias Ringwald HFP_CMD_AVAILABLE_CODECS == received AT+BAC with list of codecs 601aa4dd815SMatthias Ringwald HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP: 602aa4dd815SMatthias Ringwald hf_trigger_codec_connection_setup == received BCC 603aa4dd815SMatthias Ringwald ag_trigger_codec_connection_setup == received from AG to send BCS 604aa4dd815SMatthias Ringwald HFP_CMD_HF_CONFIRMED_CODEC == received AT+BCS 605aa4dd815SMatthias Ringwald */ 606a0ffb263SMatthias Ringwald switch (hfp_connection->codecs_state){ 6073e3b9207SMilanka Ringwald case HFP_CODECS_EXCHANGED: 6083e3b9207SMilanka Ringwald if (hfp_connection->command == HFP_CMD_AVAILABLE_CODECS){ 6093e3b9207SMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 6103e3b9207SMilanka Ringwald return 1; 6113e3b9207SMilanka Ringwald } 6123e3b9207SMilanka Ringwald break; 6133e3b9207SMilanka Ringwald 614aa4dd815SMatthias Ringwald case HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE: 615aa4dd815SMatthias Ringwald case HFP_CODECS_AG_RESEND_COMMON_CODEC: 616a814da6aSMatthias Ringwald hfp_connection->ag_send_common_codec = true; 617aa4dd815SMatthias Ringwald break; 618aa4dd815SMatthias Ringwald default: 619aa4dd815SMatthias Ringwald break; 620aa4dd815SMatthias Ringwald } 621aa4dd815SMatthias Ringwald 622a814da6aSMatthias Ringwald if (hfp_connection->ag_send_common_codec){ 623a814da6aSMatthias Ringwald hfp_connection->ag_send_common_codec = false; 624a814da6aSMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_AG_SENT_COMMON_CODEC; 625a814da6aSMatthias Ringwald hfp_connection->suggested_codec = hfp_ag_suggest_codec(hfp_connection); 626a814da6aSMatthias Ringwald hfp_ag_send_suggest_codec_cmd(hfp_connection->rfcomm_cid, hfp_connection->suggested_codec); 627a814da6aSMatthias Ringwald return 1; 628a814da6aSMatthias Ringwald } 629a814da6aSMatthias Ringwald 630a0ffb263SMatthias Ringwald switch (hfp_connection->command){ 631aa4dd815SMatthias Ringwald case HFP_CMD_AVAILABLE_CODECS: 632a0ffb263SMatthias Ringwald if (hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED){ 633a0ffb263SMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_RECEIVED_LIST; 634485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 635aa4dd815SMatthias Ringwald return 1; 636aa4dd815SMatthias Ringwald } 637aa4dd815SMatthias Ringwald 638a0ffb263SMatthias Ringwald switch (hfp_connection->codecs_state){ 639aa4dd815SMatthias Ringwald case HFP_CODECS_AG_SENT_COMMON_CODEC: 640a0ffb263SMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_AG_RESEND_COMMON_CODEC; 641aa4dd815SMatthias Ringwald break; 642aa4dd815SMatthias Ringwald default: 643aa4dd815SMatthias Ringwald break; 644aa4dd815SMatthias Ringwald } 645485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 646aa4dd815SMatthias Ringwald return 1; 647aa4dd815SMatthias Ringwald 648aa4dd815SMatthias Ringwald case HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP: 649a0ffb263SMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE; 65040e8d6c5SMatthias Ringwald hfp_connection->establish_audio_connection = 1; 6512561704cSMatthias Ringwald hfp_connection->sco_for_msbc_failed = 0; 652485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 653aa4dd815SMatthias Ringwald return 1; 654aa4dd815SMatthias Ringwald 655aa4dd815SMatthias Ringwald case HFP_CMD_HF_CONFIRMED_CODEC: 656a0ffb263SMatthias Ringwald if (hfp_connection->codec_confirmed != hfp_connection->suggested_codec){ 657a0ffb263SMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_ERROR; 658485ac19eSMilanka Ringwald hfp_ag_send_error(hfp_connection->rfcomm_cid); 659aa4dd815SMatthias Ringwald return 1; 660aa4dd815SMatthias Ringwald } 661a0ffb263SMatthias Ringwald hfp_connection->negotiated_codec = hfp_connection->codec_confirmed; 662a0ffb263SMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_EXCHANGED; 663c1ab6cc1SMatthias Ringwald log_info("hfp: codec confirmed: %s", (hfp_connection->negotiated_codec == HFP_CODEC_MSBC) ? "mSBC" : "CVSD"); 664485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 6657522e673SMatthias Ringwald // now, pick link settings 666afac2a04SMilanka Ringwald hfp_init_link_settings(hfp_connection, hfp_ag_esco_s4_supported(hfp_connection)); 6670327df7dSMatthias Ringwald // configure SBC coded if needed 6680327df7dSMatthias Ringwald hfp_prepare_for_sco(hfp_connection); 669aa4dd815SMatthias Ringwald return 1; 670aa4dd815SMatthias Ringwald default: 671aa4dd815SMatthias Ringwald break; 672aa4dd815SMatthias Ringwald } 673aa4dd815SMatthias Ringwald return 0; 674aa4dd815SMatthias Ringwald } 675aa4dd815SMatthias Ringwald 676a0ffb263SMatthias Ringwald static void hfp_ag_slc_established(hfp_connection_t * hfp_connection){ 677a0ffb263SMatthias Ringwald hfp_connection->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; 6787095467fSMatthias Ringwald hfp_emit_slc_connection_event(hfp_connection->local_role, 0, hfp_connection->acl_handle, hfp_connection->remote_addr); 679aa4dd815SMatthias Ringwald 68040a8ee13SMatthias Ringwald // in-band ring tone is active if supported 68140a8ee13SMatthias Ringwald hfp_connection->ag_in_band_ring_tone_active = has_in_band_ring_tone(); 68240a8ee13SMatthias Ringwald 68380702f97SMatthias Ringwald // HFP 4.35: "When [...] a new Service Level Connection is established all indicators are activated by default." 68480702f97SMatthias Ringwald uint16_t i; 68580702f97SMatthias Ringwald for (i=0;i<hfp_connection->ag_indicators_nr;i++){ 68680702f97SMatthias Ringwald hfp_connection->ag_indicators[i].enabled = 1; 68780702f97SMatthias Ringwald } 68880702f97SMatthias Ringwald 689a0ffb263SMatthias Ringwald // if active call exist, set per-hfp_connection state active, too (when audio is on) 690d0c20769SMatthias Ringwald if (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ 691a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE; 692aa4dd815SMatthias Ringwald } 693ce263fc8SMatthias Ringwald // if AG is ringing, also start ringing on the HF 694c4f8fc1cSMilanka Ringwald if (hfp_gsm_call_status() == HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS){ 695c4f8fc1cSMilanka Ringwald switch (hfp_gsm_callsetup_status()){ 696c4f8fc1cSMilanka Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 6972ff1d7d7SMilanka Ringwald hfp_ag_hf_start_ringing_incoming(hfp_connection); 698c4f8fc1cSMilanka Ringwald break; 699fcd59412SMilanka Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE: 700fcd59412SMilanka Ringwald hfp_ag_hf_start_ringing_outgoing(hfp_connection); 701fcd59412SMilanka Ringwald break; 702c4f8fc1cSMilanka Ringwald default: 703c4f8fc1cSMilanka Ringwald break; 704c4f8fc1cSMilanka Ringwald } 705ce263fc8SMatthias Ringwald } 706aa4dd815SMatthias Ringwald } 7073deb3ec6SMatthias Ringwald 708a0ffb263SMatthias Ringwald static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * hfp_connection){ 709c79b4cabSMatthias Ringwald // log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command); 710a0ffb263SMatthias Ringwald if (hfp_connection->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0; 711c04cf7ffSMilanka Ringwald int sent = 0; 712a0ffb263SMatthias Ringwald switch(hfp_connection->command){ 7133deb3ec6SMatthias Ringwald case HFP_CMD_SUPPORTED_FEATURES: 714a0ffb263SMatthias Ringwald switch(hfp_connection->state){ 7153deb3ec6SMatthias Ringwald case HFP_W4_EXCHANGE_SUPPORTED_FEATURES: 716aa4dd815SMatthias Ringwald case HFP_EXCHANGE_SUPPORTED_FEATURES: 717aeb0f0feSMatthias Ringwald hfp_hf_drop_mSBC_if_eSCO_not_supported(hfp_ag_codecs, &hfp_ag_codecs_nr); 718a0ffb263SMatthias Ringwald if (has_codec_negotiation_feature(hfp_connection)){ 719a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_NOTIFY_ON_CODECS; 720aa4dd815SMatthias Ringwald } else { 721a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_RETRIEVE_INDICATORS; 722aa4dd815SMatthias Ringwald } 723a0ffb263SMatthias Ringwald hfp_ag_exchange_supported_features_cmd(hfp_connection->rfcomm_cid); 724aa4dd815SMatthias Ringwald return 1; 7253deb3ec6SMatthias Ringwald default: 7263deb3ec6SMatthias Ringwald break; 7273deb3ec6SMatthias Ringwald } 7283deb3ec6SMatthias Ringwald break; 7293deb3ec6SMatthias Ringwald case HFP_CMD_AVAILABLE_CODECS: 730c04cf7ffSMilanka Ringwald sent = codecs_exchange_state_machine(hfp_connection); 731a0ffb263SMatthias Ringwald if (hfp_connection->codecs_state == HFP_CODECS_RECEIVED_LIST){ 732a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_RETRIEVE_INDICATORS; 7333deb3ec6SMatthias Ringwald } 734c04cf7ffSMilanka Ringwald return sent; 735aa4dd815SMatthias Ringwald 736aa4dd815SMatthias Ringwald case HFP_CMD_RETRIEVE_AG_INDICATORS: 737a0ffb263SMatthias Ringwald if (hfp_connection->state == HFP_W4_RETRIEVE_INDICATORS) { 738473ac565SMatthias Ringwald // HF requested AG Indicators and we did expect it 73984a0c24eSMatthias Ringwald hfp_connection->send_ag_indicators_segment = 0; 740473ac565SMatthias Ringwald hfp_connection->state = HFP_RETRIEVE_INDICATORS; 741473ac565SMatthias Ringwald // continue below in state switch 742ad902e3dSMatthias Ringwald } 743ad902e3dSMatthias Ringwald break; 744aa4dd815SMatthias Ringwald 745aa4dd815SMatthias Ringwald case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: 746a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_W4_RETRIEVE_INDICATORS_STATUS) break; 747a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE; 748485ac19eSMilanka Ringwald hfp_ag_send_retrieve_indicators_status_cmd(hfp_connection->rfcomm_cid); 749aa4dd815SMatthias Ringwald return 1; 750aa4dd815SMatthias Ringwald 7513deb3ec6SMatthias Ringwald case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: 752a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE) break; 753a0ffb263SMatthias Ringwald if (has_call_waiting_and_3way_calling_feature(hfp_connection)){ 754a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL; 755880e8fc6SMatthias Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 756a0ffb263SMatthias Ringwald } else if (has_hf_indicators_feature(hfp_connection)){ 757a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; 758880e8fc6SMatthias Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 759aa4dd815SMatthias Ringwald } else { 760880e8fc6SMatthias Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 761a0ffb263SMatthias Ringwald hfp_ag_slc_established(hfp_connection); 7623deb3ec6SMatthias Ringwald } 763aa4dd815SMatthias Ringwald return 1; 7643deb3ec6SMatthias Ringwald 765aa4dd815SMatthias Ringwald case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES: 766a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_W4_RETRIEVE_CAN_HOLD_CALL) break; 767a0ffb263SMatthias Ringwald if (has_hf_indicators_feature(hfp_connection)){ 768a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; 769aa4dd815SMatthias Ringwald } 770485ac19eSMilanka Ringwald hfp_ag_send_retrieve_can_hold_call_cmd(hfp_connection->rfcomm_cid); 771a0ffb263SMatthias Ringwald if (!has_hf_indicators_feature(hfp_connection)){ 772a0ffb263SMatthias Ringwald hfp_ag_slc_established(hfp_connection); 773ce263fc8SMatthias Ringwald } 774aa4dd815SMatthias Ringwald return 1; 775aa4dd815SMatthias Ringwald 776aa4dd815SMatthias Ringwald case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: 777a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_W4_LIST_GENERIC_STATUS_INDICATORS) break; 778a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS; 779485ac19eSMilanka Ringwald hfp_ag_send_list_supported_generic_status_indicators_cmd(hfp_connection->rfcomm_cid); 780aa4dd815SMatthias Ringwald return 1; 781aa4dd815SMatthias Ringwald 782aa4dd815SMatthias Ringwald case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS: 783a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS) break; 784a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS; 785485ac19eSMilanka Ringwald hfp_ag_send_retrieve_supported_generic_status_indicators_cmd(hfp_connection->rfcomm_cid); 786aa4dd815SMatthias Ringwald return 1; 787aa4dd815SMatthias Ringwald 788aa4dd815SMatthias Ringwald case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE: 789a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS) break; 790485ac19eSMilanka Ringwald hfp_ag_send_retrieve_initital_supported_generic_status_indicators_cmd(hfp_connection->rfcomm_cid); 791a7666b02SMatthias Ringwald hfp_ag_slc_established(hfp_connection); 792aa4dd815SMatthias Ringwald return 1; 7933deb3ec6SMatthias Ringwald default: 7943deb3ec6SMatthias Ringwald break; 7953deb3ec6SMatthias Ringwald } 796473ac565SMatthias Ringwald 797473ac565SMatthias Ringwald switch (hfp_connection->state){ 798473ac565SMatthias Ringwald case HFP_RETRIEVE_INDICATORS: { 799485ac19eSMilanka Ringwald int next_segment = hfp_ag_send_retrieve_indicators_cmd_via_generator(hfp_connection->rfcomm_cid, hfp_connection, hfp_connection->send_ag_indicators_segment); 800473ac565SMatthias Ringwald int num_segments = hfp_ag_indicators_cmd_generator_num_segments(hfp_connection); 801473ac565SMatthias Ringwald log_info("HFP_CMD_RETRIEVE_AG_INDICATORS next segment %u, num_segments %u", next_segment, num_segments); 802473ac565SMatthias Ringwald if (next_segment < num_segments){ 803473ac565SMatthias Ringwald // prepare sending of next segment 804473ac565SMatthias Ringwald hfp_connection->send_ag_indicators_segment = next_segment; 805473ac565SMatthias Ringwald log_info("HFP_CMD_RETRIEVE_AG_INDICATORS more. command %u, next seg %u", hfp_connection->command, next_segment); 806473ac565SMatthias Ringwald } else { 807473ac565SMatthias Ringwald // done, go to next state 808473ac565SMatthias Ringwald hfp_connection->send_ag_indicators_segment = 0; 809473ac565SMatthias Ringwald hfp_connection->state = HFP_W4_RETRIEVE_INDICATORS_STATUS; 810473ac565SMatthias Ringwald } 811473ac565SMatthias Ringwald return 1; 812473ac565SMatthias Ringwald } 813473ac565SMatthias Ringwald default: 814473ac565SMatthias Ringwald break; 815473ac565SMatthias Ringwald } 816c04cf7ffSMilanka Ringwald return 0; 8173deb3ec6SMatthias Ringwald } 8183deb3ec6SMatthias Ringwald 8199cd26862SMilanka Ringwald static bool hfp_ag_vra_flag_supported(hfp_connection_t * hfp_connection){ 82014c148caSMilanka Ringwald int ag = get_bit(hfp_ag_supported_features, HFP_AGSF_VOICE_RECOGNITION_FUNCTION); 82114c148caSMilanka Ringwald int hf = get_bit(hfp_connection->remote_supported_features, HFP_HFSF_VOICE_RECOGNITION_FUNCTION); 82214c148caSMilanka Ringwald return hf && ag; 82314c148caSMilanka Ringwald } 82414c148caSMilanka Ringwald 8259cd26862SMilanka Ringwald static bool hfp_ag_enhanced_vra_flag_supported(hfp_connection_t * hfp_connection){ 82614c148caSMilanka Ringwald int ag = get_bit(hfp_ag_supported_features, HFP_AGSF_ENHANCED_VOICE_RECOGNITION_STATUS); 82714c148caSMilanka Ringwald int hf = get_bit(hfp_connection->remote_supported_features, HFP_HFSF_ENHANCED_VOICE_RECOGNITION_STATUS); 82814c148caSMilanka Ringwald return hf && ag; 82914c148caSMilanka Ringwald } 83014c148caSMilanka Ringwald 8319cd26862SMilanka Ringwald static bool hfp_ag_can_send_enhanced_vra_message_flag_supported(hfp_connection_t * hfp_connection){ 832f71d2aacSMilanka Ringwald int ag = get_bit(hfp_ag_supported_features, HFP_AGSF_VOICE_RECOGNITION_TEXT); 833f71d2aacSMilanka Ringwald int hf = get_bit(hfp_connection->remote_supported_features, HFP_HFSF_VOICE_RECOGNITION_TEXT); 834f71d2aacSMilanka Ringwald return hf && ag; 83508a0b01cSMilanka Ringwald } 836f71d2aacSMilanka Ringwald 837135d9718SMilanka Ringwald static bool hfp_ag_can_activate_voice_recognition(hfp_connection_t * hfp_connection){ 838f71d2aacSMilanka Ringwald if (hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED || hfp_connection->state > HFP_AUDIO_CONNECTION_ESTABLISHED){ 839135d9718SMilanka Ringwald return false; 840d84719b1SMilanka Ringwald } 8410c738fd4SMilanka Ringwald if (hfp_connection->vra_state != HFP_VRA_VOICE_RECOGNITION_OFF){ 842135d9718SMilanka Ringwald return false; 843d84719b1SMilanka Ringwald } 84408dc7cc6SMatthias Ringwald if (hfp_connection->ag_vra_send_command){ 84508dc7cc6SMatthias Ringwald return false; 84608dc7cc6SMatthias Ringwald } 847135d9718SMilanka Ringwald return true; 848d84719b1SMilanka Ringwald } 849d84719b1SMilanka Ringwald 850135d9718SMilanka Ringwald static bool hfp_ag_voice_recognition_session_active(hfp_connection_t * hfp_connection){ 85108a0b01cSMilanka Ringwald if (hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED || hfp_connection->state > HFP_AUDIO_CONNECTION_ESTABLISHED){ 852135d9718SMilanka Ringwald return false; 85308a0b01cSMilanka Ringwald } 8540c738fd4SMilanka Ringwald if (hfp_connection->vra_state != HFP_VRA_VOICE_RECOGNITION_ACTIVATED){ 855de9e0ea7SMilanka Ringwald if (hfp_connection->vra_state == HFP_VRA_ENHANCED_VOICE_RECOGNITION_READY_FOR_AUDIO){ 856135d9718SMilanka Ringwald return true; 857de9e0ea7SMilanka Ringwald } 858135d9718SMilanka Ringwald return false; 859d84719b1SMilanka Ringwald } 860135d9718SMilanka Ringwald return true; 861d84719b1SMilanka Ringwald } 862754910caSMilanka Ringwald 8630b4debbfSMilanka Ringwald static bool hfp_ag_is_audio_connection_active(hfp_connection_t * hfp_connection){ 8640b4debbfSMilanka Ringwald switch (hfp_connection->state){ 8650b4debbfSMilanka Ringwald case HFP_W2_CONNECT_SCO: 8660b4debbfSMilanka Ringwald case HFP_W4_SCO_CONNECTED: 8670b4debbfSMilanka Ringwald case HFP_AUDIO_CONNECTION_ESTABLISHED: 8680b4debbfSMilanka Ringwald return true; 869754910caSMilanka Ringwald default: 8700b4debbfSMilanka Ringwald return false; 871754910caSMilanka Ringwald } 872754910caSMilanka Ringwald } 873754910caSMilanka Ringwald 874b95cac54SMilanka Ringwald static void hfp_ag_emit_enhanced_voice_recognition_msg_sent_event(hfp_connection_t * hfp_connection, uint8_t status){ 875b95cac54SMilanka Ringwald hci_con_handle_t acl_handle = (hfp_connection != NULL) ? hfp_connection->acl_handle : HCI_CON_HANDLE_INVALID; 876b95cac54SMilanka Ringwald 877b95cac54SMilanka Ringwald uint8_t event[6]; 878b95cac54SMilanka Ringwald event[0] = HCI_EVENT_HFP_META; 879b95cac54SMilanka Ringwald event[1] = sizeof(event) - 2; 880c95b5b3cSMilanka Ringwald event[2] = HFP_SUBEVENT_ENHANCED_VOICE_RECOGNITION_AG_MESSAGE_SENT; 881b95cac54SMilanka Ringwald little_endian_store_16(event, 3, acl_handle); 882b95cac54SMilanka Ringwald event[5] = status; 883b95cac54SMilanka Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 884b95cac54SMilanka Ringwald } 885b95cac54SMilanka Ringwald 886347d46c8SMilanka Ringwald 887585db78dSMilanka Ringwald static void hfp_ag_emit_hf_indicator_value(hfp_connection_t * hfp_connection, uint16_t uuid, uint8_t value){ 888347d46c8SMilanka Ringwald hci_con_handle_t acl_handle = (hfp_connection != NULL) ? hfp_connection->acl_handle : HCI_CON_HANDLE_INVALID; 889347d46c8SMilanka Ringwald 890347d46c8SMilanka Ringwald uint8_t event[8]; 891347d46c8SMilanka Ringwald event[0] = HCI_EVENT_HFP_META; 892347d46c8SMilanka Ringwald event[1] = sizeof(event) - 2; 893347d46c8SMilanka Ringwald event[2] = HFP_SUBEVENT_HF_INDICATOR; 894347d46c8SMilanka Ringwald little_endian_store_16(event, 3, acl_handle); 895347d46c8SMilanka Ringwald little_endian_store_16(event, 5, uuid); 896347d46c8SMilanka Ringwald event[7] = value; 897347d46c8SMilanka Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 898347d46c8SMilanka Ringwald } 899347d46c8SMilanka Ringwald 900a4f85bd2SMilanka Ringwald static void hfp_ag_emit_general_simple_event(uint8_t event_subtype){ 901a4f85bd2SMilanka Ringwald if (!hfp_ag_callback) return; 902a4f85bd2SMilanka Ringwald 903a4f85bd2SMilanka Ringwald uint8_t event[5]; 904a4f85bd2SMilanka Ringwald event[0] = HCI_EVENT_HFP_META; 905a4f85bd2SMilanka Ringwald event[1] = sizeof(event) - 2; 906a4f85bd2SMilanka Ringwald event[2] = event_subtype; 907a4f85bd2SMilanka Ringwald little_endian_store_16(event, 3, HCI_CON_HANDLE_INVALID); 908a4f85bd2SMilanka Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 909a4f85bd2SMilanka Ringwald } 910a4f85bd2SMilanka Ringwald 911*edc46354SMatthias Ringwald void hfp_ag_emit_apple_accessory_information(hfp_connection_t *hfp_connection){ 912*edc46354SMatthias Ringwald uint8_t event[40]; 913*edc46354SMatthias Ringwald uint16_t version_len = strlen(hfp_connection->apple_accessory_version); 914*edc46354SMatthias Ringwald event[0] = HCI_EVENT_HFP_META; 915*edc46354SMatthias Ringwald event[1] = 9 + version_len; 916*edc46354SMatthias Ringwald event[2] = HFP_SUBEVENT_APPLE_ACCESSORY_INFORMATION; 917*edc46354SMatthias Ringwald little_endian_store_16(event, 3, hfp_connection->acl_handle); 918*edc46354SMatthias Ringwald little_endian_store_16(event, 5, hfp_connection->apple_accessory_vendor_id); 919*edc46354SMatthias Ringwald little_endian_store_16(event, 7, hfp_connection->apple_accessory_product_id); 920*edc46354SMatthias Ringwald event[9] = hfp_connection->apple_accessory_features; 921*edc46354SMatthias Ringwald event[10] = version_len; 922*edc46354SMatthias Ringwald memcpy(&event[11], hfp_connection->apple_accessory_version, version_len); 923*edc46354SMatthias Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, 11 + version_len); 924*edc46354SMatthias Ringwald } 925*edc46354SMatthias Ringwald 926*edc46354SMatthias Ringwald void hfp_ag_emit_apple_accessory_state(hfp_connection_t *hfp_connection){ 927*edc46354SMatthias Ringwald uint8_t event[40]; 928*edc46354SMatthias Ringwald event[0] = HCI_EVENT_HFP_META; 929*edc46354SMatthias Ringwald event[1] = 2; 930*edc46354SMatthias Ringwald if (hfp_connection->apple_accessory_battery_level >= 0){ 931*edc46354SMatthias Ringwald event[2] = HFP_SUBEVENT_APPLE_BATTERY_LEVEL; 932*edc46354SMatthias Ringwald event[3] = hfp_connection->apple_accessory_battery_level; 933*edc46354SMatthias Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, 4); 934*edc46354SMatthias Ringwald } 935*edc46354SMatthias Ringwald if (hfp_connection->apple_accessory_docked >= 0){ 936*edc46354SMatthias Ringwald event[2] = HFP_SUBEVENT_APPLE_DOCKED_STATE; 937*edc46354SMatthias Ringwald event[3] = hfp_connection->apple_accessory_docked; 938*edc46354SMatthias Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, 4); 939*edc46354SMatthias Ringwald } 940*edc46354SMatthias Ringwald } 941*edc46354SMatthias Ringwald 942471dea41SMatthias Ringwald static void hfp_ag_emit_custom_command_event(hfp_connection_t * hfp_connection){ 943471dea41SMatthias Ringwald btstack_assert(sizeof(hfp_connection->line_buffer) < (255-5)); 944471dea41SMatthias Ringwald 9451a3bc516SMatthias Ringwald uint16_t line_len = (uint16_t) strlen((const char*)hfp_connection->line_buffer) + 1; 946471dea41SMatthias Ringwald uint8_t event[7 + sizeof(hfp_connection->line_buffer)]; 947471dea41SMatthias Ringwald event[0] = HCI_EVENT_HFP_META; 948471dea41SMatthias Ringwald event[1] = 5 + line_len; 949471dea41SMatthias Ringwald event[2] = HFP_SUBEVENT_CUSTOM_AT_COMMAND; 950471dea41SMatthias Ringwald little_endian_store_16(event, 3, hfp_connection->acl_handle); 9516d9a41f9SMatthias Ringwald little_endian_store_16(event, 5, hfp_connection->custom_at_command_id); 952471dea41SMatthias Ringwald memcpy(&event[7], hfp_connection->line_buffer, line_len); 953471dea41SMatthias Ringwald (*hfp_ag_callback)(HCI_EVENT_PACKET, 0, event, 7 + line_len); 954471dea41SMatthias Ringwald } 955471dea41SMatthias Ringwald 956774e3016SMatthias Ringwald // @return status 957774e3016SMatthias Ringwald static uint8_t hfp_ag_vra_state_machine_two(hfp_connection_t * hfp_connection){ 958774e3016SMatthias Ringwald uint8_t status = ERROR_CODE_SUCCESS; 959774e3016SMatthias Ringwald switch (hfp_connection->vra_state_requested){ 960774e3016SMatthias Ringwald case HFP_VRA_W2_SEND_ENHANCED_VOICE_RECOGNITION_READY_FOR_AUDIO: 961774e3016SMatthias Ringwald if (!hfp_ag_is_audio_connection_active(hfp_connection)){ 962774e3016SMatthias Ringwald status = hfp_ag_setup_audio_connection(hfp_connection); 963774e3016SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){ 964774e3016SMatthias Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 965774e3016SMatthias Ringwald hfp_emit_voice_recognition_enabled(hfp_connection, status); 966774e3016SMatthias Ringwald break; 967774e3016SMatthias Ringwald } 968774e3016SMatthias Ringwald } 969774e3016SMatthias Ringwald hfp_connection->enhanced_voice_recognition_enabled = true; 970774e3016SMatthias Ringwald hfp_connection->vra_state = HFP_VRA_ENHANCED_VOICE_RECOGNITION_READY_FOR_AUDIO; 971774e3016SMatthias Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 972774e3016SMatthias Ringwald hfp_emit_enhanced_voice_recognition_hf_ready_for_audio_event(hfp_connection, ERROR_CODE_SUCCESS); 973774e3016SMatthias Ringwald break; 974774e3016SMatthias Ringwald 975774e3016SMatthias Ringwald case HFP_VRA_W2_SEND_VOICE_RECOGNITION_OFF: 976774e3016SMatthias Ringwald hfp_connection->vra_state = HFP_VRA_VOICE_RECOGNITION_OFF; 977774e3016SMatthias Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 978774e3016SMatthias Ringwald 979774e3016SMatthias Ringwald // release audio connection only if it was opened after audio VR activated 980774e3016SMatthias Ringwald if (hfp_ag_is_audio_connection_active(hfp_connection) && !hfp_connection->ag_audio_connection_opened_before_vra){ 981774e3016SMatthias Ringwald hfp_trigger_release_audio_connection(hfp_connection); 982774e3016SMatthias Ringwald } 983774e3016SMatthias Ringwald 984774e3016SMatthias Ringwald hfp_emit_voice_recognition_disabled(hfp_connection, ERROR_CODE_SUCCESS); 985774e3016SMatthias Ringwald break; 986774e3016SMatthias Ringwald 987774e3016SMatthias Ringwald case HFP_VRA_W2_SEND_VOICE_RECOGNITION_ACTIVATED: 988774e3016SMatthias Ringwald // open only if audio connection does not already exist 989774e3016SMatthias Ringwald if (!hfp_ag_is_audio_connection_active(hfp_connection)){ 990774e3016SMatthias Ringwald status = hfp_ag_setup_audio_connection(hfp_connection); 991774e3016SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){ 992774e3016SMatthias Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 993774e3016SMatthias Ringwald hfp_emit_voice_recognition_enabled(hfp_connection, status); 994774e3016SMatthias Ringwald break; 995774e3016SMatthias Ringwald } 996774e3016SMatthias Ringwald } 997774e3016SMatthias Ringwald 998774e3016SMatthias Ringwald hfp_connection->vra_state = HFP_VRA_VOICE_RECOGNITION_ACTIVATED; 999774e3016SMatthias Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 1000774e3016SMatthias Ringwald hfp_connection->enhanced_voice_recognition_enabled = hfp_ag_enhanced_vra_flag_supported(hfp_connection); 100184fb9ac1SMilanka Ringwald 100284fb9ac1SMilanka Ringwald if (hfp_connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED){ 1003774e3016SMatthias Ringwald hfp_emit_voice_recognition_enabled(hfp_connection, ERROR_CODE_SUCCESS); 100484fb9ac1SMilanka Ringwald } else { 100584fb9ac1SMilanka Ringwald hfp_connection->emit_vra_enabled_after_audio_established = true; 100684fb9ac1SMilanka Ringwald } 1007774e3016SMatthias Ringwald break; 1008774e3016SMatthias Ringwald 1009774e3016SMatthias Ringwald default: 1010774e3016SMatthias Ringwald break; 1011774e3016SMatthias Ringwald } 1012774e3016SMatthias Ringwald return status; 1013774e3016SMatthias Ringwald } 1014774e3016SMatthias Ringwald 101508dc7cc6SMatthias Ringwald static uint8_t hfp_ag_vra_send_command(hfp_connection_t * hfp_connection){ 101608dc7cc6SMatthias Ringwald uint8_t done = 0; 1017754910caSMilanka Ringwald uint8_t status; 1018754910caSMilanka Ringwald 1019cf75be85SMilanka Ringwald switch (hfp_connection->vra_state_requested){ 1020b95cac54SMilanka Ringwald case HFP_VRA_W2_SEND_ENHANCED_VOICE_RECOGNITION_MSG: 1021b95cac54SMilanka Ringwald done = hfp_ag_send_enhanced_voice_recognition_msg_cmd(hfp_connection); 1022b95cac54SMilanka Ringwald if (done == 0){ 1023b95cac54SMilanka Ringwald hfp_ag_emit_enhanced_voice_recognition_msg_sent_event(hfp_connection, ERROR_CODE_UNSPECIFIED_ERROR); 1024b95cac54SMilanka Ringwald } else { 1025b95cac54SMilanka Ringwald hfp_ag_emit_enhanced_voice_recognition_msg_sent_event(hfp_connection, ERROR_CODE_SUCCESS); 1026b95cac54SMilanka Ringwald } 1027b95cac54SMilanka Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 1028b95cac54SMilanka Ringwald return done; 1029b95cac54SMilanka Ringwald 1030cf75be85SMilanka Ringwald case HFP_VRA_W2_SEND_ENHANCED_VOICE_RECOGNITION_STATUS: 1031cf75be85SMilanka Ringwald done = hfp_ag_send_enhanced_voice_recognition_state_cmd(hfp_connection); 1032cf75be85SMilanka Ringwald if (done == 0){ 1033cf75be85SMilanka Ringwald hfp_emit_enhanced_voice_recognition_state_event(hfp_connection, ERROR_CODE_UNSPECIFIED_ERROR); 1034cf75be85SMilanka Ringwald } else { 1035cf75be85SMilanka Ringwald hfp_emit_enhanced_voice_recognition_state_event(hfp_connection, ERROR_CODE_SUCCESS); 1036cf75be85SMilanka Ringwald } 1037cf75be85SMilanka Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 1038b95cac54SMilanka Ringwald return done; 1039b95cac54SMilanka Ringwald 10406e13e408SMatthias Ringwald case HFP_VRA_W2_SEND_VOICE_RECOGNITION_ACTIVATED: 10416e13e408SMatthias Ringwald case HFP_VRA_W2_SEND_VOICE_RECOGNITION_OFF: 1042d1b50446SMilanka Ringwald done = hfp_ag_send_voice_recognition_cmd(hfp_connection, hfp_connection->ag_activate_voice_recognition_value); 10430c738fd4SMilanka Ringwald if (done == 0){ 10440c738fd4SMilanka Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 1045553a4a56SMilanka Ringwald 1046553a4a56SMilanka Ringwald if (hfp_connection->ag_activate_voice_recognition_value == 1){ 1047553a4a56SMilanka Ringwald hfp_emit_voice_recognition_enabled(hfp_connection, done); 1048553a4a56SMilanka Ringwald } else { 1049553a4a56SMilanka Ringwald hfp_emit_voice_recognition_disabled(hfp_connection, done); 1050553a4a56SMilanka Ringwald } 1051fd4151d1SMilanka Ringwald return 0; 1052fd4151d1SMilanka Ringwald } 105308dc7cc6SMatthias Ringwald status = hfp_ag_vra_state_machine_two(hfp_connection); 105408dc7cc6SMatthias Ringwald return (status == ERROR_CODE_SUCCESS) ? 1 : 0; 105508dc7cc6SMatthias Ringwald 10566e13e408SMatthias Ringwald default: 10576e13e408SMatthias Ringwald log_error("state %u", hfp_connection->vra_state_requested); 10586e13e408SMatthias Ringwald btstack_assert(false); 105908dc7cc6SMatthias Ringwald return 0; 1060cf75be85SMilanka Ringwald } 106108dc7cc6SMatthias Ringwald } 106208dc7cc6SMatthias Ringwald 106308dc7cc6SMatthias Ringwald static int hfp_ag_voice_recognition_state_machine(hfp_connection_t * hfp_connection){ 106408dc7cc6SMatthias Ringwald if (hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) { 106508dc7cc6SMatthias Ringwald return 0; 106608dc7cc6SMatthias Ringwald } 106708dc7cc6SMatthias Ringwald int done = 0; 106808dc7cc6SMatthias Ringwald uint8_t status; 106908dc7cc6SMatthias Ringwald 10708f8818a4SMatthias Ringwald // VRA action initiated by AG 10718f8818a4SMatthias Ringwald if (hfp_connection->ag_vra_send_command){ 10728f8818a4SMatthias Ringwald hfp_connection->ag_vra_send_command = false; 10738f8818a4SMatthias Ringwald return hfp_ag_vra_send_command(hfp_connection); 10748f8818a4SMatthias Ringwald } 10758f8818a4SMatthias Ringwald 1076e7c46708SMatthias Ringwald if (hfp_connection->ag_vra_requested_by_hf){ 1077e7c46708SMatthias Ringwald hfp_connection->ag_vra_requested_by_hf = false; 1078e7c46708SMatthias Ringwald 1079734ef2bdSMilanka Ringwald // HF initiatied voice recognition, parser extracted activation value 1080de9e0ea7SMilanka Ringwald switch (hfp_connection->ag_activate_voice_recognition_value){ 1081de9e0ea7SMilanka Ringwald case 0: 1082135d9718SMilanka Ringwald if (hfp_ag_voice_recognition_session_active(hfp_connection)){ 1083fd4151d1SMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_VOICE_RECOGNITION_OFF; 1084fd4151d1SMilanka Ringwald done = hfp_ag_send_ok(hfp_connection->rfcomm_cid); 1085fd4151d1SMilanka Ringwald } 1086de9e0ea7SMilanka Ringwald break; 1087de9e0ea7SMilanka Ringwald 1088de9e0ea7SMilanka Ringwald case 1: 1089135d9718SMilanka Ringwald if (hfp_ag_can_activate_voice_recognition(hfp_connection)){ 1090fd4151d1SMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_VOICE_RECOGNITION_ACTIVATED; 1091fd4151d1SMilanka Ringwald done = hfp_ag_send_ok(hfp_connection->rfcomm_cid); 1092fd4151d1SMilanka Ringwald } 1093de9e0ea7SMilanka Ringwald break; 1094de9e0ea7SMilanka Ringwald 1095de9e0ea7SMilanka Ringwald case 2: 1096135d9718SMilanka Ringwald if (hfp_ag_voice_recognition_session_active(hfp_connection)){ 1097de9e0ea7SMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_ENHANCED_VOICE_RECOGNITION_READY_FOR_AUDIO; 1098de9e0ea7SMilanka Ringwald done = hfp_ag_send_ok(hfp_connection->rfcomm_cid); 1099de9e0ea7SMilanka Ringwald } 1100de9e0ea7SMilanka Ringwald break; 1101de9e0ea7SMilanka Ringwald default: 1102de9e0ea7SMilanka Ringwald break; 1103fd4151d1SMilanka Ringwald } 1104fd4151d1SMilanka Ringwald if (done == 0){ 1105fd4151d1SMilanka Ringwald hfp_connection->vra_state_requested = hfp_connection->vra_state; 1106fd4151d1SMilanka Ringwald done = hfp_ag_send_error(hfp_connection->rfcomm_cid); 1107fd4151d1SMilanka Ringwald return 1; 1108fd4151d1SMilanka Ringwald } 1109754910caSMilanka Ringwald 1110774e3016SMatthias Ringwald status = hfp_ag_vra_state_machine_two(hfp_connection); 11112cc52945SMatthias Ringwald return (status == ERROR_CODE_SUCCESS) ? 1 : 0; 1112de9e0ea7SMilanka Ringwald } 11132cc52945SMatthias Ringwald 11142cc52945SMatthias Ringwald return 0; 1115754910caSMilanka Ringwald } 1116754910caSMilanka Ringwald 1117a0ffb263SMatthias Ringwald static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connection_t * hfp_connection){ 1118dc6af379SMatthias Ringwald if (hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) { 1119dc6af379SMatthias Ringwald return 0; 1120dc6af379SMatthias Ringwald } 1121dc6af379SMatthias Ringwald 1122c04cf7ffSMilanka Ringwald int sent = codecs_exchange_state_machine(hfp_connection); 1123c04cf7ffSMilanka Ringwald if (sent) return 1; 1124aa4dd815SMatthias Ringwald 112540a8ee13SMatthias Ringwald if (has_in_band_ring_tone()){ 112640a8ee13SMatthias Ringwald if (hfp_connection->ag_in_band_ring_tone_active != hfp_ag_in_band_ring_tone_active) { 112740a8ee13SMatthias Ringwald hfp_connection->ag_in_band_ring_tone_active = hfp_ag_in_band_ring_tone_active; 1128f197e761SMatthias Ringwald hfp_ag_send_change_in_band_ring_tone_setting_cmd(hfp_connection); 1129aa4dd815SMatthias Ringwald return 1; 11309430c71eSMatthias Ringwald } 113140a8ee13SMatthias Ringwald } 11329430c71eSMatthias Ringwald 11339430c71eSMatthias Ringwald switch(hfp_connection->command){ 1134aa4dd815SMatthias Ringwald case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: 1135485ac19eSMilanka Ringwald hfp_ag_send_report_network_operator_name_cmd(hfp_connection->rfcomm_cid, hfp_connection->network_operator); 1136aa4dd815SMatthias Ringwald return 1; 1137aa4dd815SMatthias Ringwald case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: 1138a0ffb263SMatthias Ringwald if (hfp_connection->network_operator.format != 0){ 1139485ac19eSMilanka Ringwald hfp_ag_send_error(hfp_connection->rfcomm_cid); 1140aa4dd815SMatthias Ringwald } else { 1141485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 11423deb3ec6SMatthias Ringwald } 1143aa4dd815SMatthias Ringwald return 1; 1144aa4dd815SMatthias Ringwald case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE: 1145485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 1146aa4dd815SMatthias Ringwald return 1; 11473deb3ec6SMatthias Ringwald case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR: 1148df9853c5SMatthias Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 1149df9853c5SMatthias Ringwald return 1; 11503deb3ec6SMatthias Ringwald case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: 1151485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 1152ce263fc8SMatthias Ringwald return 1; 11533deb3ec6SMatthias Ringwald default: 11543deb3ec6SMatthias Ringwald break; 11553deb3ec6SMatthias Ringwald } 1156aa4dd815SMatthias Ringwald return 0; 11573deb3ec6SMatthias Ringwald } 11583deb3ec6SMatthias Ringwald 1159a0ffb263SMatthias Ringwald static int hfp_ag_run_for_audio_connection(hfp_connection_t * hfp_connection){ 1160505f1c30SMatthias Ringwald if ((hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) || 1161505f1c30SMatthias Ringwald (hfp_connection->state > HFP_W2_DISCONNECT_SCO)) return 0; 11623deb3ec6SMatthias Ringwald 1163a0ffb263SMatthias Ringwald if (hfp_connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; 116452cbdd6dSMilanka Ringwald 116552cbdd6dSMilanka Ringwald if (hfp_connection->release_audio_connection){ 116652cbdd6dSMilanka Ringwald hfp_connection->state = HFP_W4_SCO_DISCONNECTED; 116752cbdd6dSMilanka Ringwald hfp_connection->release_audio_connection = 0; 116852cbdd6dSMilanka Ringwald gap_disconnect(hfp_connection->sco_handle); 116952cbdd6dSMilanka Ringwald return 1; 117052cbdd6dSMilanka Ringwald } 1171aa4dd815SMatthias Ringwald 117229cddf58SMatthias Ringwald // accept incoming audio connection (codec negotiation is not used) 1173447743f7SMatthias Ringwald if (hfp_connection->accept_sco && (hfp_sco_setup_active() == false)){ 117429cddf58SMatthias Ringwald // notify about codec selection if not done already 117529cddf58SMatthias Ringwald if (hfp_connection->negotiated_codec == 0){ 117629cddf58SMatthias Ringwald hfp_connection->negotiated_codec = HFP_CODEC_CVSD; 117729cddf58SMatthias Ringwald } 117829cddf58SMatthias Ringwald // 117929cddf58SMatthias Ringwald bool incoming_eSCO = hfp_connection->accept_sco == 2; 118029cddf58SMatthias Ringwald hfp_connection->state = HFP_W4_SCO_CONNECTED; 118129cddf58SMatthias Ringwald hfp_accept_synchronous_connection(hfp_connection, incoming_eSCO); 118229cddf58SMatthias Ringwald return 1; 118329cddf58SMatthias Ringwald } 118429cddf58SMatthias Ringwald 1185aa4dd815SMatthias Ringwald // run codecs exchange 1186c04cf7ffSMilanka Ringwald int sent = codecs_exchange_state_machine(hfp_connection); 1187c04cf7ffSMilanka Ringwald if (sent) return 1; 1188aa4dd815SMatthias Ringwald 1189c04cf7ffSMilanka Ringwald if (hfp_connection->codecs_state != HFP_CODECS_EXCHANGED) return 0; 1190447743f7SMatthias Ringwald if (hfp_sco_setup_active()) return 0; 1191d2e34ffbSMatthias Ringwald if (hci_can_send_command_packet_now() == false) return 0; 1192a0ffb263SMatthias Ringwald if (hfp_connection->establish_audio_connection){ 1193a0ffb263SMatthias Ringwald hfp_connection->state = HFP_W4_SCO_CONNECTED; 1194a0ffb263SMatthias Ringwald hfp_connection->establish_audio_connection = 0; 1195eddcd308SMatthias Ringwald hfp_setup_synchronous_connection(hfp_connection); 1196aa4dd815SMatthias Ringwald return 1; 1197aa4dd815SMatthias Ringwald } 1198aa4dd815SMatthias Ringwald return 0; 1199aa4dd815SMatthias Ringwald } 1200aa4dd815SMatthias Ringwald 1201056efd45SMatthias Ringwald static void hfp_ag_set_callsetup_indicator(void){ 1202056efd45SMatthias Ringwald hfp_ag_indicator_t * indicator = get_ag_indicator_for_name("callsetup"); 1203056efd45SMatthias Ringwald if (!indicator){ 1204056efd45SMatthias Ringwald log_error("hfp_ag_set_callsetup_indicator: callsetup indicator is missing"); 1205056efd45SMatthias Ringwald return; 1206056efd45SMatthias Ringwald }; 1207056efd45SMatthias Ringwald indicator->status = hfp_gsm_callsetup_status(); 1208056efd45SMatthias Ringwald } 1209056efd45SMatthias Ringwald 1210056efd45SMatthias Ringwald static void hfp_ag_set_callheld_indicator(void){ 1211056efd45SMatthias Ringwald hfp_ag_indicator_t * indicator = get_ag_indicator_for_name("callheld"); 1212056efd45SMatthias Ringwald if (!indicator){ 1213056efd45SMatthias Ringwald log_error("hfp_ag_set_callheld_state: callheld indicator is missing"); 1214056efd45SMatthias Ringwald return; 1215056efd45SMatthias Ringwald }; 1216056efd45SMatthias Ringwald indicator->status = hfp_gsm_callheld_status(); 1217056efd45SMatthias Ringwald } 1218056efd45SMatthias Ringwald 1219aa4dd815SMatthias Ringwald // 1220fcd59412SMilanka Ringwald // transition implementations for hfp_ag_call_state_machine 1221aa4dd815SMatthias Ringwald // 1222aa4dd815SMatthias Ringwald 12232ff1d7d7SMilanka Ringwald static void hfp_ag_hf_start_ringing_incoming(hfp_connection_t * hfp_connection){ 1224f197e761SMatthias Ringwald if (use_in_band_tone(hfp_connection)){ 1225a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING; 1226d97d752dSMilanka Ringwald hfp_ag_establish_audio_connection(hfp_connection->acl_handle); 1227aa4dd815SMatthias Ringwald } else { 12289493406bSMilanka Ringwald hfp_connection->call_state = HFP_CALL_INCOMING_RINGING; 1229aa4dd815SMatthias Ringwald } 1230aa4dd815SMatthias Ringwald } 1231aa4dd815SMatthias Ringwald 1232fcd59412SMilanka Ringwald static void hfp_ag_hf_start_ringing_outgoing(hfp_connection_t * hfp_connection){ 1233fcd59412SMilanka Ringwald hfp_connection->call_state = HFP_CALL_OUTGOING_RINGING; 1234fcd59412SMilanka Ringwald } 1235fcd59412SMilanka Ringwald 1236a0ffb263SMatthias Ringwald static void hfp_ag_hf_stop_ringing(hfp_connection_t * hfp_connection){ 1237a0ffb263SMatthias Ringwald hfp_connection->ag_ring = 0; 1238a0ffb263SMatthias Ringwald hfp_connection->ag_send_clip = 0; 1239aa4dd815SMatthias Ringwald } 1240aa4dd815SMatthias Ringwald 1241a4f85bd2SMilanka Ringwald 124231f55b91SMilanka Ringwald static void hfp_ag_trigger_incoming_call_during_active_one(void){ 124331f55b91SMilanka Ringwald btstack_linked_list_iterator_t it; 124431f55b91SMilanka Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 124531f55b91SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 124631f55b91SMilanka Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 124731f55b91SMilanka Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 124831f55b91SMilanka Ringwald if (hfp_connection->call_state != HFP_CALL_ACTIVE) continue; 124931f55b91SMilanka Ringwald 125031f55b91SMilanka Ringwald hfp_connection->call_state = HFP_CALL_W2_SEND_CALL_WAITING; 125131f55b91SMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 125231f55b91SMilanka Ringwald } 125331f55b91SMilanka Ringwald } 125431f55b91SMilanka Ringwald 1255d3709f3eSMatthias Ringwald static void hfp_ag_trigger_ring_and_clip(void) { 1256d3709f3eSMatthias Ringwald btstack_linked_list_iterator_t it; 1257d3709f3eSMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1258d3709f3eSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1259d3709f3eSMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 1260d3709f3eSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1261d3709f3eSMatthias Ringwald switch (hfp_connection->call_state){ 1262d3709f3eSMatthias Ringwald case HFP_CALL_INCOMING_RINGING: 1263d3709f3eSMatthias Ringwald case HFP_CALL_OUTGOING_RINGING: 1264d3709f3eSMatthias Ringwald case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING: 12653561686eSMatthias Ringwald // Queue RING on this connection 1266a4f85bd2SMilanka Ringwald hfp_connection->ag_ring = 1; 12673561686eSMatthias Ringwald 12683561686eSMatthias Ringwald // HFP v1.8, 4.23 Calling Line Identification (CLI) Notification 12693561686eSMatthias Ringwald // "If the calling subscriber number information is available from the network, the AG shall issue the 12703561686eSMatthias Ringwald // +CLIP unsolicited result code just after every RING indication when the HF is alerted in an incoming call." 12713561686eSMatthias Ringwald hfp_connection->ag_send_clip = hfp_gsm_clip_type() && hfp_connection->clip_enabled; 12723561686eSMatthias Ringwald 12733561686eSMatthias Ringwald // trigger next message 1274a4f85bd2SMilanka Ringwald rfcomm_request_can_send_now_event(hfp_connection->rfcomm_cid); 1275b7e27351SMilanka Ringwald break; 1276b7e27351SMilanka Ringwald default: 1277b7e27351SMilanka Ringwald break; 1278b7e27351SMilanka Ringwald } 1279a4f85bd2SMilanka Ringwald } 1280a4f85bd2SMilanka Ringwald } 1281a4f85bd2SMilanka Ringwald 128250b9afa4SMilanka Ringwald // trigger RING and CLIP messages on connections that are ringing 128350b9afa4SMilanka Ringwald static void hfp_ag_ring_timeout_handler(btstack_timer_source_t * timer){ 128450b9afa4SMilanka Ringwald hfp_ag_trigger_ring_and_clip(); 128550b9afa4SMilanka Ringwald 12866db648bcSMilanka Ringwald btstack_run_loop_set_timer(timer, HFP_RING_PERIOD_MS); // 2 seconds timeout 128750b9afa4SMilanka Ringwald btstack_run_loop_add_timer(timer); 1288a4f85bd2SMilanka Ringwald } 1289a4f85bd2SMilanka Ringwald 1290a4f85bd2SMilanka Ringwald static void hfp_ag_ring_timeout_stop(void){ 1291a4f85bd2SMilanka Ringwald btstack_run_loop_remove_timer(&hfp_ag_ring_timeout); 1292a4f85bd2SMilanka Ringwald } 1293a4f85bd2SMilanka Ringwald 1294a4f85bd2SMilanka Ringwald static void hfp_ag_start_ringing(void){ 129550b9afa4SMilanka Ringwald // setup ring timer 129650b9afa4SMilanka Ringwald btstack_run_loop_remove_timer(&hfp_ag_ring_timeout); 129750b9afa4SMilanka Ringwald btstack_run_loop_set_timer_handler(&hfp_ag_ring_timeout, hfp_ag_ring_timeout_handler); 12986db648bcSMilanka Ringwald btstack_run_loop_set_timer(&hfp_ag_ring_timeout, HFP_RING_PERIOD_MS); // 2 seconds timeout 129950b9afa4SMilanka Ringwald btstack_run_loop_add_timer(&hfp_ag_ring_timeout); 130050b9afa4SMilanka Ringwald 130150b9afa4SMilanka Ringwald // emit start ringing 1302a4f85bd2SMilanka Ringwald hfp_ag_emit_general_simple_event(HFP_SUBEVENT_START_RINGING); 130350b9afa4SMilanka Ringwald 130450b9afa4SMilanka Ringwald // send initial RING + CLIP 130550b9afa4SMilanka Ringwald hfp_ag_trigger_ring_and_clip(); 1306a4f85bd2SMilanka Ringwald } 1307a4f85bd2SMilanka Ringwald 1308a4f85bd2SMilanka Ringwald static void hfp_ag_stop_ringing(void){ 1309a4f85bd2SMilanka Ringwald hfp_ag_ring_timeout_stop(); 1310a4f85bd2SMilanka Ringwald hfp_ag_emit_general_simple_event(HFP_SUBEVENT_STOP_RINGING); 1311a4f85bd2SMilanka Ringwald 1312a4f85bd2SMilanka Ringwald btstack_linked_list_iterator_t it; 1313a4f85bd2SMilanka Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1314a4f85bd2SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1315a4f85bd2SMilanka Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 1316a4f85bd2SMilanka Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1317a4f85bd2SMilanka Ringwald if ((hfp_connection->call_state != HFP_CALL_INCOMING_RINGING) && 1318a4f85bd2SMilanka Ringwald (hfp_connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING)) continue; 1319a4f85bd2SMilanka Ringwald hfp_ag_hf_stop_ringing(hfp_connection); 1320a4f85bd2SMilanka Ringwald } 1321a4f85bd2SMilanka Ringwald } 1322a4f85bd2SMilanka Ringwald 132331f55b91SMilanka Ringwald static void hfp_ag_trigger_incoming_call_idle(void){ 1324aa4dd815SMatthias Ringwald int indicator_index = get_ag_indicator_index_for_name("callsetup"); 1325aa4dd815SMatthias Ringwald if (indicator_index < 0) return; 1326aa4dd815SMatthias Ringwald 13273561686eSMatthias Ringwald // update callsetup state in connections 1328665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1329665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1330665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1331a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 133266c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 133331f55b91SMilanka Ringwald if (hfp_connection->call_state != HFP_CALL_IDLE) continue; 133431f55b91SMilanka Ringwald 1335a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, indicator_index, 1); 1336a4f85bd2SMilanka Ringwald 1337a4f85bd2SMilanka Ringwald hfp_ag_hf_start_ringing_incoming(hfp_connection); 1338a4f85bd2SMilanka Ringwald 1339f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1340aa4dd815SMatthias Ringwald } 13413561686eSMatthias Ringwald 13423561686eSMatthias Ringwald // start local ringing - started after connection state has been updated 13433561686eSMatthias Ringwald hfp_ag_start_ringing(); 1344aa4dd815SMatthias Ringwald } 1345aa4dd815SMatthias Ringwald 1346fe899794SMatthias Ringwald static void hfp_ag_trigger_outgoing_call(void){ 1347fe899794SMatthias Ringwald btstack_linked_list_iterator_t it; 1348fe899794SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1349fe899794SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1350fe899794SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 1351fe899794SMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1352fe899794SMatthias Ringwald hfp_ag_establish_service_level_connection(hfp_connection->remote_addr); 1353fe899794SMatthias Ringwald if (hfp_connection->call_state == HFP_CALL_IDLE){ 1354fe899794SMatthias Ringwald hfp_connection->call_state = HFP_CALL_OUTGOING_INITIATED; 1355fe899794SMatthias Ringwald } 1356fe899794SMatthias Ringwald } 1357fe899794SMatthias Ringwald } 13585a7033e2SMatthias Ringwald 13595a7033e2SMatthias Ringwald static void hfp_ag_handle_outgoing_call_accepted(hfp_connection_t * hfp_connection){ 13605a7033e2SMatthias Ringwald hfp_connection->call_state = HFP_CALL_OUTGOING_DIALING; 13615a7033e2SMatthias Ringwald 13625a7033e2SMatthias Ringwald // trigger callsetup to be 13635a7033e2SMatthias Ringwald int put_call_on_hold = hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT; 13645a7033e2SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_CALL_ACCEPTED, 0, 0, NULL); 13655a7033e2SMatthias Ringwald 13665a7033e2SMatthias Ringwald int indicator_index ; 13675a7033e2SMatthias Ringwald 13685a7033e2SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 13695a7033e2SMatthias Ringwald indicator_index = get_ag_indicator_index_for_name("callsetup"); 13705a7033e2SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, indicator_index, 1); 13715a7033e2SMatthias Ringwald 13725a7033e2SMatthias Ringwald // put current call on hold if active 13735a7033e2SMatthias Ringwald if (put_call_on_hold){ 13745a7033e2SMatthias Ringwald log_info("AG putting current call on hold for new outgoing call"); 13755a7033e2SMatthias Ringwald hfp_ag_set_callheld_indicator(); 13765a7033e2SMatthias Ringwald indicator_index = get_ag_indicator_index_for_name("callheld"); 13775a7033e2SMatthias Ringwald hfp_ag_send_transfer_ag_indicators_status_cmd(hfp_connection->rfcomm_cid, &hfp_ag_indicators[indicator_index]); 13785a7033e2SMatthias Ringwald } 13795a7033e2SMatthias Ringwald 13805a7033e2SMatthias Ringwald // start audio if needed 13815a7033e2SMatthias Ringwald hfp_ag_establish_audio_connection(hfp_connection->acl_handle); 13825a7033e2SMatthias Ringwald } 1383fe899794SMatthias Ringwald 13844a2e058fSMatthias Ringwald static void hfp_ag_transfer_indicator(const char * name){ 13854a2e058fSMatthias Ringwald int indicator_index = get_ag_indicator_index_for_name(name); 1386aa4dd815SMatthias Ringwald if (indicator_index < 0) return; 1387aa4dd815SMatthias Ringwald 1388665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1389665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1390665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1391a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 139266c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1393a0ffb263SMatthias Ringwald hfp_ag_establish_service_level_connection(hfp_connection->remote_addr); 1394a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, indicator_index, 1); 1395f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1396aa4dd815SMatthias Ringwald } 1397aa4dd815SMatthias Ringwald } 1398aa4dd815SMatthias Ringwald 13994a2e058fSMatthias Ringwald static void hfp_ag_transfer_callsetup_state(void){ 14004a2e058fSMatthias Ringwald hfp_ag_transfer_indicator("callsetup"); 14014a2e058fSMatthias Ringwald } 14024a2e058fSMatthias Ringwald 1403aa4dd815SMatthias Ringwald static void hfp_ag_transfer_call_state(void){ 14044a2e058fSMatthias Ringwald hfp_ag_transfer_indicator("call"); 1405aa4dd815SMatthias Ringwald } 1406aa4dd815SMatthias Ringwald 1407aa4dd815SMatthias Ringwald static void hfp_ag_transfer_callheld_state(void){ 14084a2e058fSMatthias Ringwald hfp_ag_transfer_indicator("callheld"); 1409aa4dd815SMatthias Ringwald } 1410aa4dd815SMatthias Ringwald 1411aa4dd815SMatthias Ringwald static void hfp_ag_hf_accept_call(hfp_connection_t * source){ 1412aa4dd815SMatthias Ringwald int call_indicator_index = get_ag_indicator_index_for_name("call"); 1413aa4dd815SMatthias Ringwald int callsetup_indicator_index = get_ag_indicator_index_for_name("callsetup"); 1414aa4dd815SMatthias Ringwald 1415a4f85bd2SMilanka Ringwald hfp_ag_stop_ringing(); 1416a4f85bd2SMilanka Ringwald 1417665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1418665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1419665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1420a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 142166c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 14229493406bSMilanka Ringwald if ((hfp_connection->call_state != HFP_CALL_INCOMING_RINGING) && 1423505f1c30SMatthias Ringwald (hfp_connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING)) continue; 1424aa4dd815SMatthias Ringwald 1425a0ffb263SMatthias Ringwald if (hfp_connection == source){ 1426aa4dd815SMatthias Ringwald 1427f197e761SMatthias Ringwald if (use_in_band_tone(hfp_connection)){ 1428a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 1429aa4dd815SMatthias Ringwald } else { 1430a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE; 1431d97d752dSMilanka Ringwald hfp_ag_establish_audio_connection(hfp_connection->acl_handle); 1432aa4dd815SMatthias Ringwald } 1433aa4dd815SMatthias Ringwald 1434a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); 1435a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); 1436aa4dd815SMatthias Ringwald 1437aa4dd815SMatthias Ringwald } else { 1438a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_IDLE; 1439aa4dd815SMatthias Ringwald } 1440f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1441aa4dd815SMatthias Ringwald } 1442aa4dd815SMatthias Ringwald } 1443aa4dd815SMatthias Ringwald 1444aa4dd815SMatthias Ringwald static void hfp_ag_ag_accept_call(void){ 1445aa4dd815SMatthias Ringwald int call_indicator_index = get_ag_indicator_index_for_name("call"); 1446aa4dd815SMatthias Ringwald int callsetup_indicator_index = get_ag_indicator_index_for_name("callsetup"); 1447aa4dd815SMatthias Ringwald 1448a4f85bd2SMilanka Ringwald hfp_ag_stop_ringing(); 1449a4f85bd2SMilanka Ringwald 1450665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1451665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1452665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1453a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 145466c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1455a5764cd2SMatthias Ringwald if ((hfp_connection->call_state != HFP_CALL_INCOMING_RINGING) && 1456a5764cd2SMatthias Ringwald (hfp_connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING)) continue; 1457aa4dd815SMatthias Ringwald 1458a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_TRIGGER_AUDIO_CONNECTION; 1459aa4dd815SMatthias Ringwald 1460a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); 1461a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); 1462aa4dd815SMatthias Ringwald 1463f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1464aa4dd815SMatthias Ringwald break; // only single 1465aa4dd815SMatthias Ringwald } 1466aa4dd815SMatthias Ringwald } 1467aa4dd815SMatthias Ringwald 1468aa4dd815SMatthias Ringwald static void hfp_ag_trigger_reject_call(void){ 1469aa4dd815SMatthias Ringwald int callsetup_indicator_index = get_ag_indicator_index_for_name("callsetup"); 1470a4f85bd2SMilanka Ringwald 1471a4f85bd2SMilanka Ringwald hfp_ag_stop_ringing(); 1472a4f85bd2SMilanka Ringwald 1473665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1474665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1475665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1476665d90f2SMatthias Ringwald hfp_connection_t * connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 147766c5995fSMatthias Ringwald if (connection->local_role != HFP_ROLE_AG) continue; 147807a44c5eSMilanka Ringwald 147907a44c5eSMilanka Ringwald switch (connection->call_state){ 148007a44c5eSMilanka Ringwald case HFP_CALL_INCOMING_RINGING: 148107a44c5eSMilanka Ringwald case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING: 148207a44c5eSMilanka Ringwald case HFP_CALL_OUTGOING_RINGING: 148307a44c5eSMilanka Ringwald case HFP_CALL_OUTGOING_INITIATED: 148407a44c5eSMilanka Ringwald case HFP_CALL_OUTGOING_DIALING: 148507a44c5eSMilanka Ringwald break; 148607a44c5eSMilanka Ringwald default: 148707a44c5eSMilanka Ringwald continue; 148807a44c5eSMilanka Ringwald } 1489095a9f2bSMilanka Ringwald 1490aa4dd815SMatthias Ringwald connection->call_state = HFP_CALL_IDLE; 1491095a9f2bSMilanka Ringwald connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); 1492095a9f2bSMilanka Ringwald 1493095a9f2bSMilanka Ringwald if (connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED){ 1494095a9f2bSMilanka Ringwald connection->release_audio_connection = 1; 1495095a9f2bSMilanka Ringwald } 1496095a9f2bSMilanka Ringwald hfp_emit_simple_event(connection, HFP_SUBEVENT_CALL_TERMINATED); 1497f0aeb307SMatthias Ringwald hfp_ag_run_for_context(connection); 1498095a9f2bSMilanka Ringwald rfcomm_request_can_send_now_event(connection->rfcomm_cid); 1499aa4dd815SMatthias Ringwald } 1500aa4dd815SMatthias Ringwald } 1501aa4dd815SMatthias Ringwald 1502aa4dd815SMatthias Ringwald static void hfp_ag_trigger_terminate_call(void){ 1503aa4dd815SMatthias Ringwald int call_indicator_index = get_ag_indicator_index_for_name("call"); 1504aa4dd815SMatthias Ringwald 1505a4f85bd2SMilanka Ringwald // no ringing during call 1506a4f85bd2SMilanka Ringwald 1507665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1508665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1509665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1510a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 151166c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1512a0ffb263SMatthias Ringwald if (hfp_connection->call_state == HFP_CALL_IDLE) continue; 1513fb75b483SMilanka Ringwald 1514a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_IDLE; 1515a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); 1516095a9f2bSMilanka Ringwald 15176d78145cSMatthias Ringwald if (hfp_connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED){ 1518a0ffb263SMatthias Ringwald hfp_connection->release_audio_connection = 1; 15196d78145cSMatthias Ringwald } 1520095a9f2bSMilanka Ringwald 1521fb75b483SMilanka Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_CALL_TERMINATED); 1522f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1523a13ff7d1SMilanka Ringwald rfcomm_request_can_send_now_event(hfp_connection->rfcomm_cid); 1524aa4dd815SMatthias Ringwald } 1525aa4dd815SMatthias Ringwald } 1526aa4dd815SMatthias Ringwald 15270cb5b971SMatthias Ringwald static void hfp_ag_set_call_indicator(void){ 1528aa4dd815SMatthias Ringwald hfp_ag_indicator_t * indicator = get_ag_indicator_for_name("call"); 1529aa4dd815SMatthias Ringwald if (!indicator){ 1530aa4dd815SMatthias Ringwald log_error("hfp_ag_set_call_state: call indicator is missing"); 153145718b6fSMatthias Ringwald return; 1532aa4dd815SMatthias Ringwald }; 1533d210d9c4SMatthias Ringwald indicator->status = hfp_gsm_call_status(); 1534aa4dd815SMatthias Ringwald } 1535aa4dd815SMatthias Ringwald 1536aa4dd815SMatthias Ringwald static hfp_connection_t * hfp_ag_connection_for_call_state(hfp_call_state_t call_state){ 1537665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1538665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1539665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1540a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 154166c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1542a0ffb263SMatthias Ringwald if (hfp_connection->call_state == call_state) return hfp_connection; 1543aa4dd815SMatthias Ringwald } 1544aa4dd815SMatthias Ringwald return NULL; 1545aa4dd815SMatthias Ringwald } 1546aa4dd815SMatthias Ringwald 1547a5bdcda8SMatthias Ringwald static void hfp_ag_send_response_and_hold_state(hfp_response_and_hold_state_t state){ 1548665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 1549665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 1550665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1551a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 155266c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 1553a0ffb263SMatthias Ringwald hfp_connection->send_response_and_hold_status = state + 1; 1554ce263fc8SMatthias Ringwald } 1555ce263fc8SMatthias Ringwald } 1556ce263fc8SMatthias Ringwald 1557a0ffb263SMatthias Ringwald static int call_setup_state_machine(hfp_connection_t * hfp_connection){ 1558aa4dd815SMatthias Ringwald int indicator_index; 1559a0ffb263SMatthias Ringwald switch (hfp_connection->call_state){ 1560aa4dd815SMatthias Ringwald case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING: 1561a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; 1562a0ffb263SMatthias Ringwald // we got event: audio hfp_connection established 15639493406bSMilanka Ringwald hfp_connection->call_state = HFP_CALL_INCOMING_RINGING; 1564aa4dd815SMatthias Ringwald break; 1565aa4dd815SMatthias Ringwald case HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE: 1566a0ffb263SMatthias Ringwald if (hfp_connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; 1567a0ffb263SMatthias Ringwald // we got event: audio hfp_connection established 1568a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 1569aa4dd815SMatthias Ringwald break; 1570aa4dd815SMatthias Ringwald case HFP_CALL_W2_SEND_CALL_WAITING: 1571a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_W4_CHLD; 1572a0ffb263SMatthias Ringwald hfp_ag_send_call_waiting_notification(hfp_connection->rfcomm_cid); 1573aa4dd815SMatthias Ringwald indicator_index = get_ag_indicator_index_for_name("callsetup"); 1574a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, indicator_index, 1); 1575aa4dd815SMatthias Ringwald break; 1576aa4dd815SMatthias Ringwald default: 1577aa4dd815SMatthias Ringwald break; 1578aa4dd815SMatthias Ringwald } 1579aa4dd815SMatthias Ringwald return 0; 1580aa4dd815SMatthias Ringwald } 158139426febSMatthias Ringwald 1582e84fa067SMatthias Ringwald // queue 'error' to HF 1583e84fa067SMatthias Ringwald static void hfp_ag_queue_error(hfp_connection_t * hfp_connection){ 15845e87d1ceSMatthias Ringwald hfp_connection->send_error++; 1585e84fa067SMatthias Ringwald } 1586e84fa067SMatthias Ringwald 1587e84fa067SMatthias Ringwald // queue 'ok' to HF 1588e84fa067SMatthias Ringwald static void hfp_ag_queue_ok(hfp_connection_t * hfp_connection){ 15895e87d1ceSMatthias Ringwald hfp_connection->ok_pending++; 1590e84fa067SMatthias Ringwald } 1591e84fa067SMatthias Ringwald 159239426febSMatthias Ringwald // functions extracted from hfp_ag_call_sm below 15939f9621cfSMatthias Ringwald static void hfp_ag_handle_reject_outgoing_call(void){ 159439426febSMatthias Ringwald hfp_connection_t * hfp_connection = hfp_ag_connection_for_call_state(HFP_CALL_OUTGOING_INITIATED); 159539426febSMatthias Ringwald if (!hfp_connection){ 159639426febSMatthias Ringwald log_info("hfp_ag_call_sm: did not find outgoing hfp_connection in initiated state"); 159739426febSMatthias Ringwald return; 159839426febSMatthias Ringwald } 159939426febSMatthias Ringwald 1600f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_CALL_REJECTED, 0, 0, NULL); 160139426febSMatthias Ringwald hfp_connection->call_state = HFP_CALL_IDLE; 1602e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 160339426febSMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 160439426febSMatthias Ringwald } 160539426febSMatthias Ringwald 1606a0ffb263SMatthias Ringwald // hfp_connection is used to identify originating HF 1607a0ffb263SMatthias Ringwald static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * hfp_connection){ 160881e25d0aSMatthias Ringwald 160981e25d0aSMatthias Ringwald // allow to intercept call statemachine events 161081e25d0aSMatthias Ringwald if (hfp_ag_custom_call_sm_handler != NULL){ 161181e25d0aSMatthias Ringwald bool handle_event = (*hfp_ag_custom_call_sm_handler)(event); 161281e25d0aSMatthias Ringwald if (!handle_event) return; 161381e25d0aSMatthias Ringwald } 161481e25d0aSMatthias Ringwald 1615d210d9c4SMatthias Ringwald int callsetup_indicator_index = get_ag_indicator_index_for_name("callsetup"); 1616d210d9c4SMatthias Ringwald int callheld_indicator_index = get_ag_indicator_index_for_name("callheld"); 1617d210d9c4SMatthias Ringwald int call_indicator_index = get_ag_indicator_index_for_name("call"); 161874386ee0SMatthias Ringwald 1619aa4dd815SMatthias Ringwald switch (event){ 1620aa4dd815SMatthias Ringwald case HFP_AG_INCOMING_CALL: 1621d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1622aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1623d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 162412cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS: 1625f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_INCOMING_CALL, 0, 0, NULL); 1626d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 162731f55b91SMilanka Ringwald hfp_ag_trigger_incoming_call_idle(); 162860ebb071SMilanka Ringwald log_info("AG rings"); 1629aa4dd815SMatthias Ringwald break; 1630aa4dd815SMatthias Ringwald default: 16313deb3ec6SMatthias Ringwald break; 16323deb3ec6SMatthias Ringwald } 16333deb3ec6SMatthias Ringwald break; 1634aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 1635d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 163612cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS: 1637f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_INCOMING_CALL, 0, 0, NULL); 1638d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 163931f55b91SMilanka Ringwald hfp_ag_trigger_incoming_call_during_active_one(); 164060ebb071SMilanka Ringwald log_info("AG call waiting"); 1641aa4dd815SMatthias Ringwald break; 1642aa4dd815SMatthias Ringwald default: 16433deb3ec6SMatthias Ringwald break; 16443deb3ec6SMatthias Ringwald } 16453deb3ec6SMatthias Ringwald break; 16467bbeb3adSMilanka Ringwald default: 16477bbeb3adSMilanka Ringwald break; 1648aa4dd815SMatthias Ringwald } 1649aa4dd815SMatthias Ringwald break; 1650aa4dd815SMatthias Ringwald case HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG: 1651d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1652aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1653d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 165412cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 1655f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG, 0, 0, NULL); 1656d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1657d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1658aa4dd815SMatthias Ringwald hfp_ag_ag_accept_call(); 165960ebb071SMilanka Ringwald log_info("AG answers call, accept call by GSM"); 1660aa4dd815SMatthias Ringwald break; 1661aa4dd815SMatthias Ringwald default: 16623deb3ec6SMatthias Ringwald break; 16633deb3ec6SMatthias Ringwald } 1664aa4dd815SMatthias Ringwald break; 1665aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 1666d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 1667aa4dd815SMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 166860ebb071SMilanka Ringwald log_info("AG: current call is placed on hold, incoming call gets active"); 1669f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG, 0, 0, NULL); 1670d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1671d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 1672ce263fc8SMatthias Ringwald hfp_ag_transfer_callsetup_state(); 1673ce263fc8SMatthias Ringwald hfp_ag_transfer_callheld_state(); 1674aa4dd815SMatthias Ringwald break; 1675aa4dd815SMatthias Ringwald default: 1676aa4dd815SMatthias Ringwald break; 1677aa4dd815SMatthias Ringwald } 1678aa4dd815SMatthias Ringwald break; 16797bbeb3adSMilanka Ringwald default: 16807bbeb3adSMilanka Ringwald break; 1681aa4dd815SMatthias Ringwald } 1682aa4dd815SMatthias Ringwald break; 1683ce263fc8SMatthias Ringwald 1684ce263fc8SMatthias Ringwald case HFP_AG_HELD_CALL_JOINED_BY_AG: 1685d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1686ce263fc8SMatthias Ringwald case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 1687d0c20769SMatthias Ringwald switch (hfp_gsm_callheld_status()){ 1688ce263fc8SMatthias Ringwald case HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED: 168960ebb071SMilanka Ringwald log_info("AG: joining held call with active call"); 1690f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_HELD_CALL_JOINED_BY_AG, 0, 0, NULL); 1691d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 1692ce263fc8SMatthias Ringwald hfp_ag_transfer_callheld_state(); 1693ca59be51SMatthias Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_CONFERENCE_CALL); 1694ce263fc8SMatthias Ringwald break; 1695ce263fc8SMatthias Ringwald default: 1696ce263fc8SMatthias Ringwald break; 1697ce263fc8SMatthias Ringwald } 1698ce263fc8SMatthias Ringwald break; 1699ce263fc8SMatthias Ringwald default: 1700ce263fc8SMatthias Ringwald break; 1701ce263fc8SMatthias Ringwald } 1702ce263fc8SMatthias Ringwald break; 1703ce263fc8SMatthias Ringwald 1704aa4dd815SMatthias Ringwald case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF: 1705d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1706aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1707d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 170812cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 1709f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF, 0, 0, NULL); 1710d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1711d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1712a0ffb263SMatthias Ringwald hfp_ag_hf_accept_call(hfp_connection); 1713e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 171460ebb071SMilanka Ringwald log_info("HF answers call, accept call by GSM"); 1715ca59be51SMatthias Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_CALL_ANSWERED); 1716aa4dd815SMatthias Ringwald break; 1717aa4dd815SMatthias Ringwald default: 1718aa4dd815SMatthias Ringwald break; 1719aa4dd815SMatthias Ringwald } 17203deb3ec6SMatthias Ringwald break; 17213deb3ec6SMatthias Ringwald default: 17223deb3ec6SMatthias Ringwald break; 17233deb3ec6SMatthias Ringwald } 17243deb3ec6SMatthias Ringwald break; 17253deb3ec6SMatthias Ringwald 1726ce263fc8SMatthias Ringwald case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG: 1727d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1728ce263fc8SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1729d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 173012cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 1731f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG, 0, 0, NULL); 173227bb1817SMatthias Ringwald hfp_ag_response_and_hold_active = true; 1733ce263fc8SMatthias Ringwald hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD; 1734a5bdcda8SMatthias Ringwald hfp_ag_send_response_and_hold_state(hfp_ag_response_and_hold_state); 1735ac6f828eSMilanka Ringwald // as with regular call 1736d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1737d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1738ce263fc8SMatthias Ringwald hfp_ag_ag_accept_call(); 173960ebb071SMilanka Ringwald log_info("AG response and hold - hold by AG"); 1740ce263fc8SMatthias Ringwald break; 1741ce263fc8SMatthias Ringwald default: 1742ce263fc8SMatthias Ringwald break; 1743ce263fc8SMatthias Ringwald } 1744ce263fc8SMatthias Ringwald break; 1745ce263fc8SMatthias Ringwald default: 1746ce263fc8SMatthias Ringwald break; 1747ce263fc8SMatthias Ringwald } 1748ce263fc8SMatthias Ringwald break; 1749ce263fc8SMatthias Ringwald 1750ce263fc8SMatthias Ringwald case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF: 1751d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1752ce263fc8SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1753d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 175412cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 1755f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF, 0, 0, NULL); 175627bb1817SMatthias Ringwald hfp_ag_response_and_hold_active = true; 1757ce263fc8SMatthias Ringwald hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD; 1758a5bdcda8SMatthias Ringwald hfp_ag_send_response_and_hold_state(hfp_ag_response_and_hold_state); 1759ac6f828eSMilanka Ringwald // as with regular call 1760d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1761d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1762a0ffb263SMatthias Ringwald hfp_ag_hf_accept_call(hfp_connection); 176360ebb071SMilanka Ringwald log_info("AG response and hold - hold by HF"); 1764ce263fc8SMatthias Ringwald break; 1765ce263fc8SMatthias Ringwald default: 1766ce263fc8SMatthias Ringwald break; 1767ce263fc8SMatthias Ringwald } 1768ce263fc8SMatthias Ringwald break; 1769ce263fc8SMatthias Ringwald default: 1770ce263fc8SMatthias Ringwald break; 1771ce263fc8SMatthias Ringwald } 1772ce263fc8SMatthias Ringwald break; 1773ce263fc8SMatthias Ringwald 1774ce263fc8SMatthias Ringwald case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG: 1775ce263fc8SMatthias Ringwald case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF: 1776ce263fc8SMatthias Ringwald if (!hfp_ag_response_and_hold_active) break; 1777ce263fc8SMatthias Ringwald if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; 1778f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG, 0, 0, NULL); 177927bb1817SMatthias Ringwald hfp_ag_response_and_hold_active = false; 1780ce263fc8SMatthias Ringwald hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED; 1781a5bdcda8SMatthias Ringwald hfp_ag_send_response_and_hold_state(hfp_ag_response_and_hold_state); 178260ebb071SMilanka Ringwald log_info("Held Call accepted and active"); 1783ce263fc8SMatthias Ringwald break; 1784ce263fc8SMatthias Ringwald 1785ce263fc8SMatthias Ringwald case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG: 1786ce263fc8SMatthias Ringwald case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF: 1787ce263fc8SMatthias Ringwald if (!hfp_ag_response_and_hold_active) break; 1788ce263fc8SMatthias Ringwald if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break; 1789f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG, 0, 0, NULL); 179027bb1817SMatthias Ringwald hfp_ag_response_and_hold_active = false; 1791ce263fc8SMatthias Ringwald hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED; 1792a5bdcda8SMatthias Ringwald hfp_ag_send_response_and_hold_state(hfp_ag_response_and_hold_state); 1793ce263fc8SMatthias Ringwald // from terminate by ag 1794d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1795ce263fc8SMatthias Ringwald hfp_ag_trigger_terminate_call(); 1796ce263fc8SMatthias Ringwald break; 1797ce263fc8SMatthias Ringwald 1798aa4dd815SMatthias Ringwald case HFP_AG_TERMINATE_CALL_BY_HF: 1799d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1800aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1801d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 180212cbbeeeSMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 180307a44c5eSMilanka Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE: 180407a44c5eSMilanka Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE: 1805f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_TERMINATE_CALL_BY_HF, 0, 0, NULL); 1806d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1807aa4dd815SMatthias Ringwald hfp_ag_transfer_callsetup_state(); 1808aa4dd815SMatthias Ringwald hfp_ag_trigger_reject_call(); 180960ebb071SMilanka Ringwald log_info("HF Rejected Incoming call, AG terminate call"); 1810aa4dd815SMatthias Ringwald break; 1811aa4dd815SMatthias Ringwald default: 1812aa4dd815SMatthias Ringwald break; 1813aa4dd815SMatthias Ringwald } 1814aa4dd815SMatthias Ringwald break; 181512cbbeeeSMatthias Ringwald case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 1816f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_TERMINATE_CALL_BY_HF, 0, 0, NULL); 18171ec112deSMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1818d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 18191ec112deSMatthias Ringwald hfp_ag_trigger_terminate_call(); 182060ebb071SMilanka Ringwald log_info("AG terminate call"); 1821aa4dd815SMatthias Ringwald break; 18227bbeb3adSMilanka Ringwald default: 18237bbeb3adSMilanka Ringwald break; 1824aa4dd815SMatthias Ringwald } 1825aa4dd815SMatthias Ringwald break; 18263deb3ec6SMatthias Ringwald 1827aa4dd815SMatthias Ringwald case HFP_AG_TERMINATE_CALL_BY_AG: 1828d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1829aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1830d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 183107a44c5eSMilanka Ringwald case HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS: 183207a44c5eSMilanka Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE: 183307a44c5eSMilanka Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE: 1834f2aa99a9SMilanka Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 1835f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_TERMINATE_CALL_BY_AG, 0, 0, NULL); 1836d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1837aa4dd815SMatthias Ringwald hfp_ag_trigger_reject_call(); 183860ebb071SMilanka Ringwald log_info("AG Rejected Incoming call, AG terminate call"); 1839aa4dd815SMatthias Ringwald break; 1840aa4dd815SMatthias Ringwald default: 1841aa4dd815SMatthias Ringwald break; 18423deb3ec6SMatthias Ringwald } 1843202c8a4cSMatthias Ringwald break; 184412cbbeeeSMatthias Ringwald case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 1845f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_TERMINATE_CALL_BY_AG, 0, 0, NULL); 1846d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1847d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1848aa4dd815SMatthias Ringwald hfp_ag_trigger_terminate_call(); 184960ebb071SMilanka Ringwald log_info("AG terminate call"); 1850aa4dd815SMatthias Ringwald break; 1851aa4dd815SMatthias Ringwald default: 18523deb3ec6SMatthias Ringwald break; 18533deb3ec6SMatthias Ringwald } 18543deb3ec6SMatthias Ringwald break; 1855aa4dd815SMatthias Ringwald case HFP_AG_CALL_DROPPED: 1856d0c20769SMatthias Ringwald switch (hfp_gsm_call_status()){ 1857aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1858d0c20769SMatthias Ringwald switch (hfp_gsm_callsetup_status()){ 1859aa4dd815SMatthias Ringwald case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS: 1860aa4dd815SMatthias Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE: 1861fb75b483SMilanka Ringwald hfp_ag_stop_ringing(); 1862fb75b483SMilanka Ringwald break; 1863fb75b483SMilanka Ringwald case HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE: 1864fb75b483SMilanka Ringwald log_info("Outgoing call interrupted\n"); 1865aa4dd815SMatthias Ringwald break; 1866aa4dd815SMatthias Ringwald default: 18673deb3ec6SMatthias Ringwald break; 18683deb3ec6SMatthias Ringwald } 1869f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_DROPPED, 0, 0, NULL); 1870d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1871aa4dd815SMatthias Ringwald hfp_ag_transfer_callsetup_state(); 187210c236bfSMatthias Ringwald hfp_ag_trigger_terminate_call(); 1873aa4dd815SMatthias Ringwald break; 1874aa4dd815SMatthias Ringwald case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: 1875ce263fc8SMatthias Ringwald if (hfp_ag_response_and_hold_active) { 1876f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_DROPPED, 0, 0, NULL); 1877ce263fc8SMatthias Ringwald hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED; 1878a5bdcda8SMatthias Ringwald hfp_ag_send_response_and_hold_state(hfp_ag_response_and_hold_state); 1879ce263fc8SMatthias Ringwald } 1880f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_DROPPED, 0, 0, NULL); 1881d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1882d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 1883aa4dd815SMatthias Ringwald hfp_ag_trigger_terminate_call(); 188460ebb071SMilanka Ringwald log_info("AG notify call dropped"); 1885aa4dd815SMatthias Ringwald break; 1886aa4dd815SMatthias Ringwald default: 1887aa4dd815SMatthias Ringwald break; 1888aa4dd815SMatthias Ringwald } 1889aa4dd815SMatthias Ringwald break; 1890aa4dd815SMatthias Ringwald 18919ff73f41SMatthias Ringwald case HFP_AG_OUTGOING_CALL_INITIATED_BY_HF: 1892d210d9c4SMatthias Ringwald // directly reject call if number of free slots is exceeded 1893d210d9c4SMatthias Ringwald if (!hfp_gsm_call_possible()){ 1894e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 1895f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1896d210d9c4SMatthias Ringwald break; 1897d210d9c4SMatthias Ringwald } 1898fe899794SMatthias Ringwald 1899fe899794SMatthias Ringwald // note: number not used currently 19009ff73f41SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_CALL_INITIATED_BY_HF, 0, 0, (const char *) &hfp_connection->line_buffer[3]); 19019cae807eSMatthias Ringwald 1902a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_OUTGOING_INITIATED; 190374386ee0SMatthias Ringwald 1904ca59be51SMatthias Ringwald hfp_emit_string_event(hfp_connection, HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER, (const char *) &hfp_connection->line_buffer[3]); 1905aa4dd815SMatthias Ringwald break; 1906aa4dd815SMatthias Ringwald 1907fe899794SMatthias Ringwald case HFP_AG_OUTGOING_CALL_INITIATED_BY_AG: 1908fe899794SMatthias Ringwald // directly reject call if number of free slots is exceeded 1909fe899794SMatthias Ringwald if (!hfp_gsm_call_possible()) { 1910fe899794SMatthias Ringwald // TODO: no error for user 1911fe899794SMatthias Ringwald break; 1912fe899794SMatthias Ringwald } 1913fe899794SMatthias Ringwald switch (hfp_gsm_call_status()) { 1914fe899794SMatthias Ringwald case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: 1915fe899794SMatthias Ringwald switch (hfp_gsm_callsetup_status()) { 1916fe899794SMatthias Ringwald case HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS: 1917fe899794SMatthias Ringwald // note: number not used currently 1918fe899794SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_CALL_INITIATED_BY_AG, 0, 0, "1234567"); 1919fe899794SMatthias Ringwald // init call state for all connections 1920fe899794SMatthias Ringwald hfp_ag_trigger_outgoing_call(); 1921fe899794SMatthias Ringwald // get first one and accept call 1922fe899794SMatthias Ringwald hfp_connection = hfp_ag_connection_for_call_state(HFP_CALL_OUTGOING_INITIATED); 1923fe899794SMatthias Ringwald if (hfp_connection) { 1924fe899794SMatthias Ringwald hfp_ag_handle_outgoing_call_accepted(hfp_connection); 1925fe899794SMatthias Ringwald } 1926fe899794SMatthias Ringwald break; 1927fe899794SMatthias Ringwald default: 1928fe899794SMatthias Ringwald // TODO: no error for user 1929fe899794SMatthias Ringwald break; 1930fe899794SMatthias Ringwald } 1931fe899794SMatthias Ringwald break; 1932fe899794SMatthias Ringwald default: 1933fe899794SMatthias Ringwald // TODO: no error for user 1934fe899794SMatthias Ringwald break; 1935fe899794SMatthias Ringwald } 1936fe899794SMatthias Ringwald break; 19379cae807eSMatthias Ringwald case HFP_AG_OUTGOING_REDIAL_INITIATED:{ 1938d210d9c4SMatthias Ringwald // directly reject call if number of free slots is exceeded 1939d210d9c4SMatthias Ringwald if (!hfp_gsm_call_possible()){ 1940e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 1941f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 1942d210d9c4SMatthias Ringwald break; 1943d210d9c4SMatthias Ringwald } 1944d210d9c4SMatthias Ringwald 1945f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_REDIAL_INITIATED, 0, 0, NULL); 1946a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_OUTGOING_INITIATED; 194774386ee0SMatthias Ringwald 194860ebb071SMilanka Ringwald log_info("Redial last number"); 19499cae807eSMatthias Ringwald char * last_dialed_number = hfp_gsm_last_dialed_number(); 1950aa4dd815SMatthias Ringwald 19519cae807eSMatthias Ringwald if (strlen(last_dialed_number) > 0){ 195260ebb071SMilanka Ringwald log_info("Last number exists: accept call"); 1953ca59be51SMatthias Ringwald hfp_emit_string_event(hfp_connection, HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER, last_dialed_number); 19549cae807eSMatthias Ringwald } else { 195560ebb071SMilanka Ringwald log_info("log_infoLast number missing: reject call"); 19569f9621cfSMatthias Ringwald hfp_ag_handle_reject_outgoing_call(); 19579cae807eSMatthias Ringwald } 19589cae807eSMatthias Ringwald break; 19599cae807eSMatthias Ringwald } 1960aa4dd815SMatthias Ringwald case HFP_AG_OUTGOING_CALL_REJECTED: 19619f9621cfSMatthias Ringwald hfp_ag_handle_reject_outgoing_call(); 1962aa4dd815SMatthias Ringwald break; 1963aa4dd815SMatthias Ringwald 1964d210d9c4SMatthias Ringwald case HFP_AG_OUTGOING_CALL_ACCEPTED:{ 1965a0ffb263SMatthias Ringwald hfp_connection = hfp_ag_connection_for_call_state(HFP_CALL_OUTGOING_INITIATED); 1966a0ffb263SMatthias Ringwald if (!hfp_connection){ 1967a0ffb263SMatthias Ringwald log_info("hfp_ag_call_sm: did not find outgoing hfp_connection in initiated state"); 1968aa4dd815SMatthias Ringwald break; 1969aa4dd815SMatthias Ringwald } 1970aa4dd815SMatthias Ringwald 1971e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 19725a7033e2SMatthias Ringwald hfp_ag_handle_outgoing_call_accepted(hfp_connection); 1973aa4dd815SMatthias Ringwald break; 1974d210d9c4SMatthias Ringwald } 1975aa4dd815SMatthias Ringwald case HFP_AG_OUTGOING_CALL_RINGING: 1976a0ffb263SMatthias Ringwald hfp_connection = hfp_ag_connection_for_call_state(HFP_CALL_OUTGOING_DIALING); 1977a0ffb263SMatthias Ringwald if (!hfp_connection){ 1978a0ffb263SMatthias Ringwald log_info("hfp_ag_call_sm: did not find outgoing hfp_connection in dialing state"); 1979aa4dd815SMatthias Ringwald break; 1980aa4dd815SMatthias Ringwald } 1981d210d9c4SMatthias Ringwald 1982f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_CALL_RINGING, 0, 0, NULL); 1983a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_OUTGOING_RINGING; 1984d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 1985aa4dd815SMatthias Ringwald hfp_ag_transfer_callsetup_state(); 1986fcd59412SMilanka Ringwald hfp_ag_hf_start_ringing_outgoing(hfp_connection); 1987aa4dd815SMatthias Ringwald break; 1988aa4dd815SMatthias Ringwald 1989d210d9c4SMatthias Ringwald case HFP_AG_OUTGOING_CALL_ESTABLISHED:{ 1990aa4dd815SMatthias Ringwald // get outgoing call 1991a0ffb263SMatthias Ringwald hfp_connection = hfp_ag_connection_for_call_state(HFP_CALL_OUTGOING_RINGING); 1992a0ffb263SMatthias Ringwald if (!hfp_connection){ 1993a0ffb263SMatthias Ringwald hfp_connection = hfp_ag_connection_for_call_state(HFP_CALL_OUTGOING_DIALING); 1994aa4dd815SMatthias Ringwald } 1995a0ffb263SMatthias Ringwald if (!hfp_connection){ 1996a0ffb263SMatthias Ringwald log_info("hfp_ag_call_sm: did not find outgoing hfp_connection"); 1997aa4dd815SMatthias Ringwald break; 1998aa4dd815SMatthias Ringwald } 1999d210d9c4SMatthias Ringwald 2000addc49c4SMilanka Ringwald bool callheld_status_call_on_hold_and_no_active_calls = hfp_gsm_callheld_status() == HFP_CALLHELD_STATUS_CALL_ON_HOLD_AND_NO_ACTIVE_CALLS; 2001f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_OUTGOING_CALL_ESTABLISHED, 0, 0, NULL); 2002a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 2003d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 2004d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 2005aa4dd815SMatthias Ringwald hfp_ag_transfer_call_state(); 2006aa4dd815SMatthias Ringwald hfp_ag_transfer_callsetup_state(); 2007addc49c4SMilanka Ringwald if (callheld_status_call_on_hold_and_no_active_calls){ 2008d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 2009aa4dd815SMatthias Ringwald hfp_ag_transfer_callheld_state(); 20103deb3ec6SMatthias Ringwald } 2011addc49c4SMilanka Ringwald hfp_ag_hf_stop_ringing(hfp_connection); 2012674ebed5SMilanka Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_CALL_ANSWERED); 20133deb3ec6SMatthias Ringwald break; 2014d210d9c4SMatthias Ringwald } 2015d210d9c4SMatthias Ringwald 201612cbbeeeSMatthias Ringwald case HFP_AG_CALL_HOLD_USER_BUSY: 2017f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_HOLD_USER_BUSY, 0, 0, NULL); 2018d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 2019a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); 2020a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 202160ebb071SMilanka Ringwald log_info("AG: Call Waiting, User Busy"); 2022d210d9c4SMatthias Ringwald break; 2023d210d9c4SMatthias Ringwald 2024d210d9c4SMatthias Ringwald case HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:{ 2025d0c20769SMatthias Ringwald int call_setup_in_progress = hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; 2026d0c20769SMatthias Ringwald int call_held = hfp_gsm_callheld_status() != HFP_CALLHELD_STATUS_NO_CALLS_HELD; 2027d210d9c4SMatthias Ringwald 2028d210d9c4SMatthias Ringwald // Releases all active calls (if any exist) and accepts the other (held or waiting) call. 2029d0c20769SMatthias Ringwald if (call_held || call_setup_in_progress){ 2030f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL, hfp_connection->call_index, 2031f8737b81SMatthias Ringwald 0, NULL); 2032d0c20769SMatthias Ringwald 2033d0c20769SMatthias Ringwald } 2034d0c20769SMatthias Ringwald 2035d210d9c4SMatthias Ringwald if (call_setup_in_progress){ 203660ebb071SMilanka Ringwald log_info("AG: Call Dropped, Accept new call"); 2037d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 2038a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); 2039d210d9c4SMatthias Ringwald } else { 204060ebb071SMilanka Ringwald log_info("AG: Call Dropped, Resume held call"); 2041d210d9c4SMatthias Ringwald } 2042d210d9c4SMatthias Ringwald if (call_held){ 2043d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 2044a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); 2045d210d9c4SMatthias Ringwald } 2046d0c20769SMatthias Ringwald 2047a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 2048d210d9c4SMatthias Ringwald break; 2049d210d9c4SMatthias Ringwald } 2050d0c20769SMatthias Ringwald 2051d210d9c4SMatthias Ringwald case HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:{ 2052d210d9c4SMatthias Ringwald // Places all active calls (if any exist) on hold and accepts the other (held or waiting) call. 2053d210d9c4SMatthias Ringwald // only update if callsetup changed 2054d0c20769SMatthias Ringwald int call_setup_in_progress = hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; 2055f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL, hfp_connection->call_index, 0, 2056f8737b81SMatthias Ringwald NULL); 2057d0c20769SMatthias Ringwald 2058d210d9c4SMatthias Ringwald if (call_setup_in_progress){ 205960ebb071SMilanka Ringwald log_info("AG: Call on Hold, Accept new call"); 2060d0c20769SMatthias Ringwald hfp_ag_set_callsetup_indicator(); 2061a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); 2062d210d9c4SMatthias Ringwald } else { 206360ebb071SMilanka Ringwald log_info("AG: Swap calls"); 2064d210d9c4SMatthias Ringwald } 2065d0c20769SMatthias Ringwald 2066d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 2067d0c20769SMatthias Ringwald // hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED); 2068a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); 2069a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 2070d210d9c4SMatthias Ringwald break; 2071d210d9c4SMatthias Ringwald } 2072d0c20769SMatthias Ringwald 2073d210d9c4SMatthias Ringwald case HFP_AG_CALL_HOLD_ADD_HELD_CALL: 2074d210d9c4SMatthias Ringwald // Adds a held call to the conversation. 2075d0c20769SMatthias Ringwald if (hfp_gsm_callheld_status() != HFP_CALLHELD_STATUS_NO_CALLS_HELD){ 207660ebb071SMilanka Ringwald log_info("AG: Join 3-way-call"); 2077f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_HOLD_ADD_HELD_CALL, 0, 0, NULL); 2078d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 2079a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); 2080ca59be51SMatthias Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_CONFERENCE_CALL); 2081d210d9c4SMatthias Ringwald } 2082a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_ACTIVE; 2083d210d9c4SMatthias Ringwald break; 2084d210d9c4SMatthias Ringwald case HFP_AG_CALL_HOLD_EXIT_AND_JOIN_CALLS: 2085d210d9c4SMatthias Ringwald // Connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer) 2086f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_CALL_HOLD_EXIT_AND_JOIN_CALLS, 0, 0, NULL); 208760ebb071SMilanka Ringwald log_info("AG: Transfer call -> Connect two calls and disconnect"); 2088d210d9c4SMatthias Ringwald hfp_ag_set_call_indicator(); 2089d0c20769SMatthias Ringwald hfp_ag_set_callheld_indicator(); 2090a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); 2091a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); 2092a0ffb263SMatthias Ringwald hfp_connection->call_state = HFP_CALL_IDLE; 2093d210d9c4SMatthias Ringwald break; 2094ce263fc8SMatthias Ringwald 20953deb3ec6SMatthias Ringwald default: 20963deb3ec6SMatthias Ringwald break; 20973deb3ec6SMatthias Ringwald } 2098d210d9c4SMatthias Ringwald 2099d0c20769SMatthias Ringwald 21003deb3ec6SMatthias Ringwald } 21013deb3ec6SMatthias Ringwald 21029cae807eSMatthias Ringwald 2103a0ffb263SMatthias Ringwald static void hfp_ag_send_call_status(hfp_connection_t * hfp_connection, int call_index){ 21049cae807eSMatthias Ringwald hfp_gsm_call_t * active_call = hfp_gsm_call(call_index); 21059cae807eSMatthias Ringwald if (!active_call) return; 21069cae807eSMatthias Ringwald 21079cae807eSMatthias Ringwald int idx = active_call->index; 21089cae807eSMatthias Ringwald hfp_enhanced_call_dir_t dir = active_call->direction; 21099cae807eSMatthias Ringwald hfp_enhanced_call_status_t status = active_call->enhanced_status; 21109cae807eSMatthias Ringwald hfp_enhanced_call_mode_t mode = active_call->mode; 21119cae807eSMatthias Ringwald hfp_enhanced_call_mpty_t mpty = active_call->mpty; 21129cae807eSMatthias Ringwald uint8_t type = active_call->clip_type; 21139cae807eSMatthias Ringwald char * number = active_call->clip_number; 21149cae807eSMatthias Ringwald 21159cae807eSMatthias Ringwald char buffer[100]; 21169cae807eSMatthias Ringwald // TODO: check length of a buffer, to fit the MTU 21179cae807eSMatthias Ringwald int offset = snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d,%d,%d,%d", HFP_LIST_CURRENT_CALLS, idx, dir, status, mode, mpty); 21189cae807eSMatthias Ringwald if (number){ 21191cc1d9e9SMilanka Ringwald offset += snprintf(buffer+offset, sizeof(buffer)-offset-3, ", \"%s\",%u", number, type); 21209cae807eSMatthias Ringwald } 21219cae807eSMatthias Ringwald snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n"); 212260ebb071SMilanka Ringwald log_info("hfp_ag_send_current_call_status 000 index %d, dir %d, status %d, mode %d, mpty %d, type %d, number %s", idx, dir, status, 21239cae807eSMatthias Ringwald mode, mpty, type, number); 2124a0ffb263SMatthias Ringwald send_str_over_rfcomm(hfp_connection->rfcomm_cid, buffer); 21259cae807eSMatthias Ringwald } 21269cae807eSMatthias Ringwald 2127febc14f5SMatthias Ringwald // sends pending command, returns if command was sent 2128febc14f5SMatthias Ringwald static int hfp_ag_send_commands(hfp_connection_t *hfp_connection){ 2129a0ffb263SMatthias Ringwald if (hfp_connection->send_status_of_current_calls){ 2130a0ffb263SMatthias Ringwald if (hfp_connection->next_call_index < hfp_gsm_get_number_of_calls()){ 2131a0ffb263SMatthias Ringwald hfp_connection->next_call_index++; 2132a0ffb263SMatthias Ringwald hfp_ag_send_call_status(hfp_connection, hfp_connection->next_call_index); 2133febc14f5SMatthias Ringwald return 1; 21349cae807eSMatthias Ringwald } else { 2135febc14f5SMatthias Ringwald // TODO: this code should be removed. check a) before setting send_status_of_current_calls, or b) right before hfp_ag_send_call_status above 2136a0ffb263SMatthias Ringwald hfp_connection->next_call_index = 0; 2137e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2138a0ffb263SMatthias Ringwald hfp_connection->send_status_of_current_calls = 0; 21399cae807eSMatthias Ringwald } 2140ce263fc8SMatthias Ringwald } 2141ce263fc8SMatthias Ringwald 2142a0ffb263SMatthias Ringwald if (hfp_connection->ag_notify_incoming_call_waiting){ 2143a0ffb263SMatthias Ringwald hfp_connection->ag_notify_incoming_call_waiting = 0; 2144a0ffb263SMatthias Ringwald hfp_ag_send_call_waiting_notification(hfp_connection->rfcomm_cid); 2145febc14f5SMatthias Ringwald return 1; 2146aa4dd815SMatthias Ringwald } 2147aa4dd815SMatthias Ringwald 2148*edc46354SMatthias Ringwald // note: before ok_pending and send_error 2149*edc46354SMatthias Ringwald if (hfp_connection->send_apple_information){ 2150*edc46354SMatthias Ringwald hfp_connection->send_apple_information = false; 2151*edc46354SMatthias Ringwald hfp_ag_send_apple_information(hfp_connection->rfcomm_cid); 2152*edc46354SMatthias Ringwald return 1; 2153*edc46354SMatthias Ringwald } 2154*edc46354SMatthias Ringwald 21555e87d1ceSMatthias Ringwald if (hfp_connection->send_error > 0){ 21565e87d1ceSMatthias Ringwald hfp_connection->send_error--; 2157a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2158485ac19eSMilanka Ringwald hfp_ag_send_error(hfp_connection->rfcomm_cid); 2159febc14f5SMatthias Ringwald return 1; 2160aa4dd815SMatthias Ringwald } 2161aa4dd815SMatthias Ringwald 2162ce263fc8SMatthias Ringwald // note: before update AG indicators and ok_pending 2163a0ffb263SMatthias Ringwald if (hfp_connection->send_response_and_hold_status){ 2164a0ffb263SMatthias Ringwald int status = hfp_connection->send_response_and_hold_status - 1; 2165a0ffb263SMatthias Ringwald hfp_connection->send_response_and_hold_status = 0; 2166485ac19eSMilanka Ringwald hfp_ag_send_set_response_and_hold(hfp_connection->rfcomm_cid, status); 2167febc14f5SMatthias Ringwald return 1; 2168ce263fc8SMatthias Ringwald } 2169ce263fc8SMatthias Ringwald 21705be6b47aSMatthias Ringwald // note: before ok_pending and send_error to allow for unsolicited result on custom command 21715be6b47aSMatthias Ringwald if (hfp_connection->send_custom_message != NULL){ 21725be6b47aSMatthias Ringwald const char * message = hfp_connection->send_custom_message; 21735be6b47aSMatthias Ringwald hfp_connection->send_custom_message = NULL; 21745be6b47aSMatthias Ringwald send_str_over_rfcomm(hfp_connection->rfcomm_cid, message); 21757f8f1191SMatthias Ringwald hfp_emit_event(hfp_connection, HFP_SUBEVENT_CUSTOM_AT_MESSAGE_SENT, ERROR_CODE_SUCCESS); 21765be6b47aSMatthias Ringwald return 1; 21775be6b47aSMatthias Ringwald } 21785be6b47aSMatthias Ringwald 21795e87d1ceSMatthias Ringwald if (hfp_connection->ok_pending > 0){ 21805e87d1ceSMatthias Ringwald hfp_connection->ok_pending--; 2181a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2182485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 2183febc14f5SMatthias Ringwald return 1; 2184ce263fc8SMatthias Ringwald } 2185ce263fc8SMatthias Ringwald 218669640b04SMatthias Ringwald // update AG indicators only if enabled by AT+CMER=3,0,0,1 218769640b04SMatthias Ringwald if ((hfp_connection->enable_status_update_for_ag_indicators == 1) && (hfp_connection->ag_indicators_status_update_bitmap != 0)){ 218869640b04SMatthias Ringwald uint16_t i; 2189a0ffb263SMatthias Ringwald for (i=0;i<hfp_connection->ag_indicators_nr;i++){ 2190a0ffb263SMatthias Ringwald if (get_bit(hfp_connection->ag_indicators_status_update_bitmap, i)){ 2191a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, i, 0); 2192485ac19eSMilanka Ringwald hfp_ag_send_transfer_ag_indicators_status_cmd(hfp_connection->rfcomm_cid, &hfp_ag_indicators[i]); 2193febc14f5SMatthias Ringwald return 1; 2194aa4dd815SMatthias Ringwald } 2195aa4dd815SMatthias Ringwald } 2196aa4dd815SMatthias Ringwald } 2197aa4dd815SMatthias Ringwald 2198245852b7SMilanka Ringwald if (hfp_connection->ag_send_no_carrier){ 2199245852b7SMilanka Ringwald hfp_connection->ag_send_no_carrier = false; 2200245852b7SMilanka Ringwald hfp_ag_send_no_carrier(hfp_connection->rfcomm_cid); 2201245852b7SMilanka Ringwald return 1; 2202245852b7SMilanka Ringwald } 2203245852b7SMilanka Ringwald 2204a0ffb263SMatthias Ringwald if (hfp_connection->send_phone_number_for_voice_tag){ 2205a0ffb263SMatthias Ringwald hfp_connection->send_phone_number_for_voice_tag = 0; 2206a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2207e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2208a0ffb263SMatthias Ringwald hfp_ag_send_phone_number_for_voice_tag_cmd(hfp_connection->rfcomm_cid); 2209febc14f5SMatthias Ringwald return 1; 2210aa4dd815SMatthias Ringwald } 2211aa4dd815SMatthias Ringwald 2212a0ffb263SMatthias Ringwald if (hfp_connection->send_subscriber_number){ 2213aeb0f0feSMatthias Ringwald if (hfp_connection->next_subscriber_number_to_send < hfp_ag_subscriber_numbers_count){ 2214aeb0f0feSMatthias Ringwald hfp_phone_number_t phone = hfp_ag_subscriber_numbers[hfp_connection->next_subscriber_number_to_send++]; 2215a0ffb263SMatthias Ringwald hfp_send_subscriber_number_cmd(hfp_connection->rfcomm_cid, phone.type, phone.number); 2216ce263fc8SMatthias Ringwald } else { 2217a0ffb263SMatthias Ringwald hfp_connection->send_subscriber_number = 0; 2218a0ffb263SMatthias Ringwald hfp_connection->next_subscriber_number_to_send = 0; 2219485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 2220ce263fc8SMatthias Ringwald } 2221a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2222febc14f5SMatthias Ringwald return 1; 2223ce263fc8SMatthias Ringwald } 2224ce263fc8SMatthias Ringwald 2225a0ffb263SMatthias Ringwald if (hfp_connection->send_microphone_gain){ 2226a0ffb263SMatthias Ringwald hfp_connection->send_microphone_gain = 0; 2227a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2228485ac19eSMilanka Ringwald hfp_ag_send_set_microphone_gain_cmd(hfp_connection->rfcomm_cid, hfp_connection->microphone_gain); 2229febc14f5SMatthias Ringwald return 1; 2230aa4dd815SMatthias Ringwald } 2231aa4dd815SMatthias Ringwald 2232a0ffb263SMatthias Ringwald if (hfp_connection->send_speaker_gain){ 2233a0ffb263SMatthias Ringwald hfp_connection->send_speaker_gain = 0; 2234a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2235485ac19eSMilanka Ringwald hfp_ag_send_set_speaker_gain_cmd(hfp_connection->rfcomm_cid, hfp_connection->speaker_gain); 2236febc14f5SMatthias Ringwald return 1; 22373deb3ec6SMatthias Ringwald } 22383deb3ec6SMatthias Ringwald 2239a0ffb263SMatthias Ringwald if (hfp_connection->send_ag_status_indicators){ 2240a0ffb263SMatthias Ringwald hfp_connection->send_ag_status_indicators = 0; 2241485ac19eSMilanka Ringwald hfp_ag_send_retrieve_indicators_status_cmd(hfp_connection->rfcomm_cid); 2242febc14f5SMatthias Ringwald return 1; 2243febc14f5SMatthias Ringwald } 2244febc14f5SMatthias Ringwald 224510ae9dfcSMatthias Ringwald if (hfp_connection->extended_audio_gateway_error){ 224610ae9dfcSMatthias Ringwald uint8_t extended_audio_gateway_error = hfp_connection->extended_audio_gateway_error; 224710ae9dfcSMatthias Ringwald hfp_connection->extended_audio_gateway_error = 0; 224810ae9dfcSMatthias Ringwald hfp_ag_send_report_extended_audio_gateway_error(hfp_connection->rfcomm_cid, extended_audio_gateway_error); 224910ae9dfcSMatthias Ringwald 225010ae9dfcSMatthias Ringwald } 2251febc14f5SMatthias Ringwald return 0; 2252febc14f5SMatthias Ringwald } 2253febc14f5SMatthias Ringwald 2254c1eef992SMatthias Ringwald // sends pending command, returns if command was sent 2255c1eef992SMatthias Ringwald static int hfp_ag_run_ring_and_clip(hfp_connection_t *hfp_connection){ 2256c1eef992SMatthias Ringwald // delay RING/CLIP until audio connection has been established for incoming calls 2257c1eef992SMatthias Ringwald // hfp_ag_hf_trigger_ring_and_clip is called in call_setup_state_machine 2258c1eef992SMatthias Ringwald if (hfp_connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING){ 2259c1eef992SMatthias Ringwald if (hfp_connection->ag_ring){ 2260c1eef992SMatthias Ringwald hfp_connection->ag_ring = 0; 2261c1eef992SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2262c1eef992SMatthias Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_RING); 2263c1eef992SMatthias Ringwald hfp_ag_send_ring(hfp_connection->rfcomm_cid); 2264c1eef992SMatthias Ringwald return 1; 2265c1eef992SMatthias Ringwald } 2266c1eef992SMatthias Ringwald 2267c1eef992SMatthias Ringwald if (hfp_connection->ag_send_clip){ 2268c1eef992SMatthias Ringwald hfp_connection->ag_send_clip = 0; 2269c1eef992SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2270c1eef992SMatthias Ringwald hfp_ag_send_clip(hfp_connection->rfcomm_cid); 2271c1eef992SMatthias Ringwald return 1; 2272c1eef992SMatthias Ringwald } 2273c1eef992SMatthias Ringwald } 2274c1eef992SMatthias Ringwald return 0; 2275c1eef992SMatthias Ringwald } 2276c1eef992SMatthias Ringwald 2277febc14f5SMatthias Ringwald static void hfp_ag_run_for_context(hfp_connection_t *hfp_connection){ 2278febc14f5SMatthias Ringwald 227976cc1527SMatthias Ringwald btstack_assert(hfp_connection != NULL); 228076cc1527SMatthias Ringwald btstack_assert(hfp_connection->local_role == HFP_ROLE_AG); 2281febc14f5SMatthias Ringwald 228276cc1527SMatthias Ringwald // during SDP query, RFCOMM CID is not set 228376cc1527SMatthias Ringwald if (hfp_connection->rfcomm_cid == 0) return; 2284ce263fc8SMatthias Ringwald 228584fb9ac1SMilanka Ringwald if (hfp_connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED && hfp_connection->emit_vra_enabled_after_audio_established){ 228684fb9ac1SMilanka Ringwald hfp_connection->emit_vra_enabled_after_audio_established = false; 228784fb9ac1SMilanka Ringwald hfp_emit_voice_recognition_enabled(hfp_connection, ERROR_CODE_SUCCESS); 228884fb9ac1SMilanka Ringwald } 228984fb9ac1SMilanka Ringwald 2290f9f3ba28SMilanka Ringwald if ((hfp_connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) && hfp_connection->release_audio_connection){ 2291f9f3ba28SMilanka Ringwald hfp_connection->state = HFP_W4_SCO_DISCONNECTED; 2292f9f3ba28SMilanka Ringwald hfp_connection->release_audio_connection = 0; 2293f9f3ba28SMilanka Ringwald gap_disconnect(hfp_connection->sco_handle); 2294f9f3ba28SMilanka Ringwald return; 2295f9f3ba28SMilanka Ringwald } 2296f9f3ba28SMilanka Ringwald 2297b7802cc8SMatthias Ringwald // configure NBS/WBS if needed using vendor-specific HCI commands 2298b7802cc8SMatthias Ringwald if (hci_can_send_command_packet_now()) { 22993721a235SMatthias Ringwald #ifdef ENABLE_CC256X_ASSISTED_HFP 23003721a235SMatthias Ringwald // WBS Disassociate 23013721a235SMatthias Ringwald if (hfp_connection->cc256x_send_wbs_disassociate){ 23023721a235SMatthias Ringwald hfp_connection->cc256x_send_wbs_disassociate = false; 23033721a235SMatthias Ringwald hci_send_cmd(&hci_ti_wbs_disassociate); 23043721a235SMatthias Ringwald return; 23053721a235SMatthias Ringwald } 23063721a235SMatthias Ringwald // Write Codec Config 23073721a235SMatthias Ringwald if (hfp_connection->cc256x_send_write_codec_config){ 23083721a235SMatthias Ringwald hfp_connection->cc256x_send_write_codec_config = false; 23093721a235SMatthias Ringwald hfp_cc256x_write_codec_config(hfp_connection); 23103721a235SMatthias Ringwald return; 23113721a235SMatthias Ringwald } 23123721a235SMatthias Ringwald // WBS Associate 23133721a235SMatthias Ringwald if (hfp_connection->cc256x_send_wbs_associate){ 23143721a235SMatthias Ringwald hfp_connection->cc256x_send_wbs_associate = false; 23153721a235SMatthias Ringwald hci_send_cmd(&hci_ti_wbs_associate, hfp_connection->acl_handle); 23163721a235SMatthias Ringwald return; 23173721a235SMatthias Ringwald } 23183721a235SMatthias Ringwald #endif 2319689d4323SMatthias Ringwald #ifdef ENABLE_BCM_PCM_WBS 2320689d4323SMatthias Ringwald // Enable WBS 2321689d4323SMatthias Ringwald if (hfp_connection->bcm_send_enable_wbs){ 2322689d4323SMatthias Ringwald hfp_connection->bcm_send_enable_wbs = false; 2323689d4323SMatthias Ringwald hci_send_cmd(&hci_bcm_enable_wbs, 1, 2); 2324689d4323SMatthias Ringwald return; 2325689d4323SMatthias Ringwald } 2326689d4323SMatthias Ringwald // Write I2S/PCM params 2327689d4323SMatthias Ringwald if (hfp_connection->bcm_send_write_i2spcm_interface_param){ 2328689d4323SMatthias Ringwald hfp_connection->bcm_send_write_i2spcm_interface_param = false; 2329689d4323SMatthias Ringwald hfp_bcm_write_i2spcm_interface_param(hfp_connection); 2330689d4323SMatthias Ringwald return; 2331689d4323SMatthias Ringwald } 2332689d4323SMatthias Ringwald // Disable WBS 2333689d4323SMatthias Ringwald if (hfp_connection->bcm_send_disable_wbs){ 2334689d4323SMatthias Ringwald hfp_connection->bcm_send_disable_wbs = false; 2335689d4323SMatthias Ringwald hci_send_cmd(&hci_bcm_enable_wbs, 0, 2); 2336689d4323SMatthias Ringwald return; 2337689d4323SMatthias Ringwald } 2338689d4323SMatthias Ringwald #endif 23392b5f92fdSMatthias Ringwald #ifdef ENABLE_RTK_PCM_WBS 23402b5f92fdSMatthias Ringwald // Configure CVSD vs. mSBC 23412b5f92fdSMatthias Ringwald if (hfp_connection->rtk_send_sco_config){ 23422b5f92fdSMatthias Ringwald hfp_connection->rtk_send_sco_config = false; 23432b5f92fdSMatthias Ringwald if (hfp_connection->negotiated_codec == HFP_CODEC_MSBC){ 23442b5f92fdSMatthias Ringwald log_info("RTK SCO: 16k + mSBC"); 23452b5f92fdSMatthias Ringwald hci_send_cmd(&hci_rtk_configure_sco_routing, 0x81, 0x90, 0x00, 0x00, 0x1a, 0x0c, 0x00, 0x00, 0x41); 23462b5f92fdSMatthias Ringwald } else { 23472b5f92fdSMatthias Ringwald log_info("RTK SCO: 16k + CVSD"); 23482b5f92fdSMatthias Ringwald hci_send_cmd(&hci_rtk_configure_sco_routing, 0x81, 0x90, 0x00, 0x00, 0x1a, 0x0c, 0x0c, 0x00, 0x01); 23492b5f92fdSMatthias Ringwald } 23502b5f92fdSMatthias Ringwald return; 23512b5f92fdSMatthias Ringwald } 23522b5f92fdSMatthias Ringwald #endif 23535fd6f360SMatthias Ringwald #ifdef ENABLE_NXP_PCM_WBS 23545fd6f360SMatthias Ringwald if (hfp_connection->nxp_start_audio_handle != HCI_CON_HANDLE_INVALID){ 23555fd6f360SMatthias Ringwald hci_con_handle_t sco_handle = hfp_connection->nxp_start_audio_handle; 23565fd6f360SMatthias Ringwald hfp_connection->nxp_start_audio_handle = HCI_CON_HANDLE_INVALID; 23575fd6f360SMatthias Ringwald hci_send_cmd(&hci_nxp_host_pcm_i2s_audio_config, 0, 0, sco_handle, 0); 23585fd6f360SMatthias Ringwald return; 23595fd6f360SMatthias Ringwald } 23605fd6f360SMatthias Ringwald if (hfp_connection->nxp_stop_audio_handle != HCI_CON_HANDLE_INVALID){ 23615fd6f360SMatthias Ringwald hci_con_handle_t sco_handle = hfp_connection->nxp_stop_audio_handle; 23625fd6f360SMatthias Ringwald hfp_connection->nxp_stop_audio_handle = HCI_CON_HANDLE_INVALID; 23635fd6f360SMatthias Ringwald hci_send_cmd(&hci_nxp_host_pcm_i2s_audio_config, 1, 0, sco_handle, 0); 23645fd6f360SMatthias Ringwald return; 23655fd6f360SMatthias Ringwald } 23665fd6f360SMatthias Ringwald #endif 2367b7802cc8SMatthias Ringwald } 2368b7802cc8SMatthias Ringwald 236948e6eeeeSMatthias Ringwald #if defined (ENABLE_CC256X_ASSISTED_HFP) || defined (ENABLE_BCM_PCM_WBS) 237048e6eeeeSMatthias Ringwald if (hfp_connection->state == HFP_W4_WBS_SHUTDOWN){ 237148e6eeeeSMatthias Ringwald hfp_finalize_connection_context(hfp_connection); 237248e6eeeeSMatthias Ringwald return; 237348e6eeeeSMatthias Ringwald } 237448e6eeeeSMatthias Ringwald #endif 23753721a235SMatthias Ringwald 2376febc14f5SMatthias Ringwald if (!rfcomm_can_send_packet_now(hfp_connection->rfcomm_cid)) { 2377febc14f5SMatthias Ringwald log_info("hfp_ag_run_for_context: request can send for 0x%02x", hfp_connection->rfcomm_cid); 2378febc14f5SMatthias Ringwald rfcomm_request_can_send_now_event(hfp_connection->rfcomm_cid); 2379febc14f5SMatthias Ringwald return; 2380febc14f5SMatthias Ringwald } 2381febc14f5SMatthias Ringwald 2382febc14f5SMatthias Ringwald int cmd_sent = hfp_ag_send_commands(hfp_connection); 2383febc14f5SMatthias Ringwald 2384febc14f5SMatthias Ringwald if (!cmd_sent){ 2385febc14f5SMatthias Ringwald cmd_sent = hfp_ag_run_for_context_service_level_connection(hfp_connection); 2386febc14f5SMatthias Ringwald } 2387febc14f5SMatthias Ringwald 2388c04cf7ffSMilanka Ringwald if (!cmd_sent){ 2389c04cf7ffSMilanka Ringwald cmd_sent = hfp_ag_run_for_context_service_level_connection_queries(hfp_connection); 23903deb3ec6SMatthias Ringwald } 23913deb3ec6SMatthias Ringwald 2392c04cf7ffSMilanka Ringwald if (!cmd_sent){ 2393c04cf7ffSMilanka Ringwald cmd_sent = call_setup_state_machine(hfp_connection); 2394aa4dd815SMatthias Ringwald } 2395aa4dd815SMatthias Ringwald 2396b956fff3SMatthias Ringwald if (!cmd_sent){ 2397b956fff3SMatthias Ringwald cmd_sent = hfp_ag_run_for_audio_connection(hfp_connection); 2398b956fff3SMatthias Ringwald } 2399b956fff3SMatthias Ringwald 2400c95b5b3cSMilanka Ringwald if (!cmd_sent){ 2401c1eef992SMatthias Ringwald cmd_sent = hfp_ag_run_ring_and_clip(hfp_connection); 2402c1eef992SMatthias Ringwald } 2403c1eef992SMatthias Ringwald 2404c1eef992SMatthias Ringwald if (!cmd_sent){ 2405c95b5b3cSMilanka Ringwald cmd_sent = hfp_ag_voice_recognition_state_machine(hfp_connection); 2406c95b5b3cSMilanka Ringwald } 2407b956fff3SMatthias Ringwald 2408d0a0eceeSMatthias Ringwald // disconnect 2409d0a0eceeSMatthias Ringwald if (!cmd_sent && (hfp_connection->command == HFP_CMD_NONE) && (hfp_connection->state == HFP_W2_DISCONNECT_RFCOMM)){ 2410d0a0eceeSMatthias Ringwald hfp_connection->state = HFP_W4_RFCOMM_DISCONNECTED; 2411d0a0eceeSMatthias Ringwald rfcomm_disconnect(hfp_connection->rfcomm_cid); 2412d0a0eceeSMatthias Ringwald } 2413d0a0eceeSMatthias Ringwald 2414c04cf7ffSMilanka Ringwald if (cmd_sent){ 2415a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2416473ac565SMatthias Ringwald rfcomm_request_can_send_now_event(hfp_connection->rfcomm_cid); 2417473ac565SMatthias Ringwald } 24183deb3ec6SMatthias Ringwald } 2419d68dcce1SMatthias Ringwald 2420e0d09929SMatthias Ringwald static int hfp_parser_is_end_of_line(uint8_t byte){ 2421c1ab6cc1SMatthias Ringwald return (byte == '\n') || (byte == '\r'); 2422e0d09929SMatthias Ringwald } 2423e0d09929SMatthias Ringwald 2424fdf18f86SMilanka Ringwald static void hfp_ag_handle_rfcomm_data(hfp_connection_t * hfp_connection, uint8_t *packet, uint16_t size){ 24258a46ec40SMatthias Ringwald // assertion: size >= 1 as rfcomm.c does not deliver empty packets 2426fdf18f86SMilanka Ringwald if (size < 1) return; 2427347d46c8SMilanka Ringwald uint8_t status = ERROR_CODE_SUCCESS; 24281e35c04dSMatthias Ringwald 2429186dd3d2SMatthias Ringwald hfp_log_rfcomm_message("HFP_AG_RX", packet, size); 2430e43d1938SMatthias Ringwald #ifdef ENABLE_HFP_AT_MESSAGES 2431e43d1938SMatthias Ringwald hfp_emit_string_event(hfp_connection, HFP_SUBEVENT_AT_MESSAGE_RECEIVED, (char *) packet); 2432e43d1938SMatthias Ringwald #endif 24331e35c04dSMatthias Ringwald 24348a46ec40SMatthias Ringwald // process messages byte-wise 24353deb3ec6SMatthias Ringwald int pos; 24363deb3ec6SMatthias Ringwald for (pos = 0; pos < size ; pos++){ 2437a0ffb263SMatthias Ringwald hfp_parse(hfp_connection, packet[pos], 0); 2438186dd3d2SMatthias Ringwald 2439e0d09929SMatthias Ringwald // parse until end of line 2440e0d09929SMatthias Ringwald if (!hfp_parser_is_end_of_line(packet[pos])) continue; 2441e0d09929SMatthias Ringwald 2442186dd3d2SMatthias Ringwald hfp_generic_status_indicator_t * indicator; 2443a0ffb263SMatthias Ringwald switch(hfp_connection->command){ 244445796ff1SMilanka Ringwald case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION: 2445e7c46708SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2446e7c46708SMatthias Ringwald hfp_connection->ag_vra_requested_by_hf = true; 244745796ff1SMilanka Ringwald break; 2448ce263fc8SMatthias Ringwald case HFP_CMD_RESPONSE_AND_HOLD_QUERY: 2449fd66594dSMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2450ce263fc8SMatthias Ringwald if (hfp_ag_response_and_hold_active){ 2451a0ffb263SMatthias Ringwald hfp_connection->send_response_and_hold_status = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD + 1; 2452ce263fc8SMatthias Ringwald } 2453e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2454ce263fc8SMatthias Ringwald break; 2455ce263fc8SMatthias Ringwald case HFP_CMD_RESPONSE_AND_HOLD_COMMAND: 2456fd66594dSMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 24570222a807SMatthias Ringwald switch(hfp_connection->ag_response_and_hold_action){ 2458ce263fc8SMatthias Ringwald case HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD: 2459a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF, hfp_connection); 2460ce263fc8SMatthias Ringwald break; 2461ce263fc8SMatthias Ringwald case HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED: 2462a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF, hfp_connection); 2463ce263fc8SMatthias Ringwald break; 2464ce263fc8SMatthias Ringwald case HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED: 2465a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF, hfp_connection); 2466ce263fc8SMatthias Ringwald break; 2467ce263fc8SMatthias Ringwald default: 2468ce263fc8SMatthias Ringwald break; 2469ce263fc8SMatthias Ringwald } 2470e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2471ce263fc8SMatthias Ringwald break; 2472ce263fc8SMatthias Ringwald case HFP_CMD_HF_INDICATOR_STATUS: 2473a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2474347d46c8SMilanka Ringwald 2475aeb0f0feSMatthias Ringwald if (hfp_connection->parser_indicator_index < hfp_ag_generic_status_indicators_nr){ 2476aeb0f0feSMatthias Ringwald indicator = &hfp_ag_generic_status_indicators[hfp_connection->parser_indicator_index]; 2477ce263fc8SMatthias Ringwald switch (indicator->uuid){ 2478347d46c8SMilanka Ringwald case HFP_HF_INDICATOR_UUID_ENHANCED_SAFETY: 24790222a807SMatthias Ringwald if (hfp_connection->parser_indicator_value > 1) { 2480e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2481ce263fc8SMatthias Ringwald break; 2482ce263fc8SMatthias Ringwald } 2483e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2484585db78dSMilanka Ringwald hfp_ag_emit_hf_indicator_value(hfp_connection, indicator->uuid, hfp_connection->parser_indicator_value); 2485347d46c8SMilanka Ringwald break; 2486347d46c8SMilanka Ringwald case HFP_HF_INDICATOR_UUID_BATTERY_LEVEL: 2487347d46c8SMilanka Ringwald if (hfp_connection->parser_indicator_value > 100){ 2488e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2489347d46c8SMilanka Ringwald break; 2490347d46c8SMilanka Ringwald } 2491e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2492585db78dSMilanka Ringwald hfp_ag_emit_hf_indicator_value(hfp_connection, indicator->uuid, hfp_connection->parser_indicator_value); 2493347d46c8SMilanka Ringwald break; 2494347d46c8SMilanka Ringwald default: 2495e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2496347d46c8SMilanka Ringwald break; 2497347d46c8SMilanka Ringwald } 2498347d46c8SMilanka Ringwald } else { 2499e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2500347d46c8SMilanka Ringwald } 2501ce263fc8SMatthias Ringwald break; 2502ce263fc8SMatthias Ringwald case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: 2503ce263fc8SMatthias Ringwald // expected by SLC state machine 2504a0ffb263SMatthias Ringwald if (hfp_connection->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) break; 2505a0ffb263SMatthias Ringwald hfp_connection->send_ag_indicators_segment = 0; 2506a0ffb263SMatthias Ringwald hfp_connection->send_ag_status_indicators = 1; 2507ce263fc8SMatthias Ringwald break; 2508ce263fc8SMatthias Ringwald case HFP_CMD_LIST_CURRENT_CALLS: 2509a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2510a0ffb263SMatthias Ringwald hfp_connection->next_call_index = 0; 2511a0ffb263SMatthias Ringwald hfp_connection->send_status_of_current_calls = 1; 2512ce263fc8SMatthias Ringwald break; 2513ce263fc8SMatthias Ringwald case HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION: 2514fd66594dSMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2515aeb0f0feSMatthias Ringwald if (hfp_ag_subscriber_numbers_count == 0){ 2516485ac19eSMilanka Ringwald hfp_ag_send_ok(hfp_connection->rfcomm_cid); 2517ce263fc8SMatthias Ringwald break; 2518ce263fc8SMatthias Ringwald } 2519a0ffb263SMatthias Ringwald hfp_connection->next_subscriber_number_to_send = 0; 2520a0ffb263SMatthias Ringwald hfp_connection->send_subscriber_number = 1; 2521ce263fc8SMatthias Ringwald break; 2522c1797c7dSMatthias Ringwald case HFP_CMD_TRANSMIT_DTMF_CODES: 25230222a807SMatthias Ringwald { 2524a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 25250222a807SMatthias Ringwald char buffer[2]; 25260222a807SMatthias Ringwald buffer[0] = (char) hfp_connection->ag_dtmf_code; 25270222a807SMatthias Ringwald buffer[1] = 0; 25280222a807SMatthias Ringwald hfp_emit_string_event(hfp_connection, HFP_SUBEVENT_TRANSMIT_DTMF_CODES, buffer); 2529c1797c7dSMatthias Ringwald break; 25300222a807SMatthias Ringwald } 2531aa4dd815SMatthias Ringwald case HFP_CMD_HF_REQUEST_PHONE_NUMBER: 2532a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2533ca59be51SMatthias Ringwald hfp_emit_simple_event(hfp_connection, HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG); 2534aa4dd815SMatthias Ringwald break; 2535aa4dd815SMatthias Ringwald case HFP_CMD_TURN_OFF_EC_AND_NR: 2536a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2537347d46c8SMilanka Ringwald 2538aeb0f0feSMatthias Ringwald if (get_bit(hfp_ag_supported_features, HFP_AGSF_EC_NR_FUNCTION)){ 2539e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2540347d46c8SMilanka Ringwald status = ERROR_CODE_SUCCESS; 2541aa4dd815SMatthias Ringwald } else { 2542e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2543347d46c8SMilanka Ringwald status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 2544aa4dd815SMatthias Ringwald } 2545347d46c8SMilanka Ringwald hfp_emit_event(hfp_connection, HFP_SUBEVENT_ECHO_CANCELING_AND_NOISE_REDUCTION_DEACTIVATE, status); 2546aa4dd815SMatthias Ringwald break; 2547aa4dd815SMatthias Ringwald case HFP_CMD_CALL_ANSWERED: 2548a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 254960ebb071SMilanka Ringwald log_info("HFP: ATA"); 2550a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF, hfp_connection); 2551aa4dd815SMatthias Ringwald break; 2552aa4dd815SMatthias Ringwald case HFP_CMD_HANG_UP_CALL: 2553a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2554e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2555a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_TERMINATE_CALL_BY_HF, hfp_connection); 2556aa4dd815SMatthias Ringwald break; 2557aa4dd815SMatthias Ringwald case HFP_CMD_CALL_HOLD: { 2558a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2559e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2560d0c20769SMatthias Ringwald 25610222a807SMatthias Ringwald switch (hfp_connection->ag_call_hold_action){ 25620222a807SMatthias Ringwald case 0: 2563d0c20769SMatthias Ringwald // Releases all held calls or sets User Determined User Busy (UDUB) for a waiting call. 2564a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_CALL_HOLD_USER_BUSY, hfp_connection); 2565aa4dd815SMatthias Ringwald break; 25660222a807SMatthias Ringwald case 1: 2567d0c20769SMatthias Ringwald // Releases all active calls (if any exist) and accepts the other (held or waiting) call. 2568d0c20769SMatthias Ringwald // Where both a held and a waiting call exist, the above procedures shall apply to the 2569d0c20769SMatthias Ringwald // waiting call (i.e., not to the held call) in conflicting situation. 2570a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL, hfp_connection); 2571aa4dd815SMatthias Ringwald break; 25720222a807SMatthias Ringwald case 2: 2573d0c20769SMatthias Ringwald // Places all active calls (if any exist) on hold and accepts the other (held or waiting) call. 2574d0c20769SMatthias Ringwald // Where both a held and a waiting call exist, the above procedures shall apply to the 2575d0c20769SMatthias Ringwald // waiting call (i.e., not to the held call) in conflicting situation. 2576a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL, hfp_connection); 2577aa4dd815SMatthias Ringwald break; 25780222a807SMatthias Ringwald case 3: 2579d0c20769SMatthias Ringwald // Adds a held call to the conversation. 2580a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_CALL_HOLD_ADD_HELD_CALL, hfp_connection); 2581aa4dd815SMatthias Ringwald break; 25820222a807SMatthias Ringwald case 4: 2583d0c20769SMatthias Ringwald // Connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer). 2584a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_CALL_HOLD_EXIT_AND_JOIN_CALLS, hfp_connection); 2585aa4dd815SMatthias Ringwald break; 2586aa4dd815SMatthias Ringwald default: 2587aa4dd815SMatthias Ringwald break; 2588aa4dd815SMatthias Ringwald } 25890222a807SMatthias Ringwald hfp_connection->call_index = 0; 259035e92150SMatthias Ringwald break; 2591aa4dd815SMatthias Ringwald } 2592aa4dd815SMatthias Ringwald case HFP_CMD_CALL_PHONE_NUMBER: 2593a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 25949ff73f41SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_INITIATED_BY_HF, hfp_connection); 2595aa4dd815SMatthias Ringwald break; 2596aa4dd815SMatthias Ringwald case HFP_CMD_REDIAL_LAST_NUMBER: 2597a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2598a0ffb263SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_REDIAL_INITIATED, hfp_connection); 2599aa4dd815SMatthias Ringwald break; 2600aa4dd815SMatthias Ringwald case HFP_CMD_ENABLE_CLIP: 2601a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2602a0ffb263SMatthias Ringwald log_info("hfp: clip set, now: %u", hfp_connection->clip_enabled); 2603e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2604aa4dd815SMatthias Ringwald break; 2605aa4dd815SMatthias Ringwald case HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION: 2606a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2607a0ffb263SMatthias Ringwald log_info("hfp: call waiting notification set, now: %u", hfp_connection->call_waiting_notification_enabled); 2608e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2609aa4dd815SMatthias Ringwald break; 2610aa4dd815SMatthias Ringwald case HFP_CMD_SET_SPEAKER_GAIN: 2611a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2612e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 261360ebb071SMilanka Ringwald log_info("HF speaker gain = %u", hfp_connection->speaker_gain); 26143db60f78SBjoern Hartmann hfp_emit_event(hfp_connection, HFP_SUBEVENT_SPEAKER_VOLUME, hfp_connection->speaker_gain); 2615aa4dd815SMatthias Ringwald break; 2616aa4dd815SMatthias Ringwald case HFP_CMD_SET_MICROPHONE_GAIN: 2617a0ffb263SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2618e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 261960ebb071SMilanka Ringwald log_info("HF microphone gain = %u", hfp_connection->microphone_gain); 26202abbd98dSMatthias Ringwald hfp_emit_event(hfp_connection, HFP_SUBEVENT_MICROPHONE_VOLUME, hfp_connection->microphone_gain); 2621aa4dd815SMatthias Ringwald break; 2622471dea41SMatthias Ringwald case HFP_CMD_CUSTOM_MESSAGE: 2623471dea41SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2624471dea41SMatthias Ringwald hfp_parser_reset_line_buffer(hfp_connection); 26256d9a41f9SMatthias Ringwald log_info("Custom AT Command ID 0x%04x", hfp_connection->custom_at_command_id); 2626471dea41SMatthias Ringwald hfp_ag_emit_custom_command_event(hfp_connection); 2627471dea41SMatthias Ringwald break; 2628*edc46354SMatthias Ringwald case HFP_CMD_APPLE_ACCESSORY_INFORMATION: 2629*edc46354SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2630*edc46354SMatthias Ringwald if (hfp_ag_apple_device != NULL){ 2631*edc46354SMatthias Ringwald hfp_connection->send_apple_information = true; 2632*edc46354SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2633*edc46354SMatthias Ringwald hfp_ag_emit_apple_accessory_information(hfp_connection); 2634*edc46354SMatthias Ringwald } else { 2635*edc46354SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2636*edc46354SMatthias Ringwald }; 2637*edc46354SMatthias Ringwald break; 2638*edc46354SMatthias Ringwald case HFP_CMD_APPLE_ACCESSORY_STATE: 2639*edc46354SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2640*edc46354SMatthias Ringwald if (hfp_ag_apple_device != NULL) { 2641*edc46354SMatthias Ringwald hfp_ag_emit_apple_accessory_state(hfp_connection); 2642*edc46354SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 2643*edc46354SMatthias Ringwald } else { 2644*edc46354SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 2645*edc46354SMatthias Ringwald } 2646*edc46354SMatthias Ringwald break; 26473f38f554SMatthias Ringwald case HFP_CMD_UNKNOWN: 26483f38f554SMatthias Ringwald hfp_connection->command = HFP_CMD_NONE; 2649e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 26503f38f554SMatthias Ringwald break; 2651aa4dd815SMatthias Ringwald default: 2652aa4dd815SMatthias Ringwald break; 26533deb3ec6SMatthias Ringwald } 26543deb3ec6SMatthias Ringwald } 26550cef86faSMatthias Ringwald } 26563deb3ec6SMatthias Ringwald 26571c6a0fc0SMatthias Ringwald static void hfp_ag_run(void){ 2658d63c37a1SMatthias Ringwald btstack_linked_list_iterator_t it; 2659d63c37a1SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 2660d63c37a1SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 2661d63c37a1SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 266222387625SMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 2663f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 26643deb3ec6SMatthias Ringwald } 26653deb3ec6SMatthias Ringwald } 26663deb3ec6SMatthias Ringwald 26671c6a0fc0SMatthias Ringwald static void hfp_ag_rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 2668fdf18f86SMilanka Ringwald hfp_connection_t * hfp_connection = NULL; 26693deb3ec6SMatthias Ringwald switch (packet_type){ 26703deb3ec6SMatthias Ringwald case RFCOMM_DATA_PACKET: 2671fdf18f86SMilanka Ringwald hfp_connection = get_hfp_connection_context_for_rfcomm_cid(channel); 2672fd075f20SMilanka Ringwald btstack_assert(hfp_connection != NULL); 2673fdf18f86SMilanka Ringwald 2674fdf18f86SMilanka Ringwald hfp_ag_handle_rfcomm_data(hfp_connection, packet, size); 2675fdf18f86SMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 2676fdf18f86SMilanka Ringwald return; 26773deb3ec6SMatthias Ringwald case HCI_EVENT_PACKET: 2678e30a6a47SMatthias Ringwald if (packet[0] == RFCOMM_EVENT_CAN_SEND_NOW){ 2679e30a6a47SMatthias Ringwald uint16_t rfcomm_cid = rfcomm_event_can_send_now_get_rfcomm_cid(packet); 2680fd075f20SMilanka Ringwald hfp_connection = get_hfp_connection_context_for_rfcomm_cid(rfcomm_cid); 2681fd075f20SMilanka Ringwald btstack_assert(hfp_connection != NULL); 2682fd075f20SMilanka Ringwald 2683fd075f20SMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 2684e30a6a47SMatthias Ringwald return; 2685e30a6a47SMatthias Ringwald } 268627950165SMatthias Ringwald hfp_handle_rfcomm_event(packet_type, channel, packet, size, HFP_ROLE_AG); 2687aa4dd815SMatthias Ringwald break; 26883deb3ec6SMatthias Ringwald default: 26893deb3ec6SMatthias Ringwald break; 26903deb3ec6SMatthias Ringwald } 26913deb3ec6SMatthias Ringwald 26921c6a0fc0SMatthias Ringwald hfp_ag_run(); 26933deb3ec6SMatthias Ringwald } 26943deb3ec6SMatthias Ringwald 26951c6a0fc0SMatthias Ringwald static void hfp_ag_hci_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 2696405014fbSMatthias Ringwald hfp_handle_hci_event(packet_type, channel, packet, size, HFP_ROLE_AG); 26971c6a0fc0SMatthias Ringwald hfp_ag_run(); 2698405014fbSMatthias Ringwald } 2699405014fbSMatthias Ringwald 2700aa10b9cbSMatthias Ringwald void hfp_ag_init_codecs(uint8_t codecs_nr, const uint8_t * codecs){ 2701aa10b9cbSMatthias Ringwald btstack_assert(codecs_nr <= HFP_MAX_NUM_CODECS); 27028cd06fb6SMatthias Ringwald if (codecs_nr > HFP_MAX_NUM_CODECS) return; 2703aa10b9cbSMatthias Ringwald 2704aeb0f0feSMatthias Ringwald hfp_ag_codecs_nr = codecs_nr; 2705aa10b9cbSMatthias Ringwald uint8_t i; 27063deb3ec6SMatthias Ringwald for (i=0; i < codecs_nr; i++){ 2707aeb0f0feSMatthias Ringwald hfp_ag_codecs[i] = codecs[i]; 27083deb3ec6SMatthias Ringwald } 2709a0ffb263SMatthias Ringwald } 27103deb3ec6SMatthias Ringwald 2711a0ffb263SMatthias Ringwald void hfp_ag_init_supported_features(uint32_t supported_features){ 2712aeb0f0feSMatthias Ringwald hfp_ag_supported_features = supported_features; 271340a8ee13SMatthias Ringwald hfp_ag_in_band_ring_tone_active = has_in_band_ring_tone(); 2714a0ffb263SMatthias Ringwald } 27153deb3ec6SMatthias Ringwald 27167ca89cabSMatthias Ringwald void hfp_ag_init_ag_indicators(int ag_indicators_nr, const hfp_ag_indicator_t * ag_indicators){ 27178cd06fb6SMatthias Ringwald btstack_assert(ag_indicators_nr <= HFP_MAX_NUM_INDICATORS); 27188cd06fb6SMatthias Ringwald if (ag_indicators_nr > HFP_MAX_NUM_CODECS) return; 27198cd06fb6SMatthias Ringwald 2720a0ffb263SMatthias Ringwald hfp_ag_indicators_nr = ag_indicators_nr; 27216535961aSMatthias Ringwald (void)memcpy(hfp_ag_indicators, ag_indicators, 27226535961aSMatthias Ringwald ag_indicators_nr * sizeof(hfp_ag_indicator_t)); 2723a0ffb263SMatthias Ringwald } 27243deb3ec6SMatthias Ringwald 27257ca89cabSMatthias Ringwald void hfp_ag_init_hf_indicators(int hf_indicators_nr, const hfp_generic_status_indicator_t * hf_indicators){ 27268cd06fb6SMatthias Ringwald btstack_assert(hf_indicators_nr <= HFP_MAX_NUM_INDICATORS); 27278cd06fb6SMatthias Ringwald if (hf_indicators_nr > HFP_MAX_NUM_CODECS) return; 27288cd06fb6SMatthias Ringwald 2729aeb0f0feSMatthias Ringwald hfp_ag_generic_status_indicators_nr = hf_indicators_nr; 2730aeb0f0feSMatthias Ringwald (void)memcpy(hfp_ag_generic_status_indicators, hf_indicators, 27316535961aSMatthias Ringwald hf_indicators_nr * sizeof(hfp_generic_status_indicator_t)); 2732a0ffb263SMatthias Ringwald } 2733a0ffb263SMatthias Ringwald 2734a0ffb263SMatthias Ringwald void hfp_ag_init_call_hold_services(int call_hold_services_nr, const char * call_hold_services[]){ 27353deb3ec6SMatthias Ringwald hfp_ag_call_hold_services_nr = call_hold_services_nr; 27366535961aSMatthias Ringwald (void)memcpy(hfp_ag_call_hold_services, call_hold_services, 27376535961aSMatthias Ringwald call_hold_services_nr * sizeof(char *)); 2738a0ffb263SMatthias Ringwald } 2739a0ffb263SMatthias Ringwald 2740*edc46354SMatthias Ringwald void hfp_ag_init_apple_identification(const char * device, uint8_t features){ 2741*edc46354SMatthias Ringwald hfp_ag_apple_device = device; 2742*edc46354SMatthias Ringwald hfp_ag_apple_features = features; 2743*edc46354SMatthias Ringwald } 2744a0ffb263SMatthias Ringwald 2745ab2445a0SMatthias Ringwald void hfp_ag_init(uint8_t rfcomm_channel_nr){ 274627950165SMatthias Ringwald 2747520c92d5SMatthias Ringwald hfp_init(); 274820b2edb6SMatthias Ringwald hfp_ag_call_hold_services_nr = 0; 274927bb1817SMatthias Ringwald hfp_ag_response_and_hold_active = false; 275020b2edb6SMatthias Ringwald hfp_ag_indicators_nr = 0; 2751aeb0f0feSMatthias Ringwald hfp_ag_codecs_nr = 0; 2752aeb0f0feSMatthias Ringwald hfp_ag_supported_features = HFP_DEFAULT_AG_SUPPORTED_FEATURES; 275340a8ee13SMatthias Ringwald hfp_ag_in_band_ring_tone_active = has_in_band_ring_tone(); 2754aeb0f0feSMatthias Ringwald hfp_ag_subscriber_numbers = NULL; 2755aeb0f0feSMatthias Ringwald hfp_ag_subscriber_numbers_count = 0; 2756d63c37a1SMatthias Ringwald 27571c6a0fc0SMatthias Ringwald hfp_ag_hci_event_callback_registration.callback = &hfp_ag_hci_event_packet_handler; 27581c6a0fc0SMatthias Ringwald hci_add_event_handler(&hfp_ag_hci_event_callback_registration); 275927950165SMatthias Ringwald 27601c6a0fc0SMatthias Ringwald rfcomm_register_service(&hfp_ag_rfcomm_packet_handler, rfcomm_channel_nr, 0xffff); 276127950165SMatthias Ringwald 276227950165SMatthias Ringwald // used to set packet handler for outgoing rfcomm connections - could be handled by emitting an event to us 27631c6a0fc0SMatthias Ringwald hfp_set_ag_rfcomm_packet_handler(&hfp_ag_rfcomm_packet_handler); 2764aa4dd815SMatthias Ringwald 2765d210d9c4SMatthias Ringwald hfp_gsm_init(); 27663deb3ec6SMatthias Ringwald } 27673deb3ec6SMatthias Ringwald 276820b2edb6SMatthias Ringwald void hfp_ag_deinit(void){ 276920b2edb6SMatthias Ringwald hfp_deinit(); 277020b2edb6SMatthias Ringwald hfp_gsm_deinit(); 2771aeb0f0feSMatthias Ringwald 2772aeb0f0feSMatthias Ringwald hfp_ag_callback = NULL; 2773aeb0f0feSMatthias Ringwald hfp_ag_supported_features = 0; 2774aeb0f0feSMatthias Ringwald hfp_ag_codecs_nr = 0; 2775aeb0f0feSMatthias Ringwald hfp_ag_indicators_nr = 0; 2776aeb0f0feSMatthias Ringwald hfp_ag_call_hold_services_nr = 0; 2777*edc46354SMatthias Ringwald hfp_ag_apple_device = NULL; 277820b2edb6SMatthias Ringwald (void) memset(&hfp_ag_call_hold_services, 0, sizeof(hfp_ag_call_hold_services)); 2779aeb0f0feSMatthias Ringwald hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD; 278020b2edb6SMatthias Ringwald (void) memset(&hfp_ag_response_and_hold_state, 0, sizeof(hfp_response_and_hold_state_t)); 2781aeb0f0feSMatthias Ringwald hfp_ag_subscriber_numbers = NULL; 2782aeb0f0feSMatthias Ringwald (void) memset(&hfp_ag_hci_event_callback_registration, 0, sizeof(btstack_packet_callback_registration_t)); 278381e25d0aSMatthias Ringwald hfp_ag_custom_call_sm_handler = NULL; 278420b2edb6SMatthias Ringwald } 278520b2edb6SMatthias Ringwald 27864eb3f1d8SMilanka Ringwald uint8_t hfp_ag_establish_service_level_connection(bd_addr_t bd_addr){ 27874eb3f1d8SMilanka Ringwald return hfp_establish_service_level_connection(bd_addr, BLUETOOTH_SERVICE_CLASS_HANDSFREE, HFP_ROLE_AG); 27883deb3ec6SMatthias Ringwald } 27893deb3ec6SMatthias Ringwald 2790657bc59fSMilanka Ringwald uint8_t hfp_ag_release_service_level_connection(hci_con_handle_t acl_handle){ 27919c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 2792a33eb0c4SMilanka Ringwald if (!hfp_connection){ 2793657bc59fSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2794a33eb0c4SMilanka Ringwald } 2795c5fa3c94SMilanka Ringwald 27961ffa0dd9SMilanka Ringwald hfp_trigger_release_service_level_connection(hfp_connection); 2797f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 2798657bc59fSMilanka Ringwald return ERROR_CODE_SUCCESS; 27993deb3ec6SMatthias Ringwald } 28003deb3ec6SMatthias Ringwald 2801c5fa3c94SMilanka Ringwald uint8_t hfp_ag_report_extended_audio_gateway_error_result_code(hci_con_handle_t acl_handle, hfp_cme_error_t error){ 28029c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 2803a0ffb263SMatthias Ringwald if (!hfp_connection){ 2804c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 28053deb3ec6SMatthias Ringwald } 28063deb3ec6SMatthias Ringwald 2807c5fa3c94SMilanka Ringwald if (!hfp_connection->enable_extended_audio_gateway_error_report){ 2808c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 2809c5fa3c94SMilanka Ringwald } 2810c5fa3c94SMilanka Ringwald 2811c5fa3c94SMilanka Ringwald hfp_connection->extended_audio_gateway_error = error; 2812c5fa3c94SMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 2813c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 2814c5fa3c94SMilanka Ringwald } 2815c5fa3c94SMilanka Ringwald 2816c5fa3c94SMilanka Ringwald static uint8_t hfp_ag_setup_audio_connection(hfp_connection_t * hfp_connection){ 2817c5fa3c94SMilanka Ringwald if (hfp_connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED){ 2818c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 2819c5fa3c94SMilanka Ringwald } 2820c5fa3c94SMilanka Ringwald if (hfp_connection->state >= HFP_W2_DISCONNECT_SCO){ 2821c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 2822c5fa3c94SMilanka Ringwald } 28233deb3ec6SMatthias Ringwald 2824a0ffb263SMatthias Ringwald hfp_connection->establish_audio_connection = 1; 28252561704cSMatthias Ringwald hfp_connection->sco_for_msbc_failed = 0; 2826a0ffb263SMatthias Ringwald if (!has_codec_negotiation_feature(hfp_connection)){ 2827d6ff09e1SMatthias Ringwald log_info("hfp_ag_establish_audio_connection - no codec negotiation feature, using CVSD"); 2828d6ff09e1SMatthias Ringwald hfp_connection->negotiated_codec = HFP_CODEC_CVSD; 2829a0ffb263SMatthias Ringwald hfp_connection->codecs_state = HFP_CODECS_EXCHANGED; 28307522e673SMatthias Ringwald // now, pick link settings 2831afac2a04SMilanka Ringwald hfp_init_link_settings(hfp_connection, hfp_ag_esco_s4_supported(hfp_connection)); 28323721a235SMatthias Ringwald #ifdef ENABLE_CC256X_ASSISTED_HFP 28333721a235SMatthias Ringwald hfp_cc256x_prepare_for_sco(hfp_connection); 28343721a235SMatthias Ringwald #endif 2835c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 28363deb3ec6SMatthias Ringwald } 2837aa4dd815SMatthias Ringwald 2838bc94e48cSMatthias Ringwald hfp_connection->ag_send_common_codec = true; 2839411a9776SMilanka Ringwald hfp_connection->codecs_state = HFP_CODECS_IDLE; 2840c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 2841aa4dd815SMatthias Ringwald } 2842aa4dd815SMatthias Ringwald 2843c5fa3c94SMilanka Ringwald uint8_t hfp_ag_establish_audio_connection(hci_con_handle_t acl_handle){ 28449c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 2845a33eb0c4SMilanka Ringwald if (!hfp_connection){ 2846c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2847a33eb0c4SMilanka Ringwald } 2848c5fa3c94SMilanka Ringwald uint8_t status = hfp_ag_setup_audio_connection(hfp_connection); 2849c5fa3c94SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 2850c5fa3c94SMilanka Ringwald return status; 2851c5fa3c94SMilanka Ringwald } 2852f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 2853c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 28543deb3ec6SMatthias Ringwald } 28553deb3ec6SMatthias Ringwald 2856c5fa3c94SMilanka Ringwald uint8_t hfp_ag_release_audio_connection(hci_con_handle_t acl_handle){ 28579c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 2858a33eb0c4SMilanka Ringwald if (!hfp_connection){ 2859c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2860a33eb0c4SMilanka Ringwald } 2861c5fa3c94SMilanka Ringwald 28620b4debbfSMilanka Ringwald if (hfp_connection->vra_state == HFP_VRA_VOICE_RECOGNITION_ACTIVATED){ 28630b4debbfSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 28640b4debbfSMilanka Ringwald } 28651a26de69SMilanka Ringwald 28660b4debbfSMilanka Ringwald uint8_t status = hfp_trigger_release_audio_connection(hfp_connection); 28670b4debbfSMilanka Ringwald if (status == ERROR_CODE_SUCCESS){ 2868f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 28690b4debbfSMilanka Ringwald } 28700b4debbfSMilanka Ringwald return status; 28713deb3ec6SMatthias Ringwald } 2872aa4dd815SMatthias Ringwald 2873aa4dd815SMatthias Ringwald /** 2874aa4dd815SMatthias Ringwald * @brief Enable in-band ring tone 2875aa4dd815SMatthias Ringwald */ 2876aa4dd815SMatthias Ringwald void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone){ 287740a8ee13SMatthias Ringwald if (has_in_band_ring_tone() == false){ 287840a8ee13SMatthias Ringwald return; 287940a8ee13SMatthias Ringwald } 288040a8ee13SMatthias Ringwald if (hfp_ag_in_band_ring_tone_active == use_in_band_ring_tone){ 2881aa4dd815SMatthias Ringwald return; 2882aa4dd815SMatthias Ringwald } 2883c5fa3c94SMilanka Ringwald 288440a8ee13SMatthias Ringwald hfp_ag_in_band_ring_tone_active = use_in_band_ring_tone; 2885aa4dd815SMatthias Ringwald 2886665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 2887665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 2888665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 2889a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 289066c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 28919430c71eSMatthias Ringwald hfp_connection->ag_send_in_band_ring_tone_setting = true; 2892f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 2893aa4dd815SMatthias Ringwald } 2894aa4dd815SMatthias Ringwald } 2895aa4dd815SMatthias Ringwald 2896aa4dd815SMatthias Ringwald /** 2897aa4dd815SMatthias Ringwald * @brief Called from GSM 2898aa4dd815SMatthias Ringwald */ 2899aa4dd815SMatthias Ringwald void hfp_ag_incoming_call(void){ 2900aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_INCOMING_CALL, NULL); 2901aa4dd815SMatthias Ringwald } 2902aa4dd815SMatthias Ringwald 29030f1da533SMatthias Ringwald void hfp_ag_outgoing_call_initiated(void) { 2904fe899794SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_INITIATED_BY_AG, NULL); 2905fe899794SMatthias Ringwald } 2906fe899794SMatthias Ringwald 2907aa4dd815SMatthias Ringwald /** 2908aa4dd815SMatthias Ringwald * @brief number is stored. 2909aa4dd815SMatthias Ringwald */ 2910aa4dd815SMatthias Ringwald void hfp_ag_set_clip(uint8_t type, const char * number){ 2911f8737b81SMatthias Ringwald hfp_gsm_handler(HFP_AG_SET_CLIP, 0, type, number); 2912aa4dd815SMatthias Ringwald } 2913aa4dd815SMatthias Ringwald 2914aa4dd815SMatthias Ringwald void hfp_ag_call_dropped(void){ 2915aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_CALL_DROPPED, NULL); 2916aa4dd815SMatthias Ringwald } 2917aa4dd815SMatthias Ringwald 2918aa4dd815SMatthias Ringwald // call from AG UI 2919aa4dd815SMatthias Ringwald void hfp_ag_answer_incoming_call(void){ 2920aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG, NULL); 2921aa4dd815SMatthias Ringwald } 2922aa4dd815SMatthias Ringwald 2923ce263fc8SMatthias Ringwald void hfp_ag_join_held_call(void){ 2924ce263fc8SMatthias Ringwald hfp_ag_call_sm(HFP_AG_HELD_CALL_JOINED_BY_AG, NULL); 2925ce263fc8SMatthias Ringwald } 2926ce263fc8SMatthias Ringwald 2927aa4dd815SMatthias Ringwald void hfp_ag_terminate_call(void){ 2928aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_TERMINATE_CALL_BY_AG, NULL); 2929aa4dd815SMatthias Ringwald } 2930aa4dd815SMatthias Ringwald 2931aa4dd815SMatthias Ringwald void hfp_ag_outgoing_call_ringing(void){ 2932aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_RINGING, NULL); 2933aa4dd815SMatthias Ringwald } 2934aa4dd815SMatthias Ringwald 2935aa4dd815SMatthias Ringwald void hfp_ag_outgoing_call_established(void){ 2936aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_ESTABLISHED, NULL); 2937aa4dd815SMatthias Ringwald } 2938aa4dd815SMatthias Ringwald 2939aa4dd815SMatthias Ringwald void hfp_ag_outgoing_call_rejected(void){ 2940aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_REJECTED, NULL); 2941aa4dd815SMatthias Ringwald } 2942aa4dd815SMatthias Ringwald 2943aa4dd815SMatthias Ringwald void hfp_ag_outgoing_call_accepted(void){ 2944aa4dd815SMatthias Ringwald hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_ACCEPTED, NULL); 2945aa4dd815SMatthias Ringwald } 2946ce263fc8SMatthias Ringwald 2947ce263fc8SMatthias Ringwald void hfp_ag_hold_incoming_call(void){ 2948ce263fc8SMatthias Ringwald hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG, NULL); 2949ce263fc8SMatthias Ringwald } 2950ce263fc8SMatthias Ringwald 2951ce263fc8SMatthias Ringwald void hfp_ag_accept_held_incoming_call(void) { 2952ce263fc8SMatthias Ringwald hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG, NULL); 2953ce263fc8SMatthias Ringwald } 2954ce263fc8SMatthias Ringwald 2955ce263fc8SMatthias Ringwald void hfp_ag_reject_held_incoming_call(void){ 2956ce263fc8SMatthias Ringwald hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG, NULL); 2957ce263fc8SMatthias Ringwald } 2958ce263fc8SMatthias Ringwald 2959aa4dd815SMatthias Ringwald static void hfp_ag_set_ag_indicator(const char * name, int value){ 2960aa4dd815SMatthias Ringwald int indicator_index = get_ag_indicator_index_for_name(name); 2961aa4dd815SMatthias Ringwald if (indicator_index < 0) return; 2962aa4dd815SMatthias Ringwald hfp_ag_indicators[indicator_index].status = value; 2963aa4dd815SMatthias Ringwald 2964665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it; 2965665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 2966665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 2967a0ffb263SMatthias Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 296866c5995fSMatthias Ringwald if (hfp_connection->local_role != HFP_ROLE_AG) continue; 2969a0ffb263SMatthias Ringwald if (!hfp_connection->ag_indicators[indicator_index].enabled) { 2970245852b7SMilanka Ringwald log_info("Requested AG indicator '%s' update to %u, but it is not enabled", hfp_ag_indicators[indicator_index].name, value); 2971ce263fc8SMatthias Ringwald continue; 2972ce263fc8SMatthias Ringwald } 2973245852b7SMilanka Ringwald log_info("AG indicator '%s' changed to %u, request transfer status", hfp_ag_indicators[indicator_index].name, value); 2974a0ffb263SMatthias Ringwald hfp_connection->ag_indicators_status_update_bitmap = store_bit(hfp_connection->ag_indicators_status_update_bitmap, indicator_index, 1); 2975f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 2976aa4dd815SMatthias Ringwald } 2977aa4dd815SMatthias Ringwald } 2978aa4dd815SMatthias Ringwald 2979c5fa3c94SMilanka Ringwald uint8_t hfp_ag_set_registration_status(int registration_status){ 2980c5fa3c94SMilanka Ringwald if ((registration_status < 0) || (registration_status > 1)){ 2981c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 2982c5fa3c94SMilanka Ringwald } 2983c5fa3c94SMilanka Ringwald 2984c5fa3c94SMilanka Ringwald if ( (registration_status == 0) && (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT)){ 2985245852b7SMilanka Ringwald 2986245852b7SMilanka Ringwald // if network goes away wihle a call is active: 2987245852b7SMilanka Ringwald // - the call gets dropped 2988245852b7SMilanka Ringwald // - we send NO CARRIER 2989245852b7SMilanka Ringwald // NOTE: the CALL=0 has to be sent before NO CARRIER 2990245852b7SMilanka Ringwald 2991245852b7SMilanka Ringwald hfp_ag_call_sm(HFP_AG_CALL_DROPPED, NULL); 2992245852b7SMilanka Ringwald 2993245852b7SMilanka Ringwald btstack_linked_list_iterator_t it; 2994245852b7SMilanka Ringwald btstack_linked_list_iterator_init(&it, hfp_get_connections()); 2995245852b7SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 2996245852b7SMilanka Ringwald hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it); 2997245852b7SMilanka Ringwald hfp_connection->ag_send_no_carrier = true; 2998245852b7SMilanka Ringwald } 2999245852b7SMilanka Ringwald } 3000c5fa3c94SMilanka Ringwald hfp_ag_set_ag_indicator("service", registration_status); 3001c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3002aa4dd815SMatthias Ringwald } 3003aa4dd815SMatthias Ringwald 3004c5fa3c94SMilanka Ringwald uint8_t hfp_ag_set_signal_strength(int signal_strength){ 3005c5fa3c94SMilanka Ringwald if ((signal_strength < 0) || (signal_strength > 5)){ 3006c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3007aa4dd815SMatthias Ringwald } 3008aa4dd815SMatthias Ringwald 3009c5fa3c94SMilanka Ringwald hfp_ag_set_ag_indicator("signal", signal_strength); 3010c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3011aa4dd815SMatthias Ringwald } 3012aa4dd815SMatthias Ringwald 3013c5fa3c94SMilanka Ringwald uint8_t hfp_ag_set_roaming_status(int roaming_status){ 3014c5fa3c94SMilanka Ringwald if ((roaming_status < 0) || (roaming_status > 1)){ 3015c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3016aa4dd815SMatthias Ringwald } 3017aa4dd815SMatthias Ringwald 3018c5fa3c94SMilanka Ringwald hfp_ag_set_ag_indicator("roam", roaming_status); 3019c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3020c5fa3c94SMilanka Ringwald } 3021c5fa3c94SMilanka Ringwald 3022c5fa3c94SMilanka Ringwald uint8_t hfp_ag_set_battery_level(int battery_level){ 3023c5fa3c94SMilanka Ringwald if ((battery_level < 0) || (battery_level > 5)){ 3024c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3025c5fa3c94SMilanka Ringwald } 3026c5fa3c94SMilanka Ringwald hfp_ag_set_ag_indicator("battchg", battery_level); 3027c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3028c5fa3c94SMilanka Ringwald } 3029c5fa3c94SMilanka Ringwald 30306b8275b0SMilanka Ringwald uint8_t hfp_ag_activate_voice_recognition(hci_con_handle_t acl_handle){ 303114c148caSMilanka Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 303214c148caSMilanka Ringwald if (!hfp_connection){ 303314c148caSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 30346b8275b0SMilanka Ringwald } 3035f71d2aacSMilanka Ringwald 303684fb9ac1SMilanka Ringwald if (hfp_connection->emit_vra_enabled_after_audio_established){ 303784fb9ac1SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 303884fb9ac1SMilanka Ringwald } 303984fb9ac1SMilanka Ringwald 30409cd26862SMilanka Ringwald bool enhanced_vra_supported = hfp_ag_enhanced_vra_flag_supported(hfp_connection); 30419cd26862SMilanka Ringwald bool legacy_vra_supported = hfp_ag_vra_flag_supported(hfp_connection); 3042f71d2aacSMilanka Ringwald if (!enhanced_vra_supported && !legacy_vra_supported){ 304314c148caSMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 30446b8275b0SMilanka Ringwald } 30456b8275b0SMilanka Ringwald 3046135d9718SMilanka Ringwald if (!hfp_ag_can_activate_voice_recognition(hfp_connection)){ 3047135d9718SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3048f71d2aacSMilanka Ringwald } 3049f71d2aacSMilanka Ringwald 3050f71d2aacSMilanka Ringwald hfp_connection->ag_activate_voice_recognition_value = 1; 3051f71d2aacSMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_VOICE_RECOGNITION_ACTIVATED; 3052fcf4ede6SMilanka Ringwald hfp_connection->enhanced_voice_recognition_enabled = enhanced_vra_supported; 3053f71d2aacSMilanka Ringwald hfp_connection->ag_audio_connection_opened_before_vra = hfp_ag_is_audio_connection_active(hfp_connection); 3054f71d2aacSMilanka Ringwald hfp_connection->ag_vra_state = HFP_VOICE_RECOGNITION_STATE_AG_READY; 30558f8818a4SMatthias Ringwald hfp_connection->ag_vra_send_command = true; 3056f71d2aacSMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 3057f71d2aacSMilanka Ringwald return ERROR_CODE_SUCCESS; 3058f71d2aacSMilanka Ringwald } 3059f71d2aacSMilanka Ringwald 3060d84719b1SMilanka Ringwald uint8_t hfp_ag_deactivate_voice_recognition(hci_con_handle_t acl_handle){ 306114c148caSMilanka Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 306214c148caSMilanka Ringwald if (!hfp_connection){ 306314c148caSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 306445796ff1SMilanka Ringwald } 3065f71d2aacSMilanka Ringwald 306684fb9ac1SMilanka Ringwald if (hfp_connection->emit_vra_enabled_after_audio_established){ 306784fb9ac1SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 306884fb9ac1SMilanka Ringwald } 306984fb9ac1SMilanka Ringwald 30709cd26862SMilanka Ringwald bool enhanced_vra_supported = hfp_ag_enhanced_vra_flag_supported(hfp_connection); 30719cd26862SMilanka Ringwald bool legacy_vra_supported = hfp_ag_vra_flag_supported(hfp_connection); 30729cd26862SMilanka Ringwald 3073f71d2aacSMilanka Ringwald if (!enhanced_vra_supported && !legacy_vra_supported){ 307414c148caSMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 307545796ff1SMilanka Ringwald } 307645796ff1SMilanka Ringwald 3077135d9718SMilanka Ringwald if (!hfp_ag_voice_recognition_session_active(hfp_connection)){ 3078135d9718SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3079f71d2aacSMilanka Ringwald } 3080f71d2aacSMilanka Ringwald 3081f71d2aacSMilanka Ringwald hfp_connection->ag_activate_voice_recognition_value = 0; 3082f71d2aacSMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_VOICE_RECOGNITION_OFF; 3083f71d2aacSMilanka Ringwald hfp_connection->ag_vra_state = HFP_VOICE_RECOGNITION_STATE_AG_READY; 30848f8818a4SMatthias Ringwald hfp_connection->ag_vra_send_command = true; 3085f71d2aacSMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 3086f71d2aacSMilanka Ringwald return ERROR_CODE_SUCCESS; 3087f71d2aacSMilanka Ringwald } 3088f71d2aacSMilanka Ringwald 3089cf75be85SMilanka Ringwald static uint8_t hfp_ag_enhanced_voice_recognition_send_state(hci_con_handle_t acl_handle, hfp_voice_recognition_state_t state){ 309045796ff1SMilanka Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 309145796ff1SMilanka Ringwald if (!hfp_connection){ 3092c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 309345796ff1SMilanka Ringwald } 3094c5fa3c94SMilanka Ringwald 309584fb9ac1SMilanka Ringwald if (hfp_connection->emit_vra_enabled_after_audio_established){ 309684fb9ac1SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3097f71d2aacSMilanka Ringwald } 3098f71d2aacSMilanka Ringwald 3099645bfa78SMilanka Ringwald if (hfp_connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED){ 3100645bfa78SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3101645bfa78SMilanka Ringwald } 3102645bfa78SMilanka Ringwald 3103645bfa78SMilanka Ringwald if (hfp_connection->vra_state != HFP_VRA_ENHANCED_VOICE_RECOGNITION_READY_FOR_AUDIO){ 3104135d9718SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 31059cd26862SMilanka Ringwald } 31069cd26862SMilanka Ringwald 310784fb9ac1SMilanka Ringwald bool enhanced_vra_supported = hfp_ag_enhanced_vra_flag_supported(hfp_connection); 310884fb9ac1SMilanka Ringwald if (!enhanced_vra_supported ){ 310984fb9ac1SMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 311084fb9ac1SMilanka Ringwald } 311184fb9ac1SMilanka Ringwald 311284fb9ac1SMilanka Ringwald 311345796ff1SMilanka Ringwald hfp_connection->ag_vra_state = state; 3114cf75be85SMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_ENHANCED_VOICE_RECOGNITION_STATUS; 31158f8818a4SMatthias Ringwald hfp_connection->ag_vra_send_command = true; 311645796ff1SMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 31179cd26862SMilanka Ringwald return ERROR_CODE_SUCCESS; 311845796ff1SMilanka Ringwald } 311945796ff1SMilanka Ringwald 3120f0c27a95SMilanka Ringwald uint8_t hfp_ag_enhanced_voice_recognition_report_sending_audio(hci_con_handle_t acl_handle){ 3121cf75be85SMilanka Ringwald return hfp_ag_enhanced_voice_recognition_send_state(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_STARTING_SOUND); 312245796ff1SMilanka Ringwald } 3123f0c27a95SMilanka Ringwald uint8_t hfp_ag_enhanced_voice_recognition_report_ready_for_audio(hci_con_handle_t acl_handle){ 3124cf75be85SMilanka Ringwald return hfp_ag_enhanced_voice_recognition_send_state(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT); 312545796ff1SMilanka Ringwald } 3126cf75be85SMilanka Ringwald uint8_t hfp_ag_enhanced_voice_recognition_report_processing_input(hci_con_handle_t acl_handle){ 3127cf75be85SMilanka Ringwald return hfp_ag_enhanced_voice_recognition_send_state(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_PROCESSING_AUDIO_INPUT); 312845796ff1SMilanka Ringwald } 312945796ff1SMilanka Ringwald 3130b95cac54SMilanka Ringwald 3131b95cac54SMilanka Ringwald uint8_t hfp_ag_enhanced_voice_recognition_send_message(hci_con_handle_t acl_handle, hfp_voice_recognition_state_t state, hfp_voice_recognition_message_t msg){ 3132b95cac54SMilanka Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3133b95cac54SMilanka Ringwald if (!hfp_connection){ 3134b95cac54SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3135b95cac54SMilanka Ringwald } 3136b95cac54SMilanka Ringwald 313784fb9ac1SMilanka Ringwald if (hfp_connection->emit_vra_enabled_after_audio_established){ 313884fb9ac1SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3139f71d2aacSMilanka Ringwald } 3140f71d2aacSMilanka Ringwald 3141645bfa78SMilanka Ringwald if (hfp_connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED){ 3142645bfa78SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3143645bfa78SMilanka Ringwald } 3144645bfa78SMilanka Ringwald 3145645bfa78SMilanka Ringwald if (hfp_connection->vra_state != HFP_VRA_ENHANCED_VOICE_RECOGNITION_READY_FOR_AUDIO){ 3146135d9718SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3147f71d2aacSMilanka Ringwald } 3148f71d2aacSMilanka Ringwald 314984fb9ac1SMilanka Ringwald bool enhanced_vra_supported = hfp_ag_enhanced_vra_flag_supported(hfp_connection); 315084fb9ac1SMilanka Ringwald if (!enhanced_vra_supported ){ 315184fb9ac1SMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 315284fb9ac1SMilanka Ringwald } 315384fb9ac1SMilanka Ringwald 315484fb9ac1SMilanka Ringwald bool enhanced_vra_msg_supported = hfp_ag_can_send_enhanced_vra_message_flag_supported(hfp_connection); 315584fb9ac1SMilanka Ringwald if (!enhanced_vra_msg_supported ){ 315684fb9ac1SMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 315784fb9ac1SMilanka Ringwald } 315884fb9ac1SMilanka Ringwald 3159ab2445a0SMatthias Ringwald uint16_t message_len = (uint16_t) strlen(msg.text); 316051aa5d5aSMilanka Ringwald 316189e7c136SMilanka Ringwald if (message_len > HFP_MAX_VR_TEXT_SIZE){ 31626504a03aSMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 31636504a03aSMilanka Ringwald } 31646504a03aSMilanka Ringwald 31656504a03aSMilanka Ringwald if ((HFP_VR_TEXT_HEADER_SIZE + message_len) > hfp_connection->rfcomm_mtu){ 316651aa5d5aSMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 316751aa5d5aSMilanka Ringwald } 316851aa5d5aSMilanka Ringwald 3169b95cac54SMilanka Ringwald hfp_connection->vra_state_requested = HFP_VRA_W2_SEND_ENHANCED_VOICE_RECOGNITION_MSG; 3170b95cac54SMilanka Ringwald hfp_connection->ag_msg = msg; 3171b95cac54SMilanka Ringwald hfp_connection->ag_vra_state = state; 31728f8818a4SMatthias Ringwald hfp_connection->ag_vra_send_command = true; 3173b95cac54SMilanka Ringwald hfp_ag_run_for_context(hfp_connection); 3174f71d2aacSMilanka Ringwald 3175f71d2aacSMilanka Ringwald return ERROR_CODE_SUCCESS; 3176b95cac54SMilanka Ringwald } 3177b95cac54SMilanka Ringwald 3178b95cac54SMilanka Ringwald 3179c5fa3c94SMilanka Ringwald uint8_t hfp_ag_set_microphone_gain(hci_con_handle_t acl_handle, int gain){ 31809c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3181a33eb0c4SMilanka Ringwald if (!hfp_connection){ 3182c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3183a33eb0c4SMilanka Ringwald } 3184c5fa3c94SMilanka Ringwald 3185c5fa3c94SMilanka Ringwald if ((gain < 0) || (gain > 15)){ 3186c5fa3c94SMilanka Ringwald log_info("Valid range for a gain is [0..15]. Currently sent: %d", gain); 3187c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3188c5fa3c94SMilanka Ringwald } 3189c5fa3c94SMilanka Ringwald 3190a0ffb263SMatthias Ringwald if (hfp_connection->microphone_gain != gain){ 3191a0ffb263SMatthias Ringwald hfp_connection->microphone_gain = gain; 3192a0ffb263SMatthias Ringwald hfp_connection->send_microphone_gain = 1; 3193aa4dd815SMatthias Ringwald } 3194f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 3195c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3196aa4dd815SMatthias Ringwald } 3197aa4dd815SMatthias Ringwald 3198c5fa3c94SMilanka Ringwald uint8_t hfp_ag_set_speaker_gain(hci_con_handle_t acl_handle, int gain){ 31999c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3200a33eb0c4SMilanka Ringwald if (!hfp_connection){ 3201c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3202a33eb0c4SMilanka Ringwald } 3203c5fa3c94SMilanka Ringwald 3204c5fa3c94SMilanka Ringwald if ((gain < 0) || (gain > 15)){ 3205c5fa3c94SMilanka Ringwald log_info("Valid range for a gain is [0..15]. Currently sent: %d", gain); 3206c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3207c5fa3c94SMilanka Ringwald } 3208c5fa3c94SMilanka Ringwald 3209a0ffb263SMatthias Ringwald if (hfp_connection->speaker_gain != gain){ 3210a0ffb263SMatthias Ringwald hfp_connection->speaker_gain = gain; 3211a0ffb263SMatthias Ringwald hfp_connection->send_speaker_gain = 1; 3212aa4dd815SMatthias Ringwald } 3213f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 3214c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3215aa4dd815SMatthias Ringwald } 3216aa4dd815SMatthias Ringwald 3217c5fa3c94SMilanka Ringwald uint8_t hfp_ag_send_phone_number_for_voice_tag(hci_con_handle_t acl_handle, const char * number){ 32189c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3219a33eb0c4SMilanka Ringwald if (!hfp_connection){ 3220c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3221a33eb0c4SMilanka Ringwald } 3222c5fa3c94SMilanka Ringwald 3223aa4dd815SMatthias Ringwald hfp_ag_set_clip(0, number); 3224a0ffb263SMatthias Ringwald hfp_connection->send_phone_number_for_voice_tag = 1; 3225c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3226aa4dd815SMatthias Ringwald } 3227aa4dd815SMatthias Ringwald 3228c5fa3c94SMilanka Ringwald uint8_t hfp_ag_reject_phone_number_for_voice_tag(hci_con_handle_t acl_handle){ 32299c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3230a33eb0c4SMilanka Ringwald if (!hfp_connection){ 3231c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3232a33eb0c4SMilanka Ringwald } 3233c5fa3c94SMilanka Ringwald 3234e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 3235c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3236aa4dd815SMatthias Ringwald } 3237aa4dd815SMatthias Ringwald 3238c5fa3c94SMilanka Ringwald uint8_t hfp_ag_send_dtmf_code_done(hci_con_handle_t acl_handle){ 32399c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3240a33eb0c4SMilanka Ringwald if (!hfp_connection){ 3241c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3242a33eb0c4SMilanka Ringwald } 3243c5fa3c94SMilanka Ringwald 3244e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 3245c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3246c1797c7dSMatthias Ringwald } 3247aa4dd815SMatthias Ringwald 32485be6b47aSMatthias Ringwald uint8_t hfp_ag_send_unsolicited_result_code(hci_con_handle_t acl_handle, const char * unsolicited_result_code){ 32495be6b47aSMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 32505be6b47aSMatthias Ringwald if (!hfp_connection){ 32515be6b47aSMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 32525be6b47aSMatthias Ringwald } 32535be6b47aSMatthias Ringwald if (hfp_connection->send_custom_message != NULL){ 32545be6b47aSMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 32555be6b47aSMatthias Ringwald } 32565be6b47aSMatthias Ringwald hfp_connection->send_custom_message = unsolicited_result_code; 32575be6b47aSMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 32585be6b47aSMatthias Ringwald return ERROR_CODE_SUCCESS; 32595be6b47aSMatthias Ringwald } 32605be6b47aSMatthias Ringwald 3261471dea41SMatthias Ringwald uint8_t hfp_ag_send_command_result_code(hci_con_handle_t acl_handle, bool ok){ 3262471dea41SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3263471dea41SMatthias Ringwald if (!hfp_connection){ 3264471dea41SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3265471dea41SMatthias Ringwald } 3266471dea41SMatthias Ringwald if (ok){ 3267e84fa067SMatthias Ringwald hfp_ag_queue_ok(hfp_connection); 3268471dea41SMatthias Ringwald } else { 3269e84fa067SMatthias Ringwald hfp_ag_queue_error(hfp_connection); 3270471dea41SMatthias Ringwald } 3271471dea41SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 3272471dea41SMatthias Ringwald return ERROR_CODE_SUCCESS; 3273471dea41SMatthias Ringwald } 3274471dea41SMatthias Ringwald 3275ce263fc8SMatthias Ringwald void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count){ 3276aeb0f0feSMatthias Ringwald hfp_ag_subscriber_numbers = numbers; 3277aeb0f0feSMatthias Ringwald hfp_ag_subscriber_numbers_count = numbers_count; 3278ce263fc8SMatthias Ringwald } 3279ce263fc8SMatthias Ringwald 32809cae807eSMatthias Ringwald void hfp_ag_clear_last_dialed_number(void){ 32819cae807eSMatthias Ringwald hfp_gsm_clear_last_dialed_number(); 3282ce263fc8SMatthias Ringwald } 3283ce263fc8SMatthias Ringwald 32849de679b7SMilanka Ringwald void hfp_ag_set_last_dialed_number(const char * number){ 32859de679b7SMilanka Ringwald hfp_gsm_set_last_dialed_number(number); 32869de679b7SMilanka Ringwald } 32879de679b7SMilanka Ringwald 3288c5fa3c94SMilanka Ringwald uint8_t hfp_ag_notify_incoming_call_waiting(hci_con_handle_t acl_handle){ 32899c9c64c1SMatthias Ringwald hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); 3290a33eb0c4SMilanka Ringwald if (!hfp_connection){ 3291c5fa3c94SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3292a33eb0c4SMilanka Ringwald } 3293c5fa3c94SMilanka Ringwald 3294c5fa3c94SMilanka Ringwald if (!hfp_connection->call_waiting_notification_enabled){ 3295c5fa3c94SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 3296c5fa3c94SMilanka Ringwald } 3297a0ffb263SMatthias Ringwald 3298a0ffb263SMatthias Ringwald hfp_connection->ag_notify_incoming_call_waiting = 1; 3299f0aeb307SMatthias Ringwald hfp_ag_run_for_context(hfp_connection); 3300c5fa3c94SMilanka Ringwald return ERROR_CODE_SUCCESS; 3301a0ffb263SMatthias Ringwald } 3302aa10b9cbSMatthias Ringwald void hfp_ag_create_sdp_record_with_codecs(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, 3303aa10b9cbSMatthias Ringwald const char * name, uint8_t ability_to_reject_call, uint16_t supported_features, 3304aa10b9cbSMatthias Ringwald uint8_t codecs_nr, const uint8_t * codecs){ 330576cc1527SMatthias Ringwald if (!name){ 3306aeb0f0feSMatthias Ringwald name = hfp_ag_default_service_name; 330776cc1527SMatthias Ringwald } 330876cc1527SMatthias Ringwald hfp_create_sdp_record(service, service_record_handle, BLUETOOTH_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY, rfcomm_channel_nr, name); 330976cc1527SMatthias Ringwald 331076cc1527SMatthias Ringwald /* 331176cc1527SMatthias Ringwald * 0x01 – Ability to reject a call 331276cc1527SMatthias Ringwald * 0x00 – No ability to reject a call 331376cc1527SMatthias Ringwald */ 331476cc1527SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0301); // Hands-Free Profile - Network 331576cc1527SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_8, ability_to_reject_call); 331676cc1527SMatthias Ringwald 331776cc1527SMatthias Ringwald // Construct SupportedFeatures for SDP bitmap: 331876cc1527SMatthias Ringwald // 331976cc1527SMatthias Ringwald // "The values of the “SupportedFeatures” bitmap given in Table 5.4 shall be the same as the values 332076cc1527SMatthias Ringwald // of the Bits 0 to 4 of the unsolicited result code +BRSF" 332176cc1527SMatthias Ringwald // 3322aa10b9cbSMatthias Ringwald // Wide band speech (bit 5) and LC3-SWB (bit 8) require Codec negotiation 332376cc1527SMatthias Ringwald // 332476cc1527SMatthias Ringwald uint16_t sdp_features = supported_features & 0x1f; 3325ef3ae4ebSMilanka Ringwald 3326ef3ae4ebSMilanka Ringwald if (supported_features & (1 << HFP_AGSF_ENHANCED_VOICE_RECOGNITION_STATUS)){ 3327ef3ae4ebSMilanka Ringwald sdp_features |= 1 << 6; 3328ef3ae4ebSMilanka Ringwald } 3329ef3ae4ebSMilanka Ringwald 3330ef3ae4ebSMilanka Ringwald if (supported_features & (1 << HFP_AGSF_VOICE_RECOGNITION_TEXT)){ 3331ef3ae4ebSMilanka Ringwald sdp_features |= 1 << 7; 3332ef3ae4ebSMilanka Ringwald } 3333ef3ae4ebSMilanka Ringwald 3334aa10b9cbSMatthias Ringwald // codecs 3335aa10b9cbSMatthias Ringwald if ((supported_features & (1 << HFP_HFSF_CODEC_NEGOTIATION)) != 0){ 3336aa10b9cbSMatthias Ringwald uint8_t i; 3337aa10b9cbSMatthias Ringwald for (i=0;i<codecs_nr;i++){ 3338aa10b9cbSMatthias Ringwald switch (codecs[i]){ 3339aa10b9cbSMatthias Ringwald case HFP_CODEC_MSBC: 3340aa10b9cbSMatthias Ringwald sdp_features |= 1 << 5; 3341aa10b9cbSMatthias Ringwald break; 3342aa10b9cbSMatthias Ringwald case HFP_CODEC_LC3_SWB: 3343aa10b9cbSMatthias Ringwald sdp_features |= 1 << 8; 3344aa10b9cbSMatthias Ringwald break; 334575389f80SMatthias Ringwald default: 334675389f80SMatthias Ringwald break; 3347aa10b9cbSMatthias Ringwald } 3348aa10b9cbSMatthias Ringwald } 3349aa10b9cbSMatthias Ringwald } 3350aa10b9cbSMatthias Ringwald 335176cc1527SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); // Hands-Free Profile - SupportedFeatures 335276cc1527SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, sdp_features); 335376cc1527SMatthias Ringwald } 335476cc1527SMatthias Ringwald 3355aa10b9cbSMatthias Ringwald void hfp_ag_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name, 3356aa10b9cbSMatthias Ringwald uint8_t ability_to_reject_call, uint16_t supported_features, int wide_band_speech){ 3357aa10b9cbSMatthias Ringwald uint8_t codecs_nr; 3358aa10b9cbSMatthias Ringwald const uint8_t * codecs; 3359aa10b9cbSMatthias Ringwald const uint8_t wide_band_codecs[] = { HFP_CODEC_MSBC }; 3360aa10b9cbSMatthias Ringwald if (wide_band_speech == 0){ 3361aa10b9cbSMatthias Ringwald codecs_nr = 0; 3362aa10b9cbSMatthias Ringwald codecs = NULL; 3363aa10b9cbSMatthias Ringwald } else { 3364aa10b9cbSMatthias Ringwald codecs_nr = 1; 3365aa10b9cbSMatthias Ringwald codecs = wide_band_codecs; 3366aa10b9cbSMatthias Ringwald } 3367aa10b9cbSMatthias Ringwald hfp_ag_create_sdp_record_with_codecs(service, service_record_handle, rfcomm_channel_nr, name, 3368aa10b9cbSMatthias Ringwald ability_to_reject_call, supported_features, codecs_nr, codecs); 3369aa10b9cbSMatthias Ringwald } 3370aa10b9cbSMatthias Ringwald 337176cc1527SMatthias Ringwald void hfp_ag_register_packet_handler(btstack_packet_handler_t callback){ 337268466199SMilanka Ringwald btstack_assert(callback != NULL); 337368466199SMilanka Ringwald 337476cc1527SMatthias Ringwald hfp_ag_callback = callback; 337576cc1527SMatthias Ringwald hfp_set_ag_callback(callback); 337676cc1527SMatthias Ringwald } 337781e25d0aSMatthias Ringwald 337881e25d0aSMatthias Ringwald void hfp_ag_register_custom_call_sm_handler(bool (*handler)(hfp_ag_call_event_t event)){ 337981e25d0aSMatthias Ringwald hfp_ag_custom_call_sm_handler = handler; 338081e25d0aSMatthias Ringwald } 3381471dea41SMatthias Ringwald 3382471dea41SMatthias Ringwald void hfp_ag_register_custom_at_command(hfp_custom_at_command_t * custom_at_command){ 3383471dea41SMatthias Ringwald hfp_register_custom_ag_command(custom_at_command); 3384471dea41SMatthias Ringwald } 3385