xref: /btstack/src/classic/hfp_gsm_model.c (revision 66a048ab906a04706fe3948e52f9291ec7110b9c)
174386ee0SMatthias Ringwald /*
274386ee0SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
374386ee0SMatthias Ringwald  *
474386ee0SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
574386ee0SMatthias Ringwald  * modification, are permitted provided that the following conditions
674386ee0SMatthias Ringwald  * are met:
774386ee0SMatthias Ringwald  *
874386ee0SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
974386ee0SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
1074386ee0SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1174386ee0SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
1274386ee0SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
1374386ee0SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
1474386ee0SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
1574386ee0SMatthias Ringwald  *    from this software without specific prior written permission.
1674386ee0SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
1774386ee0SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
1874386ee0SMatthias Ringwald  *    monetary gain.
1974386ee0SMatthias Ringwald  *
2074386ee0SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2174386ee0SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2274386ee0SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2374386ee0SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
2474386ee0SMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2574386ee0SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2674386ee0SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2774386ee0SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2874386ee0SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2974386ee0SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3074386ee0SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3174386ee0SMatthias Ringwald  * SUCH DAMAGE.
3274386ee0SMatthias Ringwald  *
3374386ee0SMatthias Ringwald  * Please inquire about commercial licensing options at
3474386ee0SMatthias Ringwald  * [email protected]
3574386ee0SMatthias Ringwald  *
3674386ee0SMatthias Ringwald  */
3774386ee0SMatthias Ringwald 
3874386ee0SMatthias Ringwald // *****************************************************************************
3974386ee0SMatthias Ringwald //
409cae807eSMatthias Ringwald // GSM Model
4174386ee0SMatthias Ringwald //
4274386ee0SMatthias Ringwald // *****************************************************************************
4374386ee0SMatthias Ringwald 
447907f069SMatthias Ringwald #include "btstack_config.h"
4574386ee0SMatthias Ringwald 
4674386ee0SMatthias Ringwald #include <stdint.h>
4774386ee0SMatthias Ringwald #include <stdio.h>
4874386ee0SMatthias Ringwald #include <stdlib.h>
4974386ee0SMatthias Ringwald #include <string.h>
5074386ee0SMatthias Ringwald 
5174386ee0SMatthias Ringwald #include "btstack_memory.h"
5274386ee0SMatthias Ringwald #include "classic/hfp.h"
5374386ee0SMatthias Ringwald #include "classic/hfp_gsm_model.h"
54746ccb7eSMatthias Ringwald #include "classic/sdp_server.h"
5574386ee0SMatthias Ringwald #include "classic/sdp_query_rfcomm.h"
5616ece135SMatthias Ringwald #include "btstack_debug.h"
5774386ee0SMatthias Ringwald #include "hci.h"
5856042629SMatthias Ringwald #include "hci_cmd.h"
5974386ee0SMatthias Ringwald #include "hci_dump.h"
6074386ee0SMatthias Ringwald #include "l2cap.h"
6182636622SMatthias Ringwald #include "btstack_run_loop.h"
6274386ee0SMatthias Ringwald 
6374386ee0SMatthias Ringwald #define HFP_GSM_MAX_NR_CALLS 3
649cae807eSMatthias Ringwald #define HFP_GSM_MAX_CALL_NUMBER_SIZE 25
6574386ee0SMatthias Ringwald 
6674386ee0SMatthias Ringwald static hfp_gsm_call_t gsm_calls[HFP_GSM_MAX_NR_CALLS];
6774386ee0SMatthias Ringwald static hfp_callsetup_status_t callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
6874386ee0SMatthias Ringwald 
69d0c20769SMatthias Ringwald static uint8_t clip_type;
709cae807eSMatthias Ringwald static char clip_number[HFP_GSM_MAX_CALL_NUMBER_SIZE];
719cae807eSMatthias Ringwald static char last_dialed_number[HFP_GSM_MAX_CALL_NUMBER_SIZE];
72d0c20769SMatthias Ringwald 
73d0c20769SMatthias Ringwald static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t type, const char * number);
74*66a048abSMatthias Ringwald static inline int get_number_active_calls(void);
75*66a048abSMatthias Ringwald 
76*66a048abSMatthias Ringwald static void set_callsetup_status(hfp_callsetup_status_t status){
77*66a048abSMatthias Ringwald     callsetup_status = status;
78*66a048abSMatthias Ringwald     if (callsetup_status != HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE) return;
79*66a048abSMatthias Ringwald 
80*66a048abSMatthias Ringwald     int i ;
81*66a048abSMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
82*66a048abSMatthias Ringwald         if (gsm_calls[i].direction == HFP_ENHANCED_CALL_DIR_OUTGOING){
83*66a048abSMatthias Ringwald             gsm_calls[i].enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING;
84*66a048abSMatthias Ringwald         }
85*66a048abSMatthias Ringwald     }
86*66a048abSMatthias Ringwald }
87*66a048abSMatthias Ringwald 
88*66a048abSMatthias Ringwald static inline void set_enhanced_call_status_active(int index_in_table){
89*66a048abSMatthias Ringwald     gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_ACTIVE;
90*66a048abSMatthias Ringwald     gsm_calls[index_in_table].used_slot = 1;
91*66a048abSMatthias Ringwald }
92*66a048abSMatthias Ringwald 
93*66a048abSMatthias Ringwald static inline void set_enhanced_call_status_held(int index_in_table){
94*66a048abSMatthias Ringwald     gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_HELD;
95*66a048abSMatthias Ringwald     gsm_calls[index_in_table].used_slot = 1;
96*66a048abSMatthias Ringwald }
97*66a048abSMatthias Ringwald 
98*66a048abSMatthias Ringwald static inline void set_enhanced_call_status_response_hold(int index_in_table){
99*66a048abSMatthias Ringwald     gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD;
100*66a048abSMatthias Ringwald     gsm_calls[index_in_table].used_slot = 1;
101*66a048abSMatthias Ringwald }
102*66a048abSMatthias Ringwald 
103*66a048abSMatthias Ringwald static inline void set_enhanced_call_status_initiated(int index_in_table){
104*66a048abSMatthias Ringwald     if (gsm_calls[index_in_table].direction == HFP_ENHANCED_CALL_DIR_OUTGOING){
105*66a048abSMatthias Ringwald         gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING;
106*66a048abSMatthias Ringwald     } else {
107*66a048abSMatthias Ringwald         if (get_number_active_calls() > 0){
108*66a048abSMatthias Ringwald             gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING;
109*66a048abSMatthias Ringwald         } else {
110*66a048abSMatthias Ringwald             gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING;
111*66a048abSMatthias Ringwald         }
112*66a048abSMatthias Ringwald     }
113*66a048abSMatthias Ringwald     gsm_calls[index_in_table].used_slot = 1;
114*66a048abSMatthias Ringwald }
115*66a048abSMatthias Ringwald 
116*66a048abSMatthias Ringwald static int get_enhanced_call_status(int index_in_table){
117*66a048abSMatthias Ringwald     if (!gsm_calls[index_in_table].used_slot) return -1;
118*66a048abSMatthias Ringwald     return gsm_calls[index_in_table].enhanced_status;
119*66a048abSMatthias Ringwald }
120*66a048abSMatthias Ringwald 
121*66a048abSMatthias Ringwald static inline int is_enhanced_call_status_active(int index_in_table){
122*66a048abSMatthias Ringwald     return get_enhanced_call_status(index_in_table) == HFP_ENHANCED_CALL_STATUS_ACTIVE;
123*66a048abSMatthias Ringwald }
124*66a048abSMatthias Ringwald 
125*66a048abSMatthias Ringwald static inline int is_enhanced_call_status_initiated(int index_in_table){
126*66a048abSMatthias Ringwald     switch (get_enhanced_call_status(index_in_table)){
127*66a048abSMatthias Ringwald         case HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING:
128*66a048abSMatthias Ringwald         case HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING:
129*66a048abSMatthias Ringwald         case HFP_ENHANCED_CALL_STATUS_INCOMING:
130*66a048abSMatthias Ringwald         case HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING:
131*66a048abSMatthias Ringwald             return 1;
132*66a048abSMatthias Ringwald         default:
133*66a048abSMatthias Ringwald             return 0;
134*66a048abSMatthias Ringwald     }
135*66a048abSMatthias Ringwald }
136*66a048abSMatthias Ringwald 
137*66a048abSMatthias Ringwald static void free_call_slot(int index_in_table){
138*66a048abSMatthias Ringwald     gsm_calls[index_in_table].used_slot = 0;
139*66a048abSMatthias Ringwald }
140d0c20769SMatthias Ringwald 
141d210d9c4SMatthias Ringwald void hfp_gsm_init(void){
142*66a048abSMatthias Ringwald     set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
143d0c20769SMatthias Ringwald     clip_type = 0;
144d0c20769SMatthias Ringwald     memset(clip_number, 0, sizeof(clip_number));
1459cae807eSMatthias Ringwald     memset(last_dialed_number, 0, sizeof(last_dialed_number));
146d210d9c4SMatthias Ringwald     memset(gsm_calls, 0, sizeof(gsm_calls));
147d210d9c4SMatthias Ringwald     int i;
148d210d9c4SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
149*66a048abSMatthias Ringwald         free_call_slot(i);
150d210d9c4SMatthias Ringwald     }
151d210d9c4SMatthias Ringwald }
15274386ee0SMatthias Ringwald 
153*66a048abSMatthias Ringwald static int get_number_calls_with_enhanced_status(hfp_enhanced_call_status_t enhanced_status){
15474386ee0SMatthias Ringwald     int i, count = 0;
15574386ee0SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
156*66a048abSMatthias Ringwald         if (get_enhanced_call_status(i) == enhanced_status) count++;
15774386ee0SMatthias Ringwald     }
15874386ee0SMatthias Ringwald     return count;
15974386ee0SMatthias Ringwald }
16074386ee0SMatthias Ringwald 
161*66a048abSMatthias Ringwald static int get_call_index_with_enhanced_status(hfp_enhanced_call_status_t enhanced_status){
16274386ee0SMatthias Ringwald     int i ;
16374386ee0SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
164*66a048abSMatthias Ringwald         if (get_enhanced_call_status(i) == enhanced_status) return i;
165*66a048abSMatthias Ringwald     }
166*66a048abSMatthias Ringwald     return -1;
167*66a048abSMatthias Ringwald }
168*66a048abSMatthias Ringwald 
169*66a048abSMatthias Ringwald static inline int get_initiated_call_index(void){
170*66a048abSMatthias Ringwald     int i ;
171*66a048abSMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
172*66a048abSMatthias Ringwald         if (is_enhanced_call_status_initiated(i)) return i;
17374386ee0SMatthias Ringwald     }
17474386ee0SMatthias Ringwald     return -1;
17574386ee0SMatthias Ringwald }
17674386ee0SMatthias Ringwald 
1779cae807eSMatthias Ringwald static inline int get_next_free_slot(void){
178*66a048abSMatthias Ringwald     int i ;
179*66a048abSMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
180*66a048abSMatthias Ringwald         if (!gsm_calls[i].used_slot) return i;
181*66a048abSMatthias Ringwald     }
182*66a048abSMatthias Ringwald     return -1;
18374386ee0SMatthias Ringwald }
18474386ee0SMatthias Ringwald 
1859cae807eSMatthias Ringwald static inline int get_active_call_index(void){
186*66a048abSMatthias Ringwald     return get_call_index_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_ACTIVE);
187d210d9c4SMatthias Ringwald }
188d210d9c4SMatthias Ringwald 
1899cae807eSMatthias Ringwald static inline int get_held_call_index(void){
190*66a048abSMatthias Ringwald     return get_call_index_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_HELD);
191d210d9c4SMatthias Ringwald }
192d210d9c4SMatthias Ringwald 
1939cae807eSMatthias Ringwald static inline int get_response_held_call_index(void){
194*66a048abSMatthias Ringwald     return get_call_index_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD);
195d210d9c4SMatthias Ringwald }
196d210d9c4SMatthias Ringwald 
1979cae807eSMatthias Ringwald static inline int get_number_none_calls(void){
198*66a048abSMatthias Ringwald     int i, count = 0;
199*66a048abSMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
200*66a048abSMatthias Ringwald         if (!gsm_calls[i].used_slot) count++;
201*66a048abSMatthias Ringwald     }
202*66a048abSMatthias Ringwald     return count;
203d210d9c4SMatthias Ringwald }
20474386ee0SMatthias Ringwald 
2059cae807eSMatthias Ringwald static inline int get_number_active_calls(void){
206*66a048abSMatthias Ringwald     return get_number_calls_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_ACTIVE);
20774386ee0SMatthias Ringwald }
20874386ee0SMatthias Ringwald 
2099cae807eSMatthias Ringwald static inline int get_number_held_calls(void){
210*66a048abSMatthias Ringwald     return get_number_calls_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_HELD);
21174386ee0SMatthias Ringwald }
21274386ee0SMatthias Ringwald 
2139cae807eSMatthias Ringwald static inline int get_number_response_held_calls(void){
214*66a048abSMatthias Ringwald     return get_number_calls_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD);
215d210d9c4SMatthias Ringwald }
216d210d9c4SMatthias Ringwald 
2179cae807eSMatthias Ringwald static int next_call_index(void){
218d0c20769SMatthias Ringwald     return HFP_GSM_MAX_NR_CALLS + 1 - get_number_none_calls();
219d0c20769SMatthias Ringwald }
220d0c20769SMatthias Ringwald 
221d0c20769SMatthias Ringwald static void hfp_gsm_set_clip(int index_in_table, uint8_t type, const char * number){
2229cae807eSMatthias Ringwald     if (strlen(number) == 0) return;
2239cae807eSMatthias Ringwald 
224d0c20769SMatthias Ringwald     gsm_calls[index_in_table].clip_type = type;
225d0c20769SMatthias Ringwald 
2269cae807eSMatthias Ringwald     int clip_number_size = strlen(number) < HFP_GSM_MAX_CALL_NUMBER_SIZE ? strlen(number) : HFP_GSM_MAX_CALL_NUMBER_SIZE-1;
227d0c20769SMatthias Ringwald     strncpy(gsm_calls[index_in_table].clip_number, number, clip_number_size);
2289cae807eSMatthias Ringwald     gsm_calls[index_in_table].clip_number[clip_number_size] = '\0';
2299cae807eSMatthias Ringwald     strncpy(last_dialed_number, number, clip_number_size);
2309cae807eSMatthias Ringwald     last_dialed_number[clip_number_size] = '\0';
2319cae807eSMatthias Ringwald 
2329cae807eSMatthias Ringwald     clip_type = 0;
2339cae807eSMatthias Ringwald     memset(clip_number, 0, sizeof(clip_number));
234d0c20769SMatthias Ringwald }
235d0c20769SMatthias Ringwald 
236d0c20769SMatthias Ringwald static void delete_call(int delete_index_in_table){
237d0c20769SMatthias Ringwald     int i ;
238d0c20769SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
239d0c20769SMatthias Ringwald         if (gsm_calls[i].index > gsm_calls[delete_index_in_table].index){
240d0c20769SMatthias Ringwald             gsm_calls[i].index--;
241d0c20769SMatthias Ringwald         }
242d0c20769SMatthias Ringwald     }
243*66a048abSMatthias Ringwald     free_call_slot(delete_index_in_table);
244d0c20769SMatthias Ringwald 
245d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].clip_type = 0;
246d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].index = 0;
247d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].clip_number[0] = '\0';
2489cae807eSMatthias Ringwald     gsm_calls[delete_index_in_table].mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL;
249d0c20769SMatthias Ringwald }
250d0c20769SMatthias Ringwald 
2519cae807eSMatthias Ringwald 
2529cae807eSMatthias Ringwald static void create_call(hfp_enhanced_call_dir_t direction){
253d0c20769SMatthias Ringwald     int next_free_slot = get_next_free_slot();
2549cae807eSMatthias Ringwald     gsm_calls[next_free_slot].direction = direction;
255d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].index = next_call_index();
256*66a048abSMatthias Ringwald     set_enhanced_call_status_initiated(next_free_slot);
257d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].clip_type = 0;
258d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].clip_number[0] = '\0';
2599cae807eSMatthias Ringwald     gsm_calls[next_free_slot].mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL;
260d0c20769SMatthias Ringwald 
261d0c20769SMatthias Ringwald     hfp_gsm_set_clip(next_free_slot, clip_type, clip_number);
262d0c20769SMatthias Ringwald }
263d0c20769SMatthias Ringwald 
2649cae807eSMatthias Ringwald 
2659cae807eSMatthias Ringwald int hfp_gsm_get_number_of_calls(void){
2669cae807eSMatthias Ringwald     return HFP_GSM_MAX_NR_CALLS - get_number_none_calls();
2679cae807eSMatthias Ringwald }
2689cae807eSMatthias Ringwald 
2699cae807eSMatthias Ringwald void hfp_gsm_clear_last_dialed_number(void){
2709cae807eSMatthias Ringwald     memset(last_dialed_number, 0, sizeof(last_dialed_number));
2719cae807eSMatthias Ringwald }
2729cae807eSMatthias Ringwald 
2739cae807eSMatthias Ringwald char * hfp_gsm_last_dialed_number(void){
2749cae807eSMatthias Ringwald     return &last_dialed_number[0];
2759cae807eSMatthias Ringwald }
2769cae807eSMatthias Ringwald 
2779cae807eSMatthias Ringwald hfp_gsm_call_t * hfp_gsm_call(int call_index){
2789cae807eSMatthias Ringwald     int i;
2799cae807eSMatthias Ringwald 
2809cae807eSMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
2819cae807eSMatthias Ringwald         hfp_gsm_call_t * call = &gsm_calls[i];
2829cae807eSMatthias Ringwald         if (call->index != call_index) continue;
2839cae807eSMatthias Ringwald         return call;
2849cae807eSMatthias Ringwald     }
2859cae807eSMatthias Ringwald     return NULL;
2869cae807eSMatthias Ringwald }
2879cae807eSMatthias Ringwald 
2889cae807eSMatthias Ringwald uint8_t hfp_gsm_clip_type(void){
289d0c20769SMatthias Ringwald     if (clip_type != 0) return clip_type;
290d0c20769SMatthias Ringwald 
291d0c20769SMatthias Ringwald     int initiated_call_index = get_initiated_call_index();
292d0c20769SMatthias Ringwald     if (initiated_call_index != -1){
293d0c20769SMatthias Ringwald         if (gsm_calls[initiated_call_index].clip_type != 0) {
294d0c20769SMatthias Ringwald             return gsm_calls[initiated_call_index].clip_type;
295d0c20769SMatthias Ringwald         }
296d0c20769SMatthias Ringwald     }
297d0c20769SMatthias Ringwald 
298d0c20769SMatthias Ringwald     int active_call_index = get_active_call_index();
299d0c20769SMatthias Ringwald     if (active_call_index != -1){
300d0c20769SMatthias Ringwald         if (gsm_calls[active_call_index].clip_type != 0) {
301d0c20769SMatthias Ringwald             return gsm_calls[active_call_index].clip_type;
302d0c20769SMatthias Ringwald         }
303d0c20769SMatthias Ringwald     }
304d0c20769SMatthias Ringwald     return 0;
305d0c20769SMatthias Ringwald }
306d0c20769SMatthias Ringwald 
3079cae807eSMatthias Ringwald char *  hfp_gsm_clip_number(void){
3089cae807eSMatthias Ringwald     if (strlen(clip_number) != 0) return clip_number;
309d0c20769SMatthias Ringwald 
310d0c20769SMatthias Ringwald     int initiated_call_index = get_initiated_call_index();
311d0c20769SMatthias Ringwald     if (initiated_call_index != -1){
312d0c20769SMatthias Ringwald         if (gsm_calls[initiated_call_index].clip_type != 0) {
313d0c20769SMatthias Ringwald             return gsm_calls[initiated_call_index].clip_number;
314d0c20769SMatthias Ringwald         }
315d0c20769SMatthias Ringwald     }
316d0c20769SMatthias Ringwald 
317d0c20769SMatthias Ringwald     int active_call_index = get_active_call_index();
318d0c20769SMatthias Ringwald     if (active_call_index != -1){
319d0c20769SMatthias Ringwald         if (gsm_calls[active_call_index].clip_type != 0) {
320d0c20769SMatthias Ringwald             return gsm_calls[active_call_index].clip_number;
321d0c20769SMatthias Ringwald         }
322d0c20769SMatthias Ringwald     }
323d0c20769SMatthias Ringwald     clip_number[0] = 0;
324d0c20769SMatthias Ringwald     return clip_number;
325d0c20769SMatthias Ringwald }
326d0c20769SMatthias Ringwald 
3279cae807eSMatthias Ringwald hfp_call_status_t hfp_gsm_call_status(void){
328d210d9c4SMatthias Ringwald     if (get_number_active_calls() + get_number_held_calls() + get_number_response_held_calls()){
32974386ee0SMatthias Ringwald         return HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT;
33074386ee0SMatthias Ringwald     }
33174386ee0SMatthias Ringwald     return HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS;
33274386ee0SMatthias Ringwald }
33374386ee0SMatthias Ringwald 
3349cae807eSMatthias Ringwald hfp_callheld_status_t hfp_gsm_callheld_status(void){
33574386ee0SMatthias Ringwald     // @note: order is important
33674386ee0SMatthias Ringwald     if (get_number_held_calls() == 0){
33774386ee0SMatthias Ringwald         return HFP_CALLHELD_STATUS_NO_CALLS_HELD;
33874386ee0SMatthias Ringwald     }
33974386ee0SMatthias Ringwald     if (get_number_active_calls() == 0) {
34074386ee0SMatthias Ringwald         return HFP_CALLHELD_STATUS_CALL_ON_HOLD_AND_NO_ACTIVE_CALLS;
34174386ee0SMatthias Ringwald     }
34274386ee0SMatthias Ringwald     return HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED;
34374386ee0SMatthias Ringwald }
34474386ee0SMatthias Ringwald 
3459cae807eSMatthias Ringwald hfp_callsetup_status_t hfp_gsm_callsetup_status(void){
34674386ee0SMatthias Ringwald     return callsetup_status;
34774386ee0SMatthias Ringwald }
34874386ee0SMatthias Ringwald 
3499cae807eSMatthias Ringwald static int hfp_gsm_response_held_active(void){
350d210d9c4SMatthias Ringwald     return get_response_held_call_index() != -1 ;
351d210d9c4SMatthias Ringwald }
352d210d9c4SMatthias Ringwald 
353d210d9c4SMatthias Ringwald int hfp_gsm_call_possible(void){
354d210d9c4SMatthias Ringwald     return get_number_none_calls() > 0;
355d210d9c4SMatthias Ringwald }
356d210d9c4SMatthias Ringwald 
35774386ee0SMatthias Ringwald void hfp_gsm_handle_event(hfp_ag_call_event_t event){
358d0c20769SMatthias Ringwald     hfp_gsm_handler(event, 0, 0, NULL);
359d0c20769SMatthias Ringwald }
360d0c20769SMatthias Ringwald 
361d0c20769SMatthias Ringwald void hfp_gsm_handle_event_with_clip(hfp_ag_call_event_t event, uint8_t type, const char * number){
362d0c20769SMatthias Ringwald     hfp_gsm_handler(event, 0, type, number);
363d0c20769SMatthias Ringwald }
364d0c20769SMatthias Ringwald 
365d0c20769SMatthias Ringwald void hfp_gsm_handle_event_with_call_index(hfp_ag_call_event_t event, uint8_t index){
366d0c20769SMatthias Ringwald     hfp_gsm_handler(event, index, 0, NULL);
367d0c20769SMatthias Ringwald }
368d0c20769SMatthias Ringwald 
3699cae807eSMatthias Ringwald void hfp_gsm_handle_event_with_call_number(hfp_ag_call_event_t event, const char * number){
3709cae807eSMatthias Ringwald     hfp_gsm_handler(event, 0, 0, number);
3719cae807eSMatthias Ringwald }
3729cae807eSMatthias Ringwald 
373d0c20769SMatthias Ringwald static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t type, const char * number){
374d0c20769SMatthias Ringwald     int next_free_slot = get_next_free_slot();
37574386ee0SMatthias Ringwald     int current_call_index = get_active_call_index();
376d210d9c4SMatthias Ringwald     int initiated_call_index = get_initiated_call_index();
377d210d9c4SMatthias Ringwald     int held_call_index = get_held_call_index();
378d0c20769SMatthias Ringwald     int i;
379d0c20769SMatthias Ringwald 
38074386ee0SMatthias Ringwald     switch (event){
38174386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_INITIATED:
38274386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_REDIAL_INITIATED:
38374386ee0SMatthias Ringwald             if (next_free_slot == -1){
384d210d9c4SMatthias Ringwald                 log_error("gsm: max call nr exceeded");
38574386ee0SMatthias Ringwald                 return;
38674386ee0SMatthias Ringwald             }
3879cae807eSMatthias Ringwald             create_call(HFP_ENHANCED_CALL_DIR_OUTGOING);
388d210d9c4SMatthias Ringwald             break;
38974386ee0SMatthias Ringwald 
390d210d9c4SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_REJECTED:
391d210d9c4SMatthias Ringwald             if (current_call_index != -1){
392d0c20769SMatthias Ringwald                 delete_call(current_call_index);
393d210d9c4SMatthias Ringwald             }
394*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
395d210d9c4SMatthias Ringwald             break;
396d210d9c4SMatthias Ringwald 
397d210d9c4SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_ACCEPTED:
39874386ee0SMatthias Ringwald             if (current_call_index != -1){
399*66a048abSMatthias Ringwald                 set_enhanced_call_status_held(current_call_index);
40074386ee0SMatthias Ringwald             }
401*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE);
40274386ee0SMatthias Ringwald             break;
403d210d9c4SMatthias Ringwald 
40474386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_RINGING:
405d210d9c4SMatthias Ringwald             if (current_call_index == -1){
406d210d9c4SMatthias Ringwald                 log_error("gsm: no active call");
407d210d9c4SMatthias Ringwald                 return;
408d210d9c4SMatthias Ringwald             }
409*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE);
41074386ee0SMatthias Ringwald             break;
41174386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_ESTABLISHED:
412*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
413*66a048abSMatthias Ringwald             set_enhanced_call_status_active(initiated_call_index);
41474386ee0SMatthias Ringwald             break;
415d210d9c4SMatthias Ringwald 
416d210d9c4SMatthias Ringwald         case HFP_AG_INCOMING_CALL:
417d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS) break;
418*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS);
4199cae807eSMatthias Ringwald             create_call(HFP_ENHANCED_CALL_DIR_INCOMING);
420d210d9c4SMatthias Ringwald             break;
421d210d9c4SMatthias Ringwald 
422d210d9c4SMatthias Ringwald         case HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG:
423d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
424*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
425d210d9c4SMatthias Ringwald 
426d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){
427*66a048abSMatthias Ringwald                 set_enhanced_call_status_held(current_call_index);
428d210d9c4SMatthias Ringwald             }
429*66a048abSMatthias Ringwald             set_enhanced_call_status_active(initiated_call_index);
430d210d9c4SMatthias Ringwald             break;
431d210d9c4SMatthias Ringwald 
432d210d9c4SMatthias Ringwald         case HFP_AG_HELD_CALL_JOINED_BY_AG:
433d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break;
434d210d9c4SMatthias Ringwald 
435d210d9c4SMatthias Ringwald             // TODO: is following condition correct? Can we join incoming call before it is answered?
436d210d9c4SMatthias Ringwald             if (callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){
437*66a048abSMatthias Ringwald                 set_enhanced_call_status_active(initiated_call_index);
438*66a048abSMatthias Ringwald                 set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
4399cae807eSMatthias Ringwald             } else if (hfp_gsm_callheld_status() == HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED) {
440*66a048abSMatthias Ringwald                 set_enhanced_call_status_active(held_call_index);
441d210d9c4SMatthias Ringwald             }
442d210d9c4SMatthias Ringwald 
4439cae807eSMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
444*66a048abSMatthias Ringwald                 if (is_enhanced_call_status_active(i)){
4459cae807eSMatthias Ringwald                     gsm_calls[i].mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL;
4469cae807eSMatthias Ringwald                 }
447d210d9c4SMatthias Ringwald             }
448d210d9c4SMatthias Ringwald             break;
449d210d9c4SMatthias Ringwald 
450d210d9c4SMatthias Ringwald         case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF:
451d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
452d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break;
453*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
454*66a048abSMatthias Ringwald             set_enhanced_call_status_active(initiated_call_index);
455d210d9c4SMatthias Ringwald             break;
456d210d9c4SMatthias Ringwald 
457d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG:
458d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF:
459d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
460d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break;
461*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
462*66a048abSMatthias Ringwald             set_enhanced_call_status_response_hold(initiated_call_index);
463d210d9c4SMatthias Ringwald             break;
464d210d9c4SMatthias Ringwald 
465d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG:
466d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF:
467d210d9c4SMatthias Ringwald             if (!hfp_gsm_response_held_active()) break;
468*66a048abSMatthias Ringwald             set_enhanced_call_status_active(get_response_held_call_index());
469d210d9c4SMatthias Ringwald             break;
470d210d9c4SMatthias Ringwald 
471d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG:
472d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF:
473d210d9c4SMatthias Ringwald             if (!hfp_gsm_response_held_active()) break;
474d0c20769SMatthias Ringwald             delete_call(get_response_held_call_index());
475d210d9c4SMatthias Ringwald             break;
476d210d9c4SMatthias Ringwald 
477d210d9c4SMatthias Ringwald 
478d210d9c4SMatthias Ringwald         case HFP_AG_TERMINATE_CALL_BY_HF:
479d210d9c4SMatthias Ringwald             switch (hfp_gsm_call_status()){
480d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
481*66a048abSMatthias Ringwald                     set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
482d210d9c4SMatthias Ringwald                     break;
483d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
484d0c20769SMatthias Ringwald                     delete_call(current_call_index);
485d210d9c4SMatthias Ringwald                     break;
486d210d9c4SMatthias Ringwald             }
487d210d9c4SMatthias Ringwald             break;
488d210d9c4SMatthias Ringwald 
489d210d9c4SMatthias Ringwald         case HFP_AG_TERMINATE_CALL_BY_AG:
490d210d9c4SMatthias Ringwald             switch (hfp_gsm_call_status()){
491d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
492d210d9c4SMatthias Ringwald                     if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
493*66a048abSMatthias Ringwald                     set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
494d210d9c4SMatthias Ringwald                     break;
495d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
496*66a048abSMatthias Ringwald                     set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
497d0c20769SMatthias Ringwald                     delete_call(current_call_index);
498d210d9c4SMatthias Ringwald                     break;
499d210d9c4SMatthias Ringwald                 default:
500d210d9c4SMatthias Ringwald                     break;
501d210d9c4SMatthias Ringwald             }
502d210d9c4SMatthias Ringwald             break;
503d210d9c4SMatthias Ringwald 
504d0c20769SMatthias Ringwald         case HFP_AG_CALL_DROPPED:
505*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
506d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break;
507d210d9c4SMatthias Ringwald 
508d210d9c4SMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
509d0c20769SMatthias Ringwald                 delete_call(i);
510d210d9c4SMatthias Ringwald             }
511d210d9c4SMatthias Ringwald             break;
512d0c20769SMatthias Ringwald 
513d210d9c4SMatthias Ringwald         case HFP_AG_CALL_HOLD_USER_BUSY:
514d210d9c4SMatthias Ringwald             // Held or waiting call gets active,
515*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
516*66a048abSMatthias Ringwald             free_call_slot(initiated_call_index);
517*66a048abSMatthias Ringwald             set_enhanced_call_status_active(held_call_index);
518d210d9c4SMatthias Ringwald             break;
519d210d9c4SMatthias Ringwald 
520d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:
521d0c20769SMatthias Ringwald             if (index != 0 && index <= HFP_GSM_MAX_NR_CALLS ){
522d0c20769SMatthias Ringwald                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
523d0c20769SMatthias Ringwald                     if (gsm_calls[i].index == index){
524d0c20769SMatthias Ringwald                         delete_call(i);
525d0c20769SMatthias Ringwald                         continue;
526d0c20769SMatthias Ringwald                     }
527d0c20769SMatthias Ringwald                 }
528d0c20769SMatthias Ringwald             } else {
529d210d9c4SMatthias Ringwald                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
530*66a048abSMatthias Ringwald                     if (is_enhanced_call_status_active(i)){
531d0c20769SMatthias Ringwald                         delete_call(i);
532d0c20769SMatthias Ringwald                     }
533d210d9c4SMatthias Ringwald                 }
534d210d9c4SMatthias Ringwald             }
535d210d9c4SMatthias Ringwald 
536d210d9c4SMatthias Ringwald             if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
537*66a048abSMatthias Ringwald                 set_enhanced_call_status_active(initiated_call_index);
538d210d9c4SMatthias Ringwald             } else {
539*66a048abSMatthias Ringwald                 set_enhanced_call_status_active(held_call_index);
540d210d9c4SMatthias Ringwald             }
541d0c20769SMatthias Ringwald 
542*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
543d210d9c4SMatthias Ringwald             break;
544d0c20769SMatthias Ringwald 
545d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:
546d210d9c4SMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
547*66a048abSMatthias Ringwald                 if (is_enhanced_call_status_active(i) && gsm_calls[i].index != index){
548*66a048abSMatthias Ringwald                     set_enhanced_call_status_held(i);
549d210d9c4SMatthias Ringwald                 }
550d210d9c4SMatthias Ringwald             }
551d210d9c4SMatthias Ringwald 
552d210d9c4SMatthias Ringwald             if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
553*66a048abSMatthias Ringwald                 set_enhanced_call_status_active(initiated_call_index);
554d210d9c4SMatthias Ringwald             } else {
555*66a048abSMatthias Ringwald                 set_enhanced_call_status_active(held_call_index);
556d210d9c4SMatthias Ringwald             }
557*66a048abSMatthias Ringwald             set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
558d210d9c4SMatthias Ringwald             break;
559d0c20769SMatthias Ringwald 
560d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_ADD_HELD_CALL:
561d210d9c4SMatthias Ringwald             if (hfp_gsm_callheld_status() != HFP_CALLHELD_STATUS_NO_CALLS_HELD){
562d210d9c4SMatthias Ringwald                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
563*66a048abSMatthias Ringwald                     if (gsm_calls[i].used_slot){
564*66a048abSMatthias Ringwald                         set_enhanced_call_status_active(i);
5659cae807eSMatthias Ringwald                         gsm_calls[i].mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL;
566d210d9c4SMatthias Ringwald                     }
567d210d9c4SMatthias Ringwald                 }
568d210d9c4SMatthias Ringwald             }
569d210d9c4SMatthias Ringwald             break;
570d0c20769SMatthias Ringwald 
571d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_EXIT_AND_JOIN_CALLS:
572d210d9c4SMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
573d0c20769SMatthias Ringwald                 delete_call(i);
574d210d9c4SMatthias Ringwald             }
575d0c20769SMatthias Ringwald             break;
576d210d9c4SMatthias Ringwald 
577d0c20769SMatthias Ringwald         case HFP_AG_SET_CLIP:
578d0c20769SMatthias Ringwald             if (initiated_call_index != -1){
579d0c20769SMatthias Ringwald                 hfp_gsm_set_clip(initiated_call_index, type, number);
580d210d9c4SMatthias Ringwald                 break;
581d210d9c4SMatthias Ringwald             }
5829cae807eSMatthias Ringwald 
583d0c20769SMatthias Ringwald             clip_type = type;
584d0c20769SMatthias Ringwald             strncpy(clip_number, number, sizeof(clip_number));
585d0c20769SMatthias Ringwald             clip_number[sizeof(clip_number)-1] = '\0';
586d0c20769SMatthias Ringwald 
587d0c20769SMatthias Ringwald             break;
58874386ee0SMatthias Ringwald         default:
58974386ee0SMatthias Ringwald             break;
59074386ee0SMatthias Ringwald     }
59174386ee0SMatthias Ringwald }