xref: /btstack/src/classic/hfp_gsm_model.c (revision 9cae807ed43e0ab5921240d65e041e665d97c7b1)
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 //
40*9cae807eSMatthias 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"
5474386ee0SMatthias Ringwald #include "classic/sdp.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
64*9cae807eSMatthias 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;
70*9cae807eSMatthias Ringwald static char clip_number[HFP_GSM_MAX_CALL_NUMBER_SIZE];
71*9cae807eSMatthias 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);
74d0c20769SMatthias Ringwald 
75d210d9c4SMatthias Ringwald void hfp_gsm_init(void){
76d0c20769SMatthias Ringwald     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
77d0c20769SMatthias Ringwald     clip_type = 0;
78d0c20769SMatthias Ringwald     memset(clip_number, 0, sizeof(clip_number));
79*9cae807eSMatthias Ringwald     memset(last_dialed_number, 0, sizeof(last_dialed_number));
80d210d9c4SMatthias Ringwald     memset(gsm_calls, 0, sizeof(gsm_calls));
81d210d9c4SMatthias Ringwald     int i;
82d210d9c4SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
83d210d9c4SMatthias Ringwald         gsm_calls[i].status = CALL_NONE;
84d210d9c4SMatthias Ringwald     }
85d210d9c4SMatthias Ringwald }
8674386ee0SMatthias Ringwald 
8774386ee0SMatthias Ringwald static int get_number_calls_with_status(hfp_gsm_call_status_t status){
8874386ee0SMatthias Ringwald     int i, count = 0;
8974386ee0SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
9074386ee0SMatthias Ringwald         if (gsm_calls[i].status == status) count++;
9174386ee0SMatthias Ringwald     }
9274386ee0SMatthias Ringwald     return count;
9374386ee0SMatthias Ringwald }
9474386ee0SMatthias Ringwald 
9574386ee0SMatthias Ringwald static int get_call_index_with_status(hfp_gsm_call_status_t status){
9674386ee0SMatthias Ringwald     int i ;
9774386ee0SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
9874386ee0SMatthias Ringwald         if (gsm_calls[i].status == status) return i;
9974386ee0SMatthias Ringwald     }
10074386ee0SMatthias Ringwald     return -1;
10174386ee0SMatthias Ringwald }
10274386ee0SMatthias Ringwald 
103*9cae807eSMatthias Ringwald static inline int get_next_free_slot(void){
10474386ee0SMatthias Ringwald     return get_call_index_with_status(CALL_NONE);
10574386ee0SMatthias Ringwald }
10674386ee0SMatthias Ringwald 
107*9cae807eSMatthias Ringwald static inline int get_active_call_index(void){
10874386ee0SMatthias Ringwald     return get_call_index_with_status(CALL_ACTIVE);
10974386ee0SMatthias Ringwald }
11074386ee0SMatthias Ringwald 
111*9cae807eSMatthias Ringwald static inline int get_initiated_call_index(void){
112d210d9c4SMatthias Ringwald     return get_call_index_with_status(CALL_INITIATED);
113d210d9c4SMatthias Ringwald }
114d210d9c4SMatthias Ringwald 
115*9cae807eSMatthias Ringwald static inline int get_held_call_index(void){
116d210d9c4SMatthias Ringwald     return get_call_index_with_status(CALL_HELD);
117d210d9c4SMatthias Ringwald }
118d210d9c4SMatthias Ringwald 
119*9cae807eSMatthias Ringwald static inline int get_response_held_call_index(void){
120d210d9c4SMatthias Ringwald     return get_call_index_with_status(CALL_RESPONSE_HOLD);
121d210d9c4SMatthias Ringwald }
122d210d9c4SMatthias Ringwald 
123*9cae807eSMatthias Ringwald static inline int get_number_none_calls(void){
124d210d9c4SMatthias Ringwald     return get_number_calls_with_status(CALL_NONE);
125d210d9c4SMatthias Ringwald }
12674386ee0SMatthias Ringwald 
127*9cae807eSMatthias Ringwald static inline int get_number_active_calls(void){
12874386ee0SMatthias Ringwald     return get_number_calls_with_status(CALL_ACTIVE);
12974386ee0SMatthias Ringwald }
13074386ee0SMatthias Ringwald 
131*9cae807eSMatthias Ringwald static inline int get_number_held_calls(void){
13274386ee0SMatthias Ringwald     return get_number_calls_with_status(CALL_HELD);
13374386ee0SMatthias Ringwald }
13474386ee0SMatthias Ringwald 
135*9cae807eSMatthias Ringwald static inline int get_number_response_held_calls(void){
136d210d9c4SMatthias Ringwald     return get_number_calls_with_status(CALL_RESPONSE_HOLD);
137d210d9c4SMatthias Ringwald }
138d210d9c4SMatthias Ringwald 
139*9cae807eSMatthias Ringwald static int next_call_index(void){
140d0c20769SMatthias Ringwald     return HFP_GSM_MAX_NR_CALLS + 1 - get_number_none_calls();
141d0c20769SMatthias Ringwald }
142d0c20769SMatthias Ringwald 
143d0c20769SMatthias Ringwald static void hfp_gsm_set_clip(int index_in_table, uint8_t type, const char * number){
144*9cae807eSMatthias Ringwald     if (strlen(number) == 0) return;
145*9cae807eSMatthias Ringwald 
146d0c20769SMatthias Ringwald     gsm_calls[index_in_table].clip_type = type;
147d0c20769SMatthias Ringwald 
148*9cae807eSMatthias Ringwald     int clip_number_size = strlen(number) < HFP_GSM_MAX_CALL_NUMBER_SIZE ? strlen(number) : HFP_GSM_MAX_CALL_NUMBER_SIZE-1;
149d0c20769SMatthias Ringwald     strncpy(gsm_calls[index_in_table].clip_number, number, clip_number_size);
150*9cae807eSMatthias Ringwald     gsm_calls[index_in_table].clip_number[clip_number_size] = '\0';
151*9cae807eSMatthias Ringwald     strncpy(last_dialed_number, number, clip_number_size);
152*9cae807eSMatthias Ringwald     last_dialed_number[clip_number_size] = '\0';
153*9cae807eSMatthias Ringwald 
154*9cae807eSMatthias Ringwald     clip_type = 0;
155*9cae807eSMatthias Ringwald     memset(clip_number, 0, sizeof(clip_number));
156d0c20769SMatthias Ringwald }
157d0c20769SMatthias Ringwald 
158d0c20769SMatthias Ringwald static void delete_call(int delete_index_in_table){
159d0c20769SMatthias Ringwald     int i ;
160d0c20769SMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
161d0c20769SMatthias Ringwald         if (gsm_calls[i].index > gsm_calls[delete_index_in_table].index){
162d0c20769SMatthias Ringwald             gsm_calls[i].index--;
163d0c20769SMatthias Ringwald         }
164d0c20769SMatthias Ringwald     }
165d0c20769SMatthias Ringwald 
166d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].status = CALL_NONE;
167d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].clip_type = 0;
168d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].index = 0;
169d0c20769SMatthias Ringwald     gsm_calls[delete_index_in_table].clip_number[0] = '\0';
170*9cae807eSMatthias Ringwald     gsm_calls[delete_index_in_table].mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL;
171d0c20769SMatthias Ringwald }
172d0c20769SMatthias Ringwald 
173*9cae807eSMatthias Ringwald 
174*9cae807eSMatthias Ringwald static void create_call(hfp_enhanced_call_dir_t direction){
175d0c20769SMatthias Ringwald     int next_free_slot = get_next_free_slot();
176*9cae807eSMatthias Ringwald     gsm_calls[next_free_slot].direction = direction;
177d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].index = next_call_index();
178d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].status = CALL_INITIATED;
179d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].clip_type = 0;
180d0c20769SMatthias Ringwald     gsm_calls[next_free_slot].clip_number[0] = '\0';
181*9cae807eSMatthias Ringwald     gsm_calls[next_free_slot].mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL;
182d0c20769SMatthias Ringwald 
183d0c20769SMatthias Ringwald     hfp_gsm_set_clip(next_free_slot, clip_type, clip_number);
184d0c20769SMatthias Ringwald }
185d0c20769SMatthias Ringwald 
186*9cae807eSMatthias Ringwald 
187*9cae807eSMatthias Ringwald int hfp_gsm_get_number_of_calls(void){
188*9cae807eSMatthias Ringwald     return HFP_GSM_MAX_NR_CALLS - get_number_none_calls();
189*9cae807eSMatthias Ringwald }
190*9cae807eSMatthias Ringwald 
191*9cae807eSMatthias Ringwald void hfp_gsm_clear_last_dialed_number(void){
192*9cae807eSMatthias Ringwald     memset(last_dialed_number, 0, sizeof(last_dialed_number));
193*9cae807eSMatthias Ringwald }
194*9cae807eSMatthias Ringwald 
195*9cae807eSMatthias Ringwald char * hfp_gsm_last_dialed_number(void){
196*9cae807eSMatthias Ringwald     return &last_dialed_number[0];
197*9cae807eSMatthias Ringwald }
198*9cae807eSMatthias Ringwald 
199*9cae807eSMatthias Ringwald hfp_gsm_call_t * hfp_gsm_call(int call_index){
200*9cae807eSMatthias Ringwald     int i;
201*9cae807eSMatthias Ringwald 
202*9cae807eSMatthias Ringwald     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
203*9cae807eSMatthias Ringwald         hfp_gsm_call_t * call = &gsm_calls[i];
204*9cae807eSMatthias Ringwald 
205*9cae807eSMatthias Ringwald         if (call->index != call_index) continue;
206*9cae807eSMatthias Ringwald 
207*9cae807eSMatthias Ringwald         call->enhanced_status = HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD;
208*9cae807eSMatthias Ringwald 
209*9cae807eSMatthias Ringwald         if (call->status == CALL_ACTIVE) call->enhanced_status = HFP_ENHANCED_CALL_STATUS_ACTIVE;
210*9cae807eSMatthias Ringwald         if (call->status == CALL_HELD)   call->enhanced_status = HFP_ENHANCED_CALL_STATUS_HELD;
211*9cae807eSMatthias Ringwald 
212*9cae807eSMatthias Ringwald         if (call->status == CALL_INITIATED){
213*9cae807eSMatthias Ringwald             if (call->direction == HFP_ENHANCED_CALL_DIR_OUTGOING){
214*9cae807eSMatthias Ringwald                 if (callsetup_status == HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE){
215*9cae807eSMatthias Ringwald                     call->enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING;
216*9cae807eSMatthias Ringwald                 }
217*9cae807eSMatthias Ringwald                 call->enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING;
218*9cae807eSMatthias Ringwald             } else {
219*9cae807eSMatthias Ringwald                 if (get_number_active_calls() > 0){
220*9cae807eSMatthias Ringwald                     call->enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING;
221*9cae807eSMatthias Ringwald                 }
222*9cae807eSMatthias Ringwald                 call->enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING;
223*9cae807eSMatthias Ringwald             }
224*9cae807eSMatthias Ringwald         }
225*9cae807eSMatthias Ringwald         return call;
226*9cae807eSMatthias Ringwald     }
227*9cae807eSMatthias Ringwald     return NULL;
228*9cae807eSMatthias Ringwald }
229*9cae807eSMatthias Ringwald 
230*9cae807eSMatthias Ringwald uint8_t hfp_gsm_clip_type(void){
231d0c20769SMatthias Ringwald     if (clip_type != 0) return clip_type;
232d0c20769SMatthias Ringwald 
233d0c20769SMatthias Ringwald     int initiated_call_index = get_initiated_call_index();
234d0c20769SMatthias Ringwald     if (initiated_call_index != -1){
235d0c20769SMatthias Ringwald         if (gsm_calls[initiated_call_index].clip_type != 0) {
236d0c20769SMatthias Ringwald             return gsm_calls[initiated_call_index].clip_type;
237d0c20769SMatthias Ringwald         }
238d0c20769SMatthias Ringwald     }
239d0c20769SMatthias Ringwald 
240d0c20769SMatthias Ringwald     int active_call_index = get_active_call_index();
241d0c20769SMatthias Ringwald     if (active_call_index != -1){
242d0c20769SMatthias Ringwald         if (gsm_calls[active_call_index].clip_type != 0) {
243d0c20769SMatthias Ringwald             return gsm_calls[active_call_index].clip_type;
244d0c20769SMatthias Ringwald         }
245d0c20769SMatthias Ringwald     }
246d0c20769SMatthias Ringwald     return 0;
247d0c20769SMatthias Ringwald }
248d0c20769SMatthias Ringwald 
249*9cae807eSMatthias Ringwald char *  hfp_gsm_clip_number(void){
250*9cae807eSMatthias Ringwald     if (strlen(clip_number) != 0) return clip_number;
251d0c20769SMatthias Ringwald 
252d0c20769SMatthias Ringwald     int initiated_call_index = get_initiated_call_index();
253d0c20769SMatthias Ringwald     if (initiated_call_index != -1){
254d0c20769SMatthias Ringwald         if (gsm_calls[initiated_call_index].clip_type != 0) {
255d0c20769SMatthias Ringwald             return gsm_calls[initiated_call_index].clip_number;
256d0c20769SMatthias Ringwald         }
257d0c20769SMatthias Ringwald     }
258d0c20769SMatthias Ringwald 
259d0c20769SMatthias Ringwald     int active_call_index = get_active_call_index();
260d0c20769SMatthias Ringwald     if (active_call_index != -1){
261d0c20769SMatthias Ringwald         if (gsm_calls[active_call_index].clip_type != 0) {
262d0c20769SMatthias Ringwald             return gsm_calls[active_call_index].clip_number;
263d0c20769SMatthias Ringwald         }
264d0c20769SMatthias Ringwald     }
265d0c20769SMatthias Ringwald     clip_number[0] = 0;
266d0c20769SMatthias Ringwald     return clip_number;
267d0c20769SMatthias Ringwald }
268d0c20769SMatthias Ringwald 
269*9cae807eSMatthias Ringwald hfp_call_status_t hfp_gsm_call_status(void){
270d210d9c4SMatthias Ringwald     if (get_number_active_calls() + get_number_held_calls() + get_number_response_held_calls()){
27174386ee0SMatthias Ringwald         return HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT;
27274386ee0SMatthias Ringwald     }
27374386ee0SMatthias Ringwald     return HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS;
27474386ee0SMatthias Ringwald }
27574386ee0SMatthias Ringwald 
276*9cae807eSMatthias Ringwald hfp_callheld_status_t hfp_gsm_callheld_status(void){
27774386ee0SMatthias Ringwald     // @note: order is important
27874386ee0SMatthias Ringwald     if (get_number_held_calls() == 0){
27974386ee0SMatthias Ringwald         return HFP_CALLHELD_STATUS_NO_CALLS_HELD;
28074386ee0SMatthias Ringwald     }
28174386ee0SMatthias Ringwald     if (get_number_active_calls() == 0) {
28274386ee0SMatthias Ringwald         return HFP_CALLHELD_STATUS_CALL_ON_HOLD_AND_NO_ACTIVE_CALLS;
28374386ee0SMatthias Ringwald     }
28474386ee0SMatthias Ringwald     return HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED;
28574386ee0SMatthias Ringwald }
28674386ee0SMatthias Ringwald 
287*9cae807eSMatthias Ringwald hfp_callsetup_status_t hfp_gsm_callsetup_status(void){
28874386ee0SMatthias Ringwald     return callsetup_status;
28974386ee0SMatthias Ringwald }
29074386ee0SMatthias Ringwald 
291*9cae807eSMatthias Ringwald static int hfp_gsm_response_held_active(void){
292d210d9c4SMatthias Ringwald     return get_response_held_call_index() != -1 ;
293d210d9c4SMatthias Ringwald }
294d210d9c4SMatthias Ringwald 
295d210d9c4SMatthias Ringwald int hfp_gsm_call_possible(void){
296d210d9c4SMatthias Ringwald     return get_number_none_calls() > 0;
297d210d9c4SMatthias Ringwald }
298d210d9c4SMatthias Ringwald 
29974386ee0SMatthias Ringwald void hfp_gsm_handle_event(hfp_ag_call_event_t event){
300d0c20769SMatthias Ringwald     hfp_gsm_handler(event, 0, 0, NULL);
301d0c20769SMatthias Ringwald }
302d0c20769SMatthias Ringwald 
303d0c20769SMatthias Ringwald void hfp_gsm_handle_event_with_clip(hfp_ag_call_event_t event, uint8_t type, const char * number){
304d0c20769SMatthias Ringwald     hfp_gsm_handler(event, 0, type, number);
305d0c20769SMatthias Ringwald }
306d0c20769SMatthias Ringwald 
307d0c20769SMatthias Ringwald void hfp_gsm_handle_event_with_call_index(hfp_ag_call_event_t event, uint8_t index){
308d0c20769SMatthias Ringwald     hfp_gsm_handler(event, index, 0, NULL);
309d0c20769SMatthias Ringwald }
310d0c20769SMatthias Ringwald 
311*9cae807eSMatthias Ringwald void hfp_gsm_handle_event_with_call_number(hfp_ag_call_event_t event, const char * number){
312*9cae807eSMatthias Ringwald     hfp_gsm_handler(event, 0, 0, number);
313*9cae807eSMatthias Ringwald }
314*9cae807eSMatthias Ringwald 
315d0c20769SMatthias Ringwald static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t type, const char * number){
316d0c20769SMatthias Ringwald     int next_free_slot = get_next_free_slot();
31774386ee0SMatthias Ringwald     int current_call_index = get_active_call_index();
318d210d9c4SMatthias Ringwald     int initiated_call_index = get_initiated_call_index();
319d210d9c4SMatthias Ringwald     int held_call_index = get_held_call_index();
320d0c20769SMatthias Ringwald     int i;
321d0c20769SMatthias Ringwald 
32274386ee0SMatthias Ringwald     switch (event){
32374386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_INITIATED:
32474386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_REDIAL_INITIATED:
32574386ee0SMatthias Ringwald             if (next_free_slot == -1){
326d210d9c4SMatthias Ringwald                 log_error("gsm: max call nr exceeded");
32774386ee0SMatthias Ringwald                 return;
32874386ee0SMatthias Ringwald             }
329*9cae807eSMatthias Ringwald             create_call(HFP_ENHANCED_CALL_DIR_OUTGOING);
330d210d9c4SMatthias Ringwald             break;
33174386ee0SMatthias Ringwald 
332d210d9c4SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_REJECTED:
333d210d9c4SMatthias Ringwald             if (current_call_index != -1){
334d0c20769SMatthias Ringwald                 delete_call(current_call_index);
335d210d9c4SMatthias Ringwald             }
336d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
337d210d9c4SMatthias Ringwald             break;
338d210d9c4SMatthias Ringwald 
339d210d9c4SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_ACCEPTED:
34074386ee0SMatthias Ringwald             if (current_call_index != -1){
34174386ee0SMatthias Ringwald                 gsm_calls[current_call_index].status = CALL_HELD;
34274386ee0SMatthias Ringwald             }
34374386ee0SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE;
34474386ee0SMatthias Ringwald             break;
345d210d9c4SMatthias Ringwald 
34674386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_RINGING:
347d210d9c4SMatthias Ringwald             if (current_call_index == -1){
348d210d9c4SMatthias Ringwald                 log_error("gsm: no active call");
349d210d9c4SMatthias Ringwald                 return;
350d210d9c4SMatthias Ringwald             }
351d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE;
35274386ee0SMatthias Ringwald             break;
35374386ee0SMatthias Ringwald         case HFP_AG_OUTGOING_CALL_ESTABLISHED:
354d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
355d210d9c4SMatthias Ringwald             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
35674386ee0SMatthias Ringwald             break;
357d210d9c4SMatthias Ringwald 
358d210d9c4SMatthias Ringwald         case HFP_AG_INCOMING_CALL:
359d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS) break;
360d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS;
361*9cae807eSMatthias Ringwald             create_call(HFP_ENHANCED_CALL_DIR_INCOMING);
362d210d9c4SMatthias Ringwald             break;
363d210d9c4SMatthias Ringwald 
364d210d9c4SMatthias Ringwald         case HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG:
365d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
366d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
367d210d9c4SMatthias Ringwald 
368d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){
369d210d9c4SMatthias Ringwald                 gsm_calls[current_call_index].status = CALL_HELD;
370d210d9c4SMatthias Ringwald             }
371d210d9c4SMatthias Ringwald             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
372d210d9c4SMatthias Ringwald             break;
373d210d9c4SMatthias Ringwald 
374d210d9c4SMatthias Ringwald         case HFP_AG_HELD_CALL_JOINED_BY_AG:
375d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break;
376d210d9c4SMatthias Ringwald 
377d210d9c4SMatthias Ringwald             // TODO: is following condition correct? Can we join incoming call before it is answered?
378d210d9c4SMatthias Ringwald             if (callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){
379d210d9c4SMatthias Ringwald                 gsm_calls[initiated_call_index].status = CALL_ACTIVE;
380d210d9c4SMatthias Ringwald                 callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
381*9cae807eSMatthias Ringwald             } else if (hfp_gsm_callheld_status() == HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED) {
382*9cae807eSMatthias Ringwald                 gsm_calls[held_call_index].status = CALL_ACTIVE;
383d210d9c4SMatthias Ringwald             }
384d210d9c4SMatthias Ringwald 
385*9cae807eSMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
386*9cae807eSMatthias Ringwald                 if (gsm_calls[i].status == CALL_ACTIVE){
387*9cae807eSMatthias Ringwald                     gsm_calls[i].mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL;
388*9cae807eSMatthias Ringwald                 }
389d210d9c4SMatthias Ringwald             }
390d210d9c4SMatthias Ringwald             break;
391d210d9c4SMatthias Ringwald 
392d210d9c4SMatthias Ringwald         case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF:
393d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
394d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break;
395d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
396d210d9c4SMatthias Ringwald             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
397d210d9c4SMatthias Ringwald             break;
398d210d9c4SMatthias Ringwald 
399d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG:
400d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF:
401d210d9c4SMatthias Ringwald             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
402d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break;
403d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
404d210d9c4SMatthias Ringwald             gsm_calls[initiated_call_index].status = CALL_RESPONSE_HOLD;
405d210d9c4SMatthias Ringwald             break;
406d210d9c4SMatthias Ringwald 
407d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG:
408d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF:
409d210d9c4SMatthias Ringwald             if (!hfp_gsm_response_held_active()) break;
410d210d9c4SMatthias Ringwald             gsm_calls[get_response_held_call_index()].status = CALL_ACTIVE;
411d210d9c4SMatthias Ringwald             break;
412d210d9c4SMatthias Ringwald 
413d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG:
414d210d9c4SMatthias Ringwald         case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF:
415d210d9c4SMatthias Ringwald             if (!hfp_gsm_response_held_active()) break;
416d0c20769SMatthias Ringwald             delete_call(get_response_held_call_index());
417d210d9c4SMatthias Ringwald             break;
418d210d9c4SMatthias Ringwald 
419d210d9c4SMatthias Ringwald 
420d210d9c4SMatthias Ringwald         case HFP_AG_TERMINATE_CALL_BY_HF:
421d210d9c4SMatthias Ringwald             switch (hfp_gsm_call_status()){
422d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
423d210d9c4SMatthias Ringwald                     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
424d210d9c4SMatthias Ringwald                     break;
425d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
426d0c20769SMatthias Ringwald                     delete_call(current_call_index);
427d210d9c4SMatthias Ringwald                     break;
428d210d9c4SMatthias Ringwald             }
429d210d9c4SMatthias Ringwald             break;
430d210d9c4SMatthias Ringwald 
431d210d9c4SMatthias Ringwald         case HFP_AG_TERMINATE_CALL_BY_AG:
432d210d9c4SMatthias Ringwald             switch (hfp_gsm_call_status()){
433d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
434d210d9c4SMatthias Ringwald                     if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
435d210d9c4SMatthias Ringwald                     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
436d210d9c4SMatthias Ringwald                     break;
437d210d9c4SMatthias Ringwald                 case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
438d210d9c4SMatthias Ringwald                     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
439d0c20769SMatthias Ringwald                     delete_call(current_call_index);
440d210d9c4SMatthias Ringwald                     break;
441d210d9c4SMatthias Ringwald                 default:
442d210d9c4SMatthias Ringwald                     break;
443d210d9c4SMatthias Ringwald             }
444d210d9c4SMatthias Ringwald             break;
445d210d9c4SMatthias Ringwald 
446d0c20769SMatthias Ringwald         case HFP_AG_CALL_DROPPED:
447d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
448d210d9c4SMatthias Ringwald             if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break;
449d210d9c4SMatthias Ringwald 
450d210d9c4SMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
451d0c20769SMatthias Ringwald                 delete_call(i);
452d210d9c4SMatthias Ringwald             }
453d210d9c4SMatthias Ringwald             break;
454d0c20769SMatthias Ringwald 
455d210d9c4SMatthias Ringwald         case HFP_AG_CALL_HOLD_USER_BUSY:
456d210d9c4SMatthias Ringwald             // Held or waiting call gets active,
457d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
458d210d9c4SMatthias Ringwald             gsm_calls[initiated_call_index].status = CALL_NONE;
459d210d9c4SMatthias Ringwald             gsm_calls[held_call_index].status = CALL_ACTIVE;
460d210d9c4SMatthias Ringwald             break;
461d210d9c4SMatthias Ringwald 
462d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:
463d0c20769SMatthias Ringwald             if (index != 0 && index <= HFP_GSM_MAX_NR_CALLS ){
464d0c20769SMatthias Ringwald                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
465d0c20769SMatthias Ringwald                     if (gsm_calls[i].index == index){
466d0c20769SMatthias Ringwald                         delete_call(i);
467d0c20769SMatthias Ringwald                         continue;
468d0c20769SMatthias Ringwald                     }
469d0c20769SMatthias Ringwald                 }
470d0c20769SMatthias Ringwald             } else {
471d210d9c4SMatthias Ringwald                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
472d210d9c4SMatthias Ringwald                     if (gsm_calls[i].status == CALL_ACTIVE){
473d0c20769SMatthias Ringwald                         delete_call(i);
474d0c20769SMatthias Ringwald                     }
475d210d9c4SMatthias Ringwald                 }
476d210d9c4SMatthias Ringwald             }
477d210d9c4SMatthias Ringwald 
478d210d9c4SMatthias Ringwald             if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
479d210d9c4SMatthias Ringwald                 gsm_calls[initiated_call_index].status = CALL_ACTIVE;
480d210d9c4SMatthias Ringwald             } else {
481d210d9c4SMatthias Ringwald                 gsm_calls[held_call_index].status = CALL_ACTIVE;
482d210d9c4SMatthias Ringwald             }
483d0c20769SMatthias Ringwald 
484d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
485d210d9c4SMatthias Ringwald             break;
486d0c20769SMatthias Ringwald 
487d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:
488d210d9c4SMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
489d0c20769SMatthias Ringwald                 if (gsm_calls[i].status == CALL_ACTIVE && gsm_calls[i].index != index){
490d210d9c4SMatthias Ringwald                     gsm_calls[i].status = CALL_HELD;
491d210d9c4SMatthias Ringwald                 }
492d210d9c4SMatthias Ringwald             }
493d210d9c4SMatthias Ringwald 
494d210d9c4SMatthias Ringwald             if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
495d210d9c4SMatthias Ringwald                 gsm_calls[initiated_call_index].status = CALL_ACTIVE;
496d210d9c4SMatthias Ringwald             } else {
497d210d9c4SMatthias Ringwald                 gsm_calls[held_call_index].status = CALL_ACTIVE;
498d210d9c4SMatthias Ringwald             }
499d210d9c4SMatthias Ringwald             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
500d210d9c4SMatthias Ringwald             break;
501d0c20769SMatthias Ringwald 
502d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_ADD_HELD_CALL:
503d210d9c4SMatthias Ringwald             if (hfp_gsm_callheld_status() != HFP_CALLHELD_STATUS_NO_CALLS_HELD){
504d210d9c4SMatthias Ringwald                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
505*9cae807eSMatthias Ringwald                     if (gsm_calls[i].status != CALL_NONE){
506d210d9c4SMatthias Ringwald                         gsm_calls[i].status = CALL_ACTIVE;
507*9cae807eSMatthias Ringwald                         gsm_calls[i].mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL;
508d210d9c4SMatthias Ringwald                     }
509d210d9c4SMatthias Ringwald                 }
510d210d9c4SMatthias Ringwald             }
511d210d9c4SMatthias Ringwald             break;
512d0c20769SMatthias Ringwald 
513d0c20769SMatthias Ringwald         case HFP_AG_CALL_HOLD_EXIT_AND_JOIN_CALLS:
514d210d9c4SMatthias Ringwald             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
515d0c20769SMatthias Ringwald                 delete_call(i);
516d210d9c4SMatthias Ringwald             }
517d0c20769SMatthias Ringwald             break;
518d210d9c4SMatthias Ringwald 
519d0c20769SMatthias Ringwald         case HFP_AG_SET_CLIP:
520d0c20769SMatthias Ringwald             if (initiated_call_index != -1){
521d0c20769SMatthias Ringwald                 hfp_gsm_set_clip(initiated_call_index, type, number);
522d210d9c4SMatthias Ringwald                 break;
523d210d9c4SMatthias Ringwald             }
524*9cae807eSMatthias Ringwald 
525d0c20769SMatthias Ringwald             clip_type = type;
526d0c20769SMatthias Ringwald             strncpy(clip_number, number, sizeof(clip_number));
527d0c20769SMatthias Ringwald             clip_number[sizeof(clip_number)-1] = '\0';
528d0c20769SMatthias Ringwald 
529d0c20769SMatthias Ringwald             break;
53074386ee0SMatthias Ringwald         default:
53174386ee0SMatthias Ringwald             break;
53274386ee0SMatthias Ringwald     }
53374386ee0SMatthias Ringwald }