~awe/phablet-extras/ofono-nettime-plugin

« back to all changes in this revision

Viewing changes to drivers/atmodem/voicecall.c

  • Committer: Bazaar Package Importer
  • Author(s): Andres Salomon
  • Date: 2009-08-15 15:55:11 UTC
  • Revision ID: james.westby@ubuntu.com-20090815155511-frst06dijguhyfi4
Tags: upstream-0.3
ImportĀ upstreamĀ versionĀ 0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 *  oFono - Open Source Telephony
 
4
 *
 
5
 *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
 
6
 *
 
7
 *  This program is free software; you can redistribute it and/or modify
 
8
 *  it under the terms of the GNU General Public License version 2 as
 
9
 *  published by the Free Software Foundation.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 *
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#define _GNU_SOURCE
 
27
#include <string.h>
 
28
#include <stdlib.h>
 
29
#include <stdio.h>
 
30
 
 
31
#include <glib.h>
 
32
 
 
33
#include <ofono/log.h>
 
34
#include <ofono/modem.h>
 
35
#include "driver.h"
 
36
 
 
37
#include "gatchat.h"
 
38
#include "gatresult.h"
 
39
 
 
40
#include "at.h"
 
41
 
 
42
/* Amount of ms we wait between CLCC calls */
 
43
#define POLL_CLCC_INTERVAL 500
 
44
 
 
45
 /* Amount of time we give for CLIP to arrive before we commence CLCC poll */
 
46
#define CLIP_INTERVAL 200
 
47
 
 
48
static const char *clcc_prefix[] = { "+CLCC:", NULL };
 
49
static const char *none_prefix[] = { NULL };
 
50
 
 
51
/* According to 27.007 COLP is an intermediate status for ATD */
 
52
static const char *atd_prefix[] = { "+COLP:", NULL };
 
53
 
 
54
struct voicecall_data {
 
55
        gboolean poll_clcc;
 
56
        GSList *calls;
 
57
        unsigned int id_list;
 
58
        unsigned int local_release;
 
59
        unsigned int clcc_source;
 
60
};
 
61
 
 
62
static gboolean poll_clcc(gpointer user_data);
 
63
 
 
64
static int class_to_call_type(int cls)
 
65
{
 
66
        switch (cls) {
 
67
        case 1:
 
68
                return 0;
 
69
        case 4:
 
70
                return 2;
 
71
        case 8:
 
72
                return 9;
 
73
        default:
 
74
                return 1;
 
75
        }
 
76
}
 
77
 
 
78
static unsigned int alloc_next_id(struct voicecall_data *d)
 
79
{
 
80
        unsigned int i;
 
81
 
 
82
        for (i = 1; i < sizeof(d->id_list) * 8; i++) {
 
83
                if (d->id_list & (0x1 << i))
 
84
                        continue;
 
85
 
 
86
                d->id_list |= (0x1 << i);
 
87
                return i;
 
88
        }
 
89
 
 
90
        return 0;
 
91
}
 
92
 
 
93
#if 0
 
94
static gboolean alloc_specific_id(struct voicecall_data *d, unsigned int id)
 
95
{
 
96
        if (id < 1 || id > sizeof(d->id_list))
 
97
                return FALSE;
 
98
 
 
99
        if (d->id_list & (0x1 << id))
 
100
                return FALSE;
 
101
 
 
102
        d->id_list |= (0x1 << id);
 
103
 
 
104
        return TRUE;
 
105
}
 
106
#endif
 
107
 
 
108
static void release_id(struct voicecall_data *d, unsigned int id)
 
109
{
 
110
        d->id_list &= ~(0x1 << id);
 
111
}
 
112
 
 
113
#if 0
 
114
static gint call_compare_by_id(gconstpointer a, gconstpointer b)
 
115
{
 
116
        const struct ofono_call *call = a;
 
117
        unsigned int id = GPOINTER_TO_UINT(b);
 
118
 
 
119
        if (id < call->id)
 
120
                return -1;
 
121
 
 
122
        if (id > call->id)
 
123
                return 1;
 
124
 
 
125
        return 0;
 
126
}
 
127
#endif
 
128
 
 
129
static gint call_compare_by_status(gconstpointer a, gconstpointer b)
 
130
{
 
131
        const struct ofono_call *call = a;
 
132
        int status = GPOINTER_TO_INT(b);
 
133
 
 
134
        if (status != call->status)
 
135
                return 1;
 
136
 
 
137
        return 0;
 
138
}
 
139
 
 
140
static gint call_compare(gconstpointer a, gconstpointer b)
 
141
{
 
142
        const struct ofono_call *ca = a;
 
143
        const struct ofono_call *cb = b;
 
144
 
 
145
        if (ca->id < cb->id)
 
146
                return -1;
 
147
 
 
148
        if (ca->id > cb->id)
 
149
                return 1;
 
150
 
 
151
        return 0;
 
152
}
 
153
 
 
154
static struct ofono_call *create_call(struct voicecall_data *d, int type,
 
155
                                        int direction, int status,
 
156
                                        const char *num, int num_type, int clip)
 
157
{
 
158
        struct ofono_call *call;
 
159
 
 
160
        /* Generate a call structure for the waiting call */
 
161
        call = g_try_new0(struct ofono_call, 1);
 
162
 
 
163
        if (!call)
 
164
                return NULL;
 
165
 
 
166
        call->id = alloc_next_id(d);
 
167
        call->type = type;
 
168
        call->direction = direction;
 
169
        call->status = status;
 
170
 
 
171
        if (clip != 2) {
 
172
                strncpy(call->phone_number.number, num,
 
173
                        OFONO_MAX_PHONE_NUMBER_LENGTH);
 
174
                call->phone_number.type = num_type;
 
175
        }
 
176
 
 
177
        call->clip_validity = clip;
 
178
 
 
179
        d->calls = g_slist_insert_sorted(d->calls, call, call_compare);
 
180
 
 
181
        return call;
 
182
}
 
183
 
 
184
static GSList *parse_clcc(GAtResult *result)
 
185
{
 
186
        GAtResultIter iter;
 
187
        GSList *l = NULL;
 
188
        int id, dir, status, type;
 
189
        struct ofono_call *call;
 
190
 
 
191
        g_at_result_iter_init(&iter, result);
 
192
 
 
193
        while (g_at_result_iter_next(&iter, "+CLCC:")) {
 
194
                const char *str = "";
 
195
                int number_type = 129;
 
196
 
 
197
                if (!g_at_result_iter_next_number(&iter, &id))
 
198
                        continue;
 
199
 
 
200
                if (!g_at_result_iter_next_number(&iter, &dir))
 
201
                        continue;
 
202
 
 
203
                if (!g_at_result_iter_next_number(&iter, &status))
 
204
                        continue;
 
205
 
 
206
                if (!g_at_result_iter_next_number(&iter, &type))
 
207
                        continue;
 
208
 
 
209
                if (!g_at_result_iter_skip_next(&iter))
 
210
                        continue;
 
211
 
 
212
                if (g_at_result_iter_next_string(&iter, &str))
 
213
                        g_at_result_iter_next_number(&iter, &number_type);
 
214
 
 
215
                call = g_try_new0(struct ofono_call, 1);
 
216
 
 
217
                if (!call)
 
218
                        break;
 
219
 
 
220
                call->id = id;
 
221
                call->direction = dir;
 
222
                call->status = status;
 
223
                call->type = type;
 
224
                strncpy(call->phone_number.number, str,
 
225
                                OFONO_MAX_PHONE_NUMBER_LENGTH);
 
226
                call->phone_number.type = number_type;
 
227
 
 
228
                if (strlen(call->phone_number.number) > 0)
 
229
                        call->clip_validity = 0;
 
230
                else
 
231
                        call->clip_validity = 2;
 
232
 
 
233
                l = g_slist_insert_sorted(l, call, call_compare);
 
234
        }
 
235
 
 
236
        return l;
 
237
}
 
238
 
 
239
static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data)
 
240
{
 
241
        struct ofono_modem *modem = user_data;
 
242
        struct at_data *at = ofono_modem_get_userdata(modem);
 
243
        GSList *calls;
 
244
        GSList *n, *o;
 
245
        struct ofono_call *nc, *oc;
 
246
        gboolean poll_again = FALSE;
 
247
 
 
248
        dump_response("clcc_poll_cb", ok, result);
 
249
 
 
250
        if (!ok) {
 
251
                ofono_error("We are polling CLCC and CLCC resulted in an error");
 
252
                ofono_error("All bets are off for call management");
 
253
                return;
 
254
        }
 
255
 
 
256
        calls = parse_clcc(result);
 
257
 
 
258
        n = calls;
 
259
        o = at->voicecall->calls;
 
260
 
 
261
        while (n || o) {
 
262
                nc = n ? n->data : NULL;
 
263
                oc = o ? o->data : NULL;
 
264
 
 
265
                if (nc && nc->status >= 2 && nc->status <= 5)
 
266
                        poll_again = TRUE;
 
267
 
 
268
                if (oc && (!nc || (nc->id > oc->id))) {
 
269
                        enum ofono_disconnect_reason reason;
 
270
 
 
271
                        if (at->voicecall->local_release & (0x1 << oc->id))
 
272
                                reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
 
273
                        else
 
274
                                reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
 
275
 
 
276
                        if (!oc->type)
 
277
                                ofono_voicecall_disconnected(modem, oc->id,
 
278
                                                                reason, NULL);
 
279
 
 
280
                        release_id(at->voicecall, oc->id);
 
281
 
 
282
                        o = o->next;
 
283
                } else if (nc && (!oc || (nc->id < oc->id))) {
 
284
                        /* new call, signal it */
 
285
                        if (nc->type == 0)
 
286
                                ofono_voicecall_notify(modem, nc);
 
287
 
 
288
                        n = n->next;
 
289
                } else {
 
290
                        /* Always use the clip_validity from old call
 
291
                         * the only place this is truly told to us is
 
292
                         * in the CLIP notify, the rest are fudged
 
293
                         * anyway.  Useful when RING, CLIP is used,
 
294
                         * and we're forced to use CLCC and clip_validity
 
295
                         * is 1
 
296
                         */
 
297
                        nc->clip_validity = oc->clip_validity;
 
298
 
 
299
                        if (memcmp(nc, oc, sizeof(struct ofono_call)) && !nc->type)
 
300
                                ofono_voicecall_notify(modem, nc);
 
301
 
 
302
                        n = n->next;
 
303
                        o = o->next;
 
304
                }
 
305
        }
 
306
 
 
307
        g_slist_foreach(at->voicecall->calls, (GFunc) g_free, NULL);
 
308
        g_slist_free(at->voicecall->calls);
 
309
 
 
310
        at->voicecall->calls = calls;
 
311
 
 
312
        at->voicecall->local_release = 0;
 
313
 
 
314
        if (poll_again && at->voicecall->poll_clcc &&
 
315
                !at->voicecall->clcc_source)
 
316
                at->voicecall->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
 
317
                                                                poll_clcc,
 
318
                                                                modem);
 
319
}
 
320
 
 
321
static gboolean poll_clcc(gpointer user_data)
 
322
{
 
323
        struct ofono_modem *modem = user_data;
 
324
        struct at_data *at = ofono_modem_get_userdata(modem);
 
325
 
 
326
        g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
327
                                clcc_poll_cb, modem, NULL);
 
328
 
 
329
        at->voicecall->clcc_source = 0;
 
330
 
 
331
        return FALSE;
 
332
}
 
333
 
 
334
static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
 
335
{
 
336
        struct cb_data *cbd = user_data;
 
337
        struct at_data *at = ofono_modem_get_userdata(cbd->modem);
 
338
        ofono_generic_cb_t cb = cbd->cb;
 
339
        unsigned int released_status = GPOINTER_TO_UINT(cbd->user);
 
340
        struct ofono_error error;
 
341
 
 
342
        dump_response("generic_cb", ok, result);
 
343
        decode_at_error(&error, g_at_result_final_response(result));
 
344
 
 
345
        if (ok && released_status) {
 
346
                GSList *l;
 
347
                struct ofono_call *call;
 
348
 
 
349
                for (l = at->voicecall->calls; l; l = l->next) {
 
350
                        call = l->data;
 
351
 
 
352
                        if (released_status & (0x1 << call->status))
 
353
                                at->voicecall->local_release |=
 
354
                                        (0x1 << call->id);
 
355
                }
 
356
        }
 
357
 
 
358
        if (at->voicecall->poll_clcc)
 
359
                g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
360
                                        clcc_poll_cb, cbd->modem, NULL);
 
361
 
 
362
        /* We have to callback after we schedule a poll if required */
 
363
        cb(&error, cbd->data);
 
364
}
 
365
 
 
366
static void release_id_cb(gboolean ok, GAtResult *result,
 
367
                                gpointer user_data)
 
368
{
 
369
        struct cb_data *cbd = user_data;
 
370
        struct at_data *at = ofono_modem_get_userdata(cbd->modem);
 
371
        ofono_generic_cb_t cb = cbd->cb;
 
372
        struct ofono_error error;
 
373
 
 
374
        dump_response("release_id_cb", ok, result);
 
375
        decode_at_error(&error, g_at_result_final_response(result));
 
376
 
 
377
        if (ok)
 
378
                at->voicecall->local_release = GPOINTER_TO_UINT(cbd->user);
 
379
 
 
380
        if (at->voicecall->poll_clcc)
 
381
                g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
382
                                        clcc_poll_cb, cbd->modem, NULL);
 
383
 
 
384
        /* We have to callback after we schedule a poll if required */
 
385
        cb(&error, cbd->data);
 
386
}
 
387
static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
 
388
{
 
389
        struct cb_data *cbd = user_data;
 
390
        struct at_data *at = ofono_modem_get_userdata(cbd->modem);
 
391
        ofono_generic_cb_t cb = cbd->cb;
 
392
        GAtResultIter iter;
 
393
        const char *num;
 
394
        int type = 128;
 
395
        int validity = 2;
 
396
        struct ofono_error error;
 
397
        struct ofono_call *call;
 
398
 
 
399
        dump_response("atd_cb", ok, result);
 
400
 
 
401
        decode_at_error(&error, g_at_result_final_response(result));
 
402
 
 
403
        if (!ok)
 
404
                goto out;
 
405
 
 
406
        g_at_result_iter_init(&iter, result);
 
407
 
 
408
        if (g_at_result_iter_next(&iter, "+COLP:")) {
 
409
                g_at_result_iter_next_string(&iter, &num);
 
410
                g_at_result_iter_next_number(&iter, &type);
 
411
 
 
412
                if (strlen(num) > 0)
 
413
                        validity = 0;
 
414
                else
 
415
                        validity = 2;
 
416
 
 
417
                ofono_debug("colp_notify: %s %d %d", num, type, validity);
 
418
        }
 
419
 
 
420
        /* Generate a voice call that was just dialed, we guess the ID */
 
421
        call = create_call(at->voicecall, 0, 0, 2, num, type, validity);
 
422
 
 
423
        if (!call) {
 
424
                ofono_error("Unable to allocate call, call tracking will fail!");
 
425
                return;
 
426
        }
 
427
 
 
428
        /* Telephonyd will generate a call with the dialed number
 
429
         * inside its dial callback.  Unless we got COLP information
 
430
         * we do not need to communicate that a call is being
 
431
         * dialed
 
432
         */
 
433
        if (validity != 2)
 
434
                ofono_voicecall_notify(cbd->modem, call);
 
435
 
 
436
        if (at->voicecall->poll_clcc && !at->voicecall->clcc_source)
 
437
                at->voicecall->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
 
438
                                                                poll_clcc,
 
439
                                                                cbd->modem);
 
440
 
 
441
out:
 
442
        cb(&error, cbd->data);
 
443
}
 
444
 
 
445
static void at_dial(struct ofono_modem *modem,
 
446
                        const struct ofono_phone_number *ph,
 
447
                        enum ofono_clir_option clir, enum ofono_cug_option cug,
 
448
                        ofono_generic_cb_t cb, void *data)
 
449
{
 
450
        struct at_data *at = ofono_modem_get_userdata(modem);
 
451
        struct cb_data *cbd = cb_data_new(modem, cb, data);
 
452
        char buf[256];
 
453
 
 
454
        if (!cbd)
 
455
                goto error;
 
456
 
 
457
        if (ph->type == 145)
 
458
                sprintf(buf, "ATD+%s", ph->number);
 
459
        else
 
460
                sprintf(buf, "ATD%s", ph->number);
 
461
 
 
462
        switch (clir) {
 
463
        case OFONO_CLIR_OPTION_INVOCATION:
 
464
                strcat(buf, "I");
 
465
                break;
 
466
        case OFONO_CLIR_OPTION_SUPPRESSION:
 
467
                strcat(buf, "i");
 
468
                break;
 
469
        default:
 
470
                break;
 
471
        }
 
472
 
 
473
        switch (cug) {
 
474
        case OFONO_CUG_OPTION_INVOCATION:
 
475
                strcat(buf, "G");
 
476
                break;
 
477
        default:
 
478
                break;
 
479
        }
 
480
 
 
481
        strcat(buf, ";");
 
482
 
 
483
        if (g_at_chat_send(at->parser, buf, atd_prefix,
 
484
                                atd_cb, cbd, g_free) > 0)
 
485
                return;
 
486
 
 
487
error:
 
488
        if (cbd)
 
489
                g_free(cbd);
 
490
 
 
491
        {
 
492
                DECLARE_FAILURE(error);
 
493
                cb(&error, data);
 
494
        }
 
495
}
 
496
 
 
497
static void at_template(const char *cmd, struct ofono_modem *modem,
 
498
                        GAtResultFunc result_cb, unsigned int released_status,
 
499
                        ofono_generic_cb_t cb, void *data)
 
500
{
 
501
        struct at_data *at = ofono_modem_get_userdata(modem);
 
502
        struct cb_data *cbd = cb_data_new(modem, cb, data);
 
503
 
 
504
        if (!cbd)
 
505
                goto error;
 
506
 
 
507
        cbd->user = GUINT_TO_POINTER(released_status);
 
508
 
 
509
        if (g_at_chat_send(at->parser, cmd, none_prefix,
 
510
                                result_cb, cbd, g_free) > 0)
 
511
                return;
 
512
 
 
513
error:
 
514
        if (cbd)
 
515
                g_free(cbd);
 
516
 
 
517
        {
 
518
                DECLARE_FAILURE(error);
 
519
                cb(&error, data);
 
520
        }
 
521
}
 
522
 
 
523
static void at_answer(struct ofono_modem *modem, ofono_generic_cb_t cb, void *data)
 
524
{
 
525
        at_template("ATA", modem, generic_cb, 0, cb, data);
 
526
}
 
527
 
 
528
static void at_hangup(struct ofono_modem *modem, ofono_generic_cb_t cb, void *data)
 
529
{
 
530
        /* Hangup all calls */
 
531
        at_template("AT+CHUP", modem, generic_cb, 0x3f, cb, data);
 
532
}
 
533
 
 
534
static void clcc_cb(gboolean ok, GAtResult *result, gpointer user_data)
 
535
{
 
536
        struct cb_data *cbd = user_data;
 
537
        ofono_call_list_cb_t cb = cbd->cb;
 
538
        struct ofono_error error;
 
539
        GSList *calls = NULL;
 
540
        GSList *l;
 
541
        struct ofono_call *list;
 
542
        int num;
 
543
 
 
544
        dump_response("clcc_cb", ok, result);
 
545
        decode_at_error(&error, g_at_result_final_response(result));
 
546
 
 
547
        if (!ok) {
 
548
                cb(&error, 0, NULL, cbd->data);
 
549
                goto out;
 
550
        }
 
551
 
 
552
        calls = parse_clcc(result);
 
553
 
 
554
        if (calls == NULL) {
 
555
                DECLARE_FAILURE(e);
 
556
                cb(&e, 0, NULL, cbd->data);
 
557
                goto out;
 
558
        }
 
559
 
 
560
        list = g_try_new0(struct ofono_call, g_slist_length(calls));
 
561
 
 
562
        if (!list) {
 
563
                DECLARE_FAILURE(e);
 
564
                cb(&e, 0, NULL, cbd->data);
 
565
                goto out;
 
566
        }
 
567
 
 
568
        for (num = 0, l = calls; l; l = l->next, num++)
 
569
                memcpy(&list[num], l->data, sizeof(struct ofono_call));
 
570
 
 
571
        cb(&error, num, list, cbd->data);
 
572
 
 
573
        g_free(list);
 
574
 
 
575
out:
 
576
        g_slist_foreach(calls, (GFunc) g_free, NULL);
 
577
        g_slist_free(calls);
 
578
}
 
579
 
 
580
static void at_list_calls(struct ofono_modem *modem, ofono_call_list_cb_t cb,
 
581
                                void *data)
 
582
{
 
583
        struct at_data *at = ofono_modem_get_userdata(modem);
 
584
        struct cb_data *cbd = cb_data_new(modem, cb, data);
 
585
 
 
586
        if (!cbd)
 
587
                goto error;
 
588
 
 
589
        if (g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
590
                                clcc_cb, cbd, g_free) > 0)
 
591
                return;
 
592
 
 
593
error:
 
594
        if (cbd)
 
595
                g_free(cbd);
 
596
 
 
597
        {
 
598
                DECLARE_FAILURE(error);
 
599
                cb(&error, 0, NULL, data);
 
600
        }
 
601
 
 
602
}
 
603
 
 
604
static void at_hold_all_active(struct ofono_modem *modem, ofono_generic_cb_t cb,
 
605
                                void *data)
 
606
{
 
607
        at_template("AT+CHLD=2", modem, generic_cb, 0, cb, data);
 
608
}
 
609
 
 
610
static void at_release_all_held(struct ofono_modem *modem, ofono_generic_cb_t cb,
 
611
                                void *data)
 
612
{
 
613
        unsigned int held_status = 0x1 << 1;
 
614
        at_template("AT+CHLD=0", modem, generic_cb, held_status, cb, data);
 
615
}
 
616
 
 
617
static void at_set_udub(struct ofono_modem *modem, ofono_generic_cb_t cb, void *data)
 
618
{
 
619
        unsigned int incoming_or_waiting = (0x1 << 4) | (0x1 << 5);
 
620
        at_template("AT+CHLD=0", modem, generic_cb, incoming_or_waiting,
 
621
                        cb, data);
 
622
}
 
623
 
 
624
static void at_release_all_active(struct ofono_modem *modem, ofono_generic_cb_t cb,
 
625
                                        void *data)
 
626
{
 
627
        at_template("AT+CHLD=1", modem, generic_cb, 0x1, cb, data);
 
628
}
 
629
 
 
630
static void at_release_specific(struct ofono_modem *modem, int id,
 
631
                                ofono_generic_cb_t cb, void *data)
 
632
{
 
633
        struct at_data *at = ofono_modem_get_userdata(modem);
 
634
        struct cb_data *cbd = cb_data_new(modem, cb, data);
 
635
        char buf[32];
 
636
 
 
637
        if (!cbd)
 
638
                goto error;
 
639
 
 
640
        sprintf(buf, "AT+CHLD=1%d", id);
 
641
        cbd->user = GINT_TO_POINTER(id);
 
642
 
 
643
        if (g_at_chat_send(at->parser, buf, none_prefix,
 
644
                                release_id_cb, cbd, g_free) > 0)
 
645
                return;
 
646
 
 
647
error:
 
648
        if (cbd)
 
649
                g_free(cbd);
 
650
 
 
651
        {
 
652
                DECLARE_FAILURE(error);
 
653
                cb(&error, data);
 
654
        }
 
655
}
 
656
 
 
657
static void at_private_chat(struct ofono_modem *modem, int id,
 
658
                                ofono_generic_cb_t cb, void *data)
 
659
{
 
660
        char buf[32];
 
661
 
 
662
        sprintf(buf, "AT+CHLD=2%d", id);
 
663
        at_template(buf, modem, generic_cb, 0, cb, data);
 
664
}
 
665
 
 
666
static void at_create_multiparty(struct ofono_modem *modem, ofono_generic_cb_t cb,
 
667
                                void *data)
 
668
{
 
669
        at_template("AT+CHLD=3", modem, generic_cb, 0, cb, data);
 
670
}
 
671
 
 
672
static void at_transfer(struct ofono_modem *modem, ofono_generic_cb_t cb,
 
673
                                void *data)
 
674
{
 
675
        /* Held & Active */
 
676
        unsigned int transfer = 0x1 | 0x2;
 
677
 
 
678
        /* Transfer can puts held & active calls together and disconnects
 
679
         * from both.  However, some networks support transfering of
 
680
         * dialing/ringing calls as well.
 
681
         */
 
682
        transfer |= 0x4 | 0x8;
 
683
 
 
684
        at_template("AT+CHLD=4", modem, generic_cb, transfer, cb, data);
 
685
}
 
686
 
 
687
static void at_deflect(struct ofono_modem *modem,
 
688
                        const struct ofono_phone_number *ph,
 
689
                        ofono_generic_cb_t cb, void *data)
 
690
{
 
691
        char buf[128];
 
692
        unsigned int incoming_or_waiting = (0x1 << 4) | (0x1 << 5);
 
693
 
 
694
        sprintf(buf, "AT+CTFR=%s,%d", ph->number, ph->type);
 
695
        at_template(buf, modem, generic_cb, incoming_or_waiting, cb, data);
 
696
}
 
697
 
 
698
static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
 
699
{
 
700
        struct cb_data *cbd = user_data;
 
701
        ofono_generic_cb_t cb = cbd->cb;
 
702
        struct ofono_error error;
 
703
 
 
704
        dump_response("vts_cb", ok, result);
 
705
        decode_at_error(&error, g_at_result_final_response(result));
 
706
        cb(&error, cbd->data);
 
707
}
 
708
 
 
709
static void at_send_dtmf(struct ofono_modem *modem, const char *dtmf,
 
710
                        ofono_generic_cb_t cb, void *data)
 
711
{
 
712
        struct at_data *at = ofono_modem_get_userdata(modem);
 
713
        struct cb_data *cbd = cb_data_new(modem, cb, data);
 
714
        int len = strlen(dtmf);
 
715
        int s;
 
716
        int i;
 
717
        char *buf;
 
718
 
 
719
        if (!cbd)
 
720
                goto error;
 
721
 
 
722
        /* strlen("+VTS=\"T\";") = 9 + initial AT + null */
 
723
        buf = g_try_new(char, len * 9 + 3);
 
724
 
 
725
        if (!buf)
 
726
                goto error;
 
727
 
 
728
        s = sprintf(buf, "AT+VTS=\"%c\"", dtmf[0]);
 
729
 
 
730
        for (i = 1; i < len; i++)
 
731
                s += sprintf(buf + s, ";+VTS=\"%c\"", dtmf[i]);
 
732
 
 
733
        s = g_at_chat_send(at->parser, buf, none_prefix,
 
734
                                vts_cb, cbd, g_free);
 
735
 
 
736
        g_free(buf);
 
737
 
 
738
        if (s > 0)
 
739
                return;
 
740
 
 
741
error:
 
742
        if (cbd)
 
743
                g_free(cbd);
 
744
 
 
745
        {
 
746
                DECLARE_FAILURE(error);
 
747
                cb(&error, data);
 
748
        }
 
749
}
 
750
 
 
751
static void ring_notify(GAtResult *result, gpointer user_data)
 
752
{
 
753
        struct ofono_modem *modem = user_data;
 
754
        struct at_data *at = ofono_modem_get_userdata(modem);
 
755
        struct ofono_call *call;
 
756
 
 
757
        dump_response("ring_notify", TRUE, result);
 
758
 
 
759
        /* RING can repeat, ignore if we already have an incoming call */
 
760
        if (g_slist_find_custom(at->voicecall->calls, GINT_TO_POINTER(4),
 
761
                                call_compare_by_status))
 
762
                return;
 
763
 
 
764
        /* Generate an incoming call of unknown type */
 
765
        call = create_call(at->voicecall, 9, 1, 4, NULL, 128, 2);
 
766
 
 
767
        if (!call) {
 
768
                ofono_error("Couldn't create call, call management is fubar!");
 
769
                return;
 
770
        }
 
771
 
 
772
        /* We don't know the call type, we must run clcc */
 
773
        at->voicecall->clcc_source = g_timeout_add(CLIP_INTERVAL,
 
774
                                                        poll_clcc, modem);
 
775
}
 
776
 
 
777
static void cring_notify(GAtResult *result, gpointer user_data)
 
778
{
 
779
        struct ofono_modem *modem = user_data;
 
780
        struct at_data *at = ofono_modem_get_userdata(modem);
 
781
        GAtResultIter iter;
 
782
        const char *line;
 
783
        int type;
 
784
        struct ofono_call *call;
 
785
 
 
786
        dump_response("cring_notify", TRUE, result);
 
787
 
 
788
        /* CRING can repeat, ignore if we already have an incoming call */
 
789
        if (g_slist_find_custom(at->voicecall->calls, GINT_TO_POINTER(4),
 
790
                                call_compare_by_status))
 
791
                return;
 
792
 
 
793
        g_at_result_iter_init(&iter, result);
 
794
 
 
795
        if (!g_at_result_iter_next(&iter, "+CRING:"))
 
796
                return;
 
797
 
 
798
        line = g_at_result_iter_raw_line(&iter);
 
799
 
 
800
        if (line == NULL)
 
801
                return;
 
802
 
 
803
        /* Ignore everything that is not voice for now */
 
804
        if (!strcasecmp(line, "VOICE"))
 
805
                type = 0;
 
806
        else
 
807
                type = 9;
 
808
 
 
809
        /* Generate an incoming call */
 
810
        call = create_call(at->voicecall, type, 1, 4, NULL, 128, 2);
 
811
 
 
812
        /* We have a call, and call type but don't know the number and
 
813
         * must wait for the CLIP to arrive before announcing the call.
 
814
         * So we wait, and schedule the clcc call.  If the CLIP arrives
 
815
         * earlier, we announce the call there
 
816
         */
 
817
        at->voicecall->clcc_source =
 
818
                g_timeout_add(CLIP_INTERVAL, poll_clcc, modem);
 
819
 
 
820
        ofono_debug("cring_notify");
 
821
}
 
822
 
 
823
static void clip_notify(GAtResult *result, gpointer user_data)
 
824
{
 
825
        struct ofono_modem *modem = user_data;
 
826
        struct at_data *at = ofono_modem_get_userdata(modem);
 
827
        GAtResultIter iter;
 
828
        const char *num;
 
829
        int type, validity;
 
830
        GSList *l;
 
831
        struct ofono_call *call;
 
832
 
 
833
        dump_response("clip_notify", TRUE, result);
 
834
 
 
835
        l = g_slist_find_custom(at->voicecall->calls, GINT_TO_POINTER(4),
 
836
                                call_compare_by_status);
 
837
 
 
838
        if (l == NULL) {
 
839
                ofono_error("CLIP for unknown call");
 
840
                return;
 
841
        }
 
842
 
 
843
        g_at_result_iter_init(&iter, result);
 
844
 
 
845
        if (!g_at_result_iter_next(&iter, "+CLIP:"))
 
846
                return;
 
847
 
 
848
        if (!g_at_result_iter_next_string(&iter, &num))
 
849
                return;
 
850
 
 
851
        if (!g_at_result_iter_next_number(&iter, &type))
 
852
                return;
 
853
 
 
854
        if (strlen(num) > 0)
 
855
                validity = 0;
 
856
        else
 
857
                validity = 2;
 
858
 
 
859
        /* Skip subaddr, satype and alpha */
 
860
        g_at_result_iter_skip_next(&iter);
 
861
        g_at_result_iter_skip_next(&iter);
 
862
        g_at_result_iter_skip_next(&iter);
 
863
 
 
864
        /* If we have CLI validity field, override our guessed value */
 
865
        g_at_result_iter_next_number(&iter, &validity);
 
866
 
 
867
        ofono_debug("clip_notify: %s %d %d", num, type, validity);
 
868
 
 
869
        call = l->data;
 
870
 
 
871
        strncpy(call->phone_number.number, num,
 
872
                OFONO_MAX_PHONE_NUMBER_LENGTH);
 
873
        call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
 
874
        call->phone_number.type = type;
 
875
        call->clip_validity = validity;
 
876
 
 
877
        if (call->type == 0)
 
878
                ofono_voicecall_notify(modem, call);
 
879
 
 
880
        /* We started a CLCC, but the CLIP arrived and the call type
 
881
         * is known.  If we don't need to poll, cancel the GSource
 
882
         */
 
883
        if (call->type != 9 && !at->voicecall->poll_clcc &&
 
884
                at->voicecall->clcc_source &&
 
885
                        g_source_remove(at->voicecall->clcc_source))
 
886
                at->voicecall->clcc_source = 0;
 
887
}
 
888
 
 
889
static void ccwa_notify(GAtResult *result, gpointer user_data)
 
890
{
 
891
        struct ofono_modem *modem = user_data;
 
892
        struct at_data *at = ofono_modem_get_userdata(modem);
 
893
        GAtResultIter iter;
 
894
        const char *num;
 
895
        int num_type, validity, cls;
 
896
        struct ofono_call *call;
 
897
 
 
898
        dump_response("ccwa_notify", TRUE, result);
 
899
 
 
900
        g_at_result_iter_init(&iter, result);
 
901
 
 
902
        if (!g_at_result_iter_next(&iter, "+CCWA:"))
 
903
                return;
 
904
 
 
905
        if (!g_at_result_iter_next_string(&iter, &num))
 
906
                return;
 
907
 
 
908
        if (!g_at_result_iter_next_number(&iter, &num_type))
 
909
                return;
 
910
 
 
911
        if (!g_at_result_iter_next_number(&iter, &cls))
 
912
                return;
 
913
 
 
914
        /* Skip alpha field */
 
915
        g_at_result_iter_skip_next(&iter);
 
916
 
 
917
        if (strlen(num) > 0)
 
918
                validity = 0;
 
919
        else
 
920
                validity = 2;
 
921
 
 
922
        /* If we have CLI validity field, override our guessed value */
 
923
        g_at_result_iter_next_number(&iter, &validity);
 
924
 
 
925
        ofono_debug("ccwa_notify: %s %d %d %d", num, num_type, cls, validity);
 
926
 
 
927
        call = create_call(at->voicecall, class_to_call_type(cls), 1, 5,
 
928
                                num, num_type, validity);
 
929
 
 
930
        if (!call) {
 
931
                ofono_error("malloc call structfailed. Call management is fubar");
 
932
                return;
 
933
        }
 
934
 
 
935
        if (call->type == 0) /* Only notify voice calls */
 
936
                ofono_voicecall_notify(modem, call);
 
937
 
 
938
        if (at->voicecall->poll_clcc && !at->voicecall->clcc_source)
 
939
                at->voicecall->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
 
940
                                                                poll_clcc,
 
941
                                                                modem);
 
942
}
 
943
 
 
944
static void no_carrier_notify(GAtResult *result, gpointer user_data)
 
945
{
 
946
        struct ofono_modem *modem = user_data;
 
947
        struct at_data *at = ofono_modem_get_userdata(modem);
 
948
 
 
949
        if (at->voicecall->poll_clcc)
 
950
                g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
951
                                        clcc_poll_cb, modem, NULL);
 
952
}
 
953
 
 
954
static void no_answer_notify(GAtResult *result, gpointer user_data)
 
955
{
 
956
        struct ofono_modem *modem = user_data;
 
957
        struct at_data *at = ofono_modem_get_userdata(modem);
 
958
 
 
959
        if (at->voicecall->poll_clcc)
 
960
                g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
961
                                        clcc_poll_cb, modem, NULL);
 
962
}
 
963
 
 
964
static void busy_notify(GAtResult *result, gpointer user_data)
 
965
{
 
966
        struct ofono_modem *modem = user_data;
 
967
        struct at_data *at = ofono_modem_get_userdata(modem);
 
968
 
 
969
        /* Call was rejected, most likely due to network congestion
 
970
         * or UDUB on the other side
 
971
         * TODO: Handle UDUB or other conditions somehow
 
972
         */
 
973
        if (at->voicecall->poll_clcc)
 
974
                g_at_chat_send(at->parser, "AT+CLCC", clcc_prefix,
 
975
                                        clcc_poll_cb, modem, NULL);
 
976
}
 
977
 
 
978
static void cssi_notify(GAtResult *result, gpointer user_data)
 
979
{
 
980
        struct ofono_modem *modem = user_data;
 
981
        GAtResultIter iter;
 
982
        int code1, index;
 
983
 
 
984
        dump_response("cssi_notify", TRUE, result);
 
985
 
 
986
        g_at_result_iter_init(&iter, result);
 
987
 
 
988
        if (!g_at_result_iter_next(&iter, "+CSSI:"))
 
989
                return;
 
990
 
 
991
        if (!g_at_result_iter_next_number(&iter, &code1))
 
992
                return;
 
993
 
 
994
        if (!g_at_result_iter_next_number(&iter, &index))
 
995
                index = 0;
 
996
 
 
997
        ofono_cssi_notify(modem, code1, index);
 
998
}
 
999
 
 
1000
static void cssu_notify(GAtResult *result, gpointer user_data)
 
1001
{
 
1002
        struct ofono_modem *modem = user_data;
 
1003
        GAtResultIter iter;
 
1004
        int code2;
 
1005
        int index = -1;
 
1006
        const char *num;
 
1007
        struct ofono_phone_number ph;
 
1008
 
 
1009
        ph.number[0] = '\0';
 
1010
        ph.type = 129;
 
1011
 
 
1012
        dump_response("cssu_notify", TRUE, result);
 
1013
 
 
1014
        g_at_result_iter_init(&iter, result);
 
1015
 
 
1016
        if (!g_at_result_iter_next(&iter, "+CSSU:"))
 
1017
                return;
 
1018
 
 
1019
        if (!g_at_result_iter_next_number(&iter, &code2))
 
1020
                return;
 
1021
 
 
1022
        /* This field is optional, if we can't read it, try to skip it */
 
1023
        if (!g_at_result_iter_next_number(&iter, &index) &&
 
1024
                        !g_at_result_iter_skip_next(&iter))
 
1025
                goto out;
 
1026
 
 
1027
        if (!g_at_result_iter_next_string(&iter, &num))
 
1028
                goto out;
 
1029
 
 
1030
        strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH);
 
1031
 
 
1032
        if (!g_at_result_iter_next_number(&iter, &ph.type))
 
1033
                return;
 
1034
 
 
1035
out:
 
1036
        ofono_cssu_notify(modem, code2, index, &ph);
 
1037
}
 
1038
 
 
1039
static struct ofono_voicecall_ops ops = {
 
1040
        .dial                   = at_dial,
 
1041
        .answer                 = at_answer,
 
1042
        .hangup                 = at_hangup,
 
1043
        .list_calls             = at_list_calls,
 
1044
        .hold_all_active        = at_hold_all_active,
 
1045
        .release_all_held       = at_release_all_held,
 
1046
        .set_udub               = at_set_udub,
 
1047
        .release_all_active     = at_release_all_active,
 
1048
        .release_specific       = at_release_specific,
 
1049
        .private_chat           = at_private_chat,
 
1050
        .create_multiparty      = at_create_multiparty,
 
1051
        .transfer               = at_transfer,
 
1052
        .deflect                = at_deflect,
 
1053
        .swap_without_accept    = NULL,
 
1054
        .send_tones             = at_send_dtmf
 
1055
};
 
1056
 
 
1057
static void at_voicecall_initialized(gboolean ok, GAtResult *result,
 
1058
                                        gpointer user_data)
 
1059
{
 
1060
        struct ofono_modem *modem = user_data;
 
1061
        struct at_data *at = ofono_modem_get_userdata(modem);
 
1062
 
 
1063
        ofono_debug("voicecall_init: registering to notifications");
 
1064
 
 
1065
        g_at_chat_register(at->parser, "RING",
 
1066
                                ring_notify, FALSE, modem, NULL);
 
1067
        g_at_chat_register(at->parser, "+CRING:",
 
1068
                                cring_notify, FALSE, modem, NULL);
 
1069
        g_at_chat_register(at->parser, "+CLIP:",
 
1070
                                clip_notify, FALSE, modem, NULL);
 
1071
        g_at_chat_register(at->parser, "+CCWA:",
 
1072
                                ccwa_notify, FALSE, modem, NULL);
 
1073
        g_at_chat_register(at->parser, "+CSSI:",
 
1074
                                cssi_notify, FALSE, modem, NULL);
 
1075
        g_at_chat_register(at->parser, "+CSSU:",
 
1076
                                cssu_notify, FALSE, modem, NULL);
 
1077
 
 
1078
        /* Modems with 'better' call progress indicators should
 
1079
         * probably not even bother registering to these
 
1080
         */
 
1081
        g_at_chat_register(at->parser, "NO CARRIER",
 
1082
                                no_carrier_notify, FALSE, modem, NULL);
 
1083
        g_at_chat_register(at->parser, "NO ANSWER",
 
1084
                                no_answer_notify, FALSE, modem, NULL);
 
1085
        g_at_chat_register(at->parser, "BUSY",
 
1086
                                busy_notify, FALSE, modem, NULL);
 
1087
 
 
1088
        ofono_voicecall_register(modem, &ops);
 
1089
}
 
1090
 
 
1091
void at_voicecall_init(struct ofono_modem *modem)
 
1092
{
 
1093
        struct at_data *at = ofono_modem_get_userdata(modem);
 
1094
 
 
1095
        at->voicecall = g_try_new0(struct voicecall_data, 1);
 
1096
 
 
1097
        if (!at->voicecall)
 
1098
                return;
 
1099
 
 
1100
        at->voicecall->poll_clcc = TRUE;
 
1101
 
 
1102
        ofono_debug("Sending voice initialization commands");
 
1103
 
 
1104
        g_at_chat_send(at->parser, "AT+CRC=1", NULL, NULL, NULL, NULL);
 
1105
        g_at_chat_send(at->parser, "AT+CLIP=1", NULL, NULL, NULL, NULL);
 
1106
        g_at_chat_send(at->parser, "AT+COLP=1", NULL, NULL, NULL, NULL);
 
1107
        g_at_chat_send(at->parser, "AT+CSSN=1,1", NULL, NULL, NULL, NULL);
 
1108
        g_at_chat_send(at->parser, "AT+CCWA=1", NULL,
 
1109
                                at_voicecall_initialized, modem, NULL);
 
1110
}
 
1111
 
 
1112
void at_voicecall_exit(struct ofono_modem *modem)
 
1113
{
 
1114
        struct at_data *at = ofono_modem_get_userdata(modem);
 
1115
 
 
1116
        if (!at->voicecall)
 
1117
                return;
 
1118
 
 
1119
        g_slist_foreach(at->voicecall->calls, (GFunc) g_free, NULL);
 
1120
        g_slist_free(at->voicecall->calls);
 
1121
 
 
1122
        g_free(at->voicecall);
 
1123
        at->voicecall = NULL;
 
1124
 
 
1125
        ofono_voicecall_unregister(modem);
 
1126
}