xref: /btstack/src/classic/hfp_gsm_model.c (revision 1e598166446edd12daa519488aedfe7ced2004e2)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 // *****************************************************************************
39 //
40 // Minimal setup for HFP Audio Gateway (AG) unit (!! UNDER DEVELOPMENT !!)
41 //
42 // *****************************************************************************
43 
44 #include "btstack-config.h"
45 
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 
51 #include "btstack_memory.h"
52 #include "classic/hfp.h"
53 #include "classic/hfp_gsm_model.h"
54 #include "classic/sdp.h"
55 #include "classic/sdp_query_rfcomm.h"
56 #include "btstack_debug.h"
57 #include "hci.h"
58 #include "hci_cmd.h"
59 #include "hci_dump.h"
60 #include "l2cap.h"
61 #include "btstack_run_loop.h"
62 
63 #define HFP_GSM_MAX_NR_CALLS 3
64 
65 typedef enum{
66     CALL_NONE,
67     CALL_INITIATED,
68     CALL_RESPONSE_HOLD,
69     CALL_ACTIVE,
70     CALL_HELD
71 } hfp_gsm_call_status_t;
72 
73 
74 typedef struct {
75     hfp_gsm_call_status_t status;
76     int index;
77     uint8_t clip_type;
78     char    clip_number[25];
79 } hfp_gsm_call_t;
80 
81 static hfp_gsm_call_t gsm_calls[HFP_GSM_MAX_NR_CALLS];
82 static hfp_callsetup_status_t callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
83 
84 static uint8_t clip_type;
85 static char clip_number[25];
86 
87 static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t type, const char * number);
88 
89 void hfp_gsm_init(void){
90     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
91     clip_type = 0;
92     memset(clip_number, 0, sizeof(clip_number));
93 
94     memset(gsm_calls, 0, sizeof(gsm_calls));
95     int i;
96     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
97         gsm_calls[i].status = CALL_NONE;
98     }
99 }
100 
101 static int get_number_calls_with_status(hfp_gsm_call_status_t status){
102     int i, count = 0;
103     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
104         if (gsm_calls[i].status == status) count++;
105     }
106     return count;
107 }
108 
109 static int get_call_index_with_status(hfp_gsm_call_status_t status){
110     int i ;
111     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
112         if (gsm_calls[i].status == status) return i;
113     }
114     return -1;
115 }
116 
117 static inline int get_next_free_slot(){
118     return get_call_index_with_status(CALL_NONE);
119 }
120 
121 static inline int get_active_call_index(){
122     return get_call_index_with_status(CALL_ACTIVE);
123 }
124 
125 static inline int get_initiated_call_index(){
126     return get_call_index_with_status(CALL_INITIATED);
127 }
128 
129 static inline int get_held_call_index(){
130     return get_call_index_with_status(CALL_HELD);
131 }
132 
133 static inline int get_response_held_call_index(){
134     return get_call_index_with_status(CALL_RESPONSE_HOLD);
135 }
136 
137 static inline int get_number_none_calls(){
138     return get_number_calls_with_status(CALL_NONE);
139 }
140 
141 static inline int get_number_active_calls(){
142     return get_number_calls_with_status(CALL_ACTIVE);
143 }
144 
145 static inline int get_number_held_calls(){
146     return get_number_calls_with_status(CALL_HELD);
147 }
148 
149 static inline int get_number_response_held_calls(){
150     return get_number_calls_with_status(CALL_RESPONSE_HOLD);
151 }
152 
153 static int next_call_index(){
154     return HFP_GSM_MAX_NR_CALLS + 1 - get_number_none_calls();
155 }
156 
157 static void hfp_gsm_set_clip(int index_in_table, uint8_t type, const char * number){
158     gsm_calls[index_in_table].clip_type = type;
159 
160     int clip_number_size = sizeof(gsm_calls[index_in_table].clip_number);
161     strncpy(gsm_calls[index_in_table].clip_number, number, clip_number_size);
162     gsm_calls[index_in_table].clip_number[clip_number_size-1] = '\0';
163 }
164 
165 static void delete_call(int delete_index_in_table){
166     int i ;
167     for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
168         if (gsm_calls[i].index > gsm_calls[delete_index_in_table].index){
169             gsm_calls[i].index--;
170         }
171     }
172 
173     gsm_calls[delete_index_in_table].status = CALL_NONE;
174     gsm_calls[delete_index_in_table].clip_type = 0;
175     gsm_calls[delete_index_in_table].index = 0;
176     gsm_calls[delete_index_in_table].clip_number[0] = '\0';
177 }
178 
179 static void create_call(){
180     int next_free_slot = get_next_free_slot();
181     gsm_calls[next_free_slot].index = next_call_index();
182     gsm_calls[next_free_slot].status = CALL_INITIATED;
183     gsm_calls[next_free_slot].clip_type = 0;
184     gsm_calls[next_free_slot].clip_number[0] = '\0';
185 
186     if (clip_type != 0){
187         hfp_gsm_set_clip(next_free_slot, clip_type, clip_number);
188         clip_type = 0;
189         memset(clip_number, 0, sizeof(clip_number));
190     }
191 }
192 
193 uint8_t hfp_gsm_clip_type(){
194     if (clip_type != 0) return clip_type;
195 
196     int initiated_call_index = get_initiated_call_index();
197     if (initiated_call_index != -1){
198         if (gsm_calls[initiated_call_index].clip_type != 0) {
199             return gsm_calls[initiated_call_index].clip_type;
200         }
201     }
202 
203     int active_call_index = get_active_call_index();
204     if (active_call_index != -1){
205         if (gsm_calls[active_call_index].clip_type != 0) {
206             return gsm_calls[active_call_index].clip_type;
207         }
208     }
209     return 0;
210 }
211 
212 char *  hfp_gsm_clip_number(){
213     if (clip_type != 0) return clip_number;
214 
215     int initiated_call_index = get_initiated_call_index();
216     if (initiated_call_index != -1){
217         if (gsm_calls[initiated_call_index].clip_type != 0) {
218             return gsm_calls[initiated_call_index].clip_number;
219         }
220     }
221 
222     int active_call_index = get_active_call_index();
223     if (active_call_index != -1){
224         if (gsm_calls[active_call_index].clip_type != 0) {
225             return gsm_calls[active_call_index].clip_number;
226         }
227     }
228     clip_number[0] = 0;
229     return clip_number;
230 }
231 
232 hfp_call_status_t hfp_gsm_call_status(){
233     if (get_number_active_calls() + get_number_held_calls() + get_number_response_held_calls()){
234         return HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT;
235     }
236     return HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS;
237 }
238 
239 hfp_callheld_status_t hfp_gsm_callheld_status(){
240     // @note: order is important
241     if (get_number_held_calls() == 0){
242         return HFP_CALLHELD_STATUS_NO_CALLS_HELD;
243     }
244     if (get_number_active_calls() == 0) {
245         return HFP_CALLHELD_STATUS_CALL_ON_HOLD_AND_NO_ACTIVE_CALLS;
246     }
247     return HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED;
248 }
249 
250 hfp_callsetup_status_t hfp_gsm_callsetup_status(){
251     return callsetup_status;
252 }
253 
254 int hfp_gsm_response_held_active(){
255     return get_response_held_call_index() != -1 ;
256 }
257 
258 int hfp_gsm_call_possible(void){
259     return get_number_none_calls() > 0;
260 }
261 
262 void hfp_gsm_handle_event(hfp_ag_call_event_t event){
263     hfp_gsm_handler(event, 0, 0, NULL);
264 }
265 
266 void hfp_gsm_handle_event_with_clip(hfp_ag_call_event_t event, uint8_t type, const char * number){
267     hfp_gsm_handler(event, 0, type, number);
268 }
269 
270 void hfp_gsm_handle_event_with_call_index(hfp_ag_call_event_t event, uint8_t index){
271     hfp_gsm_handler(event, index, 0, NULL);
272 }
273 
274 static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t type, const char * number){
275     int next_free_slot = get_next_free_slot();
276     int current_call_index = get_active_call_index();
277     int initiated_call_index = get_initiated_call_index();
278     int held_call_index = get_held_call_index();
279     int i;
280 
281     switch (event){
282         case HFP_AG_OUTGOING_CALL_INITIATED:
283         case HFP_AG_OUTGOING_REDIAL_INITIATED:
284             if (next_free_slot == -1){
285                 log_error("gsm: max call nr exceeded");
286                 return;
287             }
288             create_call();
289             break;
290 
291         case HFP_AG_OUTGOING_CALL_REJECTED:
292             if (current_call_index != -1){
293                 // gsm_calls[current_call_index].status = CALL_NONE;
294                 delete_call(current_call_index);
295             }
296             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
297             break;
298 
299         case HFP_AG_OUTGOING_CALL_ACCEPTED:
300             if (current_call_index != -1){
301                 gsm_calls[current_call_index].status = CALL_HELD;
302             }
303             create_call();
304             callsetup_status = HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE;
305             break;
306 
307         case HFP_AG_OUTGOING_CALL_RINGING:
308             if (current_call_index == -1){
309                 log_error("gsm: no active call");
310                 return;
311             }
312             callsetup_status = HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE;
313             break;
314         case HFP_AG_OUTGOING_CALL_ESTABLISHED:
315             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
316             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
317             break;
318 
319         case HFP_AG_INCOMING_CALL:
320             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS) break;
321             callsetup_status = HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS;
322             create_call();
323             break;
324 
325         case HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG:
326             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
327             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
328 
329             if (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){
330                 gsm_calls[current_call_index].status = CALL_HELD;
331             }
332             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
333             break;
334 
335         case HFP_AG_HELD_CALL_JOINED_BY_AG:
336             if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break;
337 
338             // TODO: mark joined calls with "multiparty flag" (if we cannot calculate it otherwise)
339             // TODO: is following condition correct? Can we join incoming call before it is answered?
340             if (callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){
341                 gsm_calls[initiated_call_index].status = CALL_ACTIVE;
342                 callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
343                 break;
344             }
345 
346             if (hfp_gsm_callheld_status() == HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED) {
347                 gsm_calls[held_call_index].status = CALL_ACTIVE;
348                 break;
349             }
350             break;
351 
352         case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF:
353             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
354             if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break;
355             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
356             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
357             break;
358 
359         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG:
360         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF:
361             if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
362             if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break;
363             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
364             gsm_calls[initiated_call_index].status = CALL_RESPONSE_HOLD;
365             break;
366 
367         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG:
368         case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF:
369             if (!hfp_gsm_response_held_active()) break;
370             gsm_calls[get_response_held_call_index()].status = CALL_ACTIVE;
371             break;
372 
373         case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG:
374         case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF:
375             if (!hfp_gsm_response_held_active()) break;
376             // gsm_calls[get_response_held_call_index()].status = CALL_NONE;
377             delete_call(get_response_held_call_index());
378             break;
379 
380 
381         case HFP_AG_TERMINATE_CALL_BY_HF:
382             switch (hfp_gsm_call_status()){
383                 case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
384                     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
385                     break;
386                 case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
387                     // gsm_calls[current_call_index].status = CALL_NONE;
388                     delete_call(current_call_index);
389                     break;
390             }
391             break;
392 
393         case HFP_AG_TERMINATE_CALL_BY_AG:
394             switch (hfp_gsm_call_status()){
395                 case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
396                     if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break;
397                     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
398                     break;
399                 case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
400                     callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
401                     // gsm_calls[current_call_index].status = CALL_NONE;
402                     delete_call(current_call_index);
403                     break;
404                 default:
405                     break;
406             }
407             break;
408 
409         case HFP_AG_CALL_DROPPED:
410             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
411             if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break;
412 
413             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
414                 delete_call(i);
415             }
416             break;
417 
418         case HFP_AG_CALL_HOLD_USER_BUSY:
419             // Held or waiting call gets active,
420             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
421             gsm_calls[initiated_call_index].status = CALL_NONE;
422             gsm_calls[held_call_index].status = CALL_ACTIVE;
423             break;
424 
425         case HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:
426             if (index != 0 && index <= HFP_GSM_MAX_NR_CALLS ){
427                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
428                     if (gsm_calls[i].index == index){
429                         delete_call(i);
430                         continue;
431                     }
432                 }
433             } else {
434                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
435                     if (gsm_calls[i].status == CALL_ACTIVE){
436                         delete_call(i);
437                     }
438                 }
439             }
440 
441             if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
442                 gsm_calls[initiated_call_index].status = CALL_ACTIVE;
443             } else {
444                 gsm_calls[held_call_index].status = CALL_ACTIVE;
445             }
446 
447             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
448             break;
449 
450         case HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL:
451             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
452                 if (gsm_calls[i].status == CALL_ACTIVE && gsm_calls[i].index != index){
453                     gsm_calls[i].clip_type = 0;
454                     gsm_calls[i].clip_number[0] = '\0';
455                     gsm_calls[i].status = CALL_HELD;
456                 }
457             }
458 
459             if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
460                 gsm_calls[initiated_call_index].status = CALL_ACTIVE;
461             } else {
462                 gsm_calls[held_call_index].status = CALL_ACTIVE;
463             }
464             callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS;
465             break;
466 
467         case HFP_AG_CALL_HOLD_ADD_HELD_CALL:
468             if (hfp_gsm_callheld_status() != HFP_CALLHELD_STATUS_NO_CALLS_HELD){
469                 for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
470                     if (gsm_calls[i].status == CALL_HELD){
471                         gsm_calls[i].clip_type = 0;
472                         gsm_calls[i].clip_number[0] = '\0';
473                         gsm_calls[i].status = CALL_ACTIVE;
474                     }
475                 }
476             }
477             gsm_calls[initiated_call_index].status = CALL_ACTIVE;
478             break;
479 
480         case HFP_AG_CALL_HOLD_EXIT_AND_JOIN_CALLS:
481             for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){
482                 delete_call(i);
483             }
484             break;
485 
486         case HFP_AG_SET_CLIP:
487             if (initiated_call_index != -1){
488                 hfp_gsm_set_clip(initiated_call_index, type, number);
489                 break;
490             }
491             if (current_call_index != -1){
492                 hfp_gsm_set_clip(current_call_index, type, number);
493                 break;
494             }
495             clip_type = type;
496             strncpy(clip_number, number, sizeof(clip_number));
497             clip_number[sizeof(clip_number)-1] = '\0';
498 
499             break;
500         default:
501             break;
502     }
503 }