Initialize Tizen 2.3
[framework/connectivity/bluez.git] / mobile / audio / telephony-maemo6.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2008-2010  Nokia Corporation
6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <glib.h>
36 #include <dbus/dbus.h>
37 #include <gdbus.h>
38
39 #include <bluetooth/sdp.h>
40
41 #include "log.h"
42 #include "telephony.h"
43 #include "error.h"
44
45 /* SSC D-Bus definitions */
46 #define SSC_DBUS_NAME  "com.nokia.phone.SSC"
47 #define SSC_DBUS_IFACE "com.nokia.phone.SSC"
48 #define SSC_DBUS_PATH  "/com/nokia/phone/SSC"
49
50 /* libcsnet D-Bus definitions */
51 #define CSD_CSNET_BUS_NAME      "com.nokia.csd.CSNet"
52 #define CSD_CSNET_PATH          "/com/nokia/csd/csnet"
53 #define CSD_CSNET_IFACE         "com.nokia.csd.CSNet"
54 #define CSD_CSNET_REGISTRATION  "com.nokia.csd.CSNet.NetworkRegistration"
55 #define CSD_CSNET_OPERATOR      "com.nokia.csd.CSNet.NetworkOperator"
56 #define CSD_CSNET_SIGNAL        "com.nokia.csd.CSNet.SignalStrength"
57
58 enum net_registration_status {
59         NETWORK_REG_STATUS_HOME,
60         NETWORK_REG_STATUS_ROAMING,
61         NETWORK_REG_STATUS_OFFLINE,
62         NETWORK_REG_STATUS_SEARCHING,
63         NETWORK_REG_STATUS_NO_SIM,
64         NETWORK_REG_STATUS_POWEROFF,
65         NETWORK_REG_STATUS_POWERSAFE,
66         NETWORK_REG_STATUS_NO_COVERAGE,
67         NETWORK_REG_STATUS_REJECTED,
68         NETWORK_REG_STATUS_UNKOWN
69 };
70
71 /* CSD CALL plugin D-Bus definitions */
72 #define CSD_CALL_BUS_NAME       "com.nokia.csd.Call"
73 #define CSD_CALL_INTERFACE      "com.nokia.csd.Call"
74 #define CSD_CALL_INSTANCE       "com.nokia.csd.Call.Instance"
75 #define CSD_CALL_CONFERENCE     "com.nokia.csd.Call.Conference"
76 #define CSD_CALL_PATH           "/com/nokia/csd/call"
77 #define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
78
79 /* Call status values as exported by the CSD CALL plugin */
80 #define CSD_CALL_STATUS_IDLE                    0
81 #define CSD_CALL_STATUS_CREATE                  1
82 #define CSD_CALL_STATUS_COMING                  2
83 #define CSD_CALL_STATUS_PROCEEDING              3
84 #define CSD_CALL_STATUS_MO_ALERTING             4
85 #define CSD_CALL_STATUS_MT_ALERTING             5
86 #define CSD_CALL_STATUS_WAITING                 6
87 #define CSD_CALL_STATUS_ANSWERED                7
88 #define CSD_CALL_STATUS_ACTIVE                  8
89 #define CSD_CALL_STATUS_MO_RELEASE              9
90 #define CSD_CALL_STATUS_MT_RELEASE              10
91 #define CSD_CALL_STATUS_HOLD_INITIATED          11
92 #define CSD_CALL_STATUS_HOLD                    12
93 #define CSD_CALL_STATUS_RETRIEVE_INITIATED      13
94 #define CSD_CALL_STATUS_RECONNECT_PENDING       14
95 #define CSD_CALL_STATUS_TERMINATED              15
96 #define CSD_CALL_STATUS_SWAP_INITIATED          16
97
98 #define CALL_FLAG_NONE                          0
99 #define CALL_FLAG_PRESENTATION_ALLOWED          0x01
100 #define CALL_FLAG_PRESENTATION_RESTRICTED       0x02
101
102 /* SIM Phonebook D-Bus definitions */
103 #define CSD_SIMPB_BUS_NAME                      "com.nokia.csd.SIM"
104 #define CSD_SIMPB_INTERFACE                     "com.nokia.csd.SIM.Phonebook"
105 #define CSD_SIMPB_PATH                          "/com/nokia/csd/sim/phonebook"
106
107 #define CSD_SIMPB_TYPE_ADN                      "ADN"
108 #define CSD_SIMPB_TYPE_FDN                      "FDN"
109 #define CSD_SIMPB_TYPE_SDN                      "SDN"
110 #define CSD_SIMPB_TYPE_VMBX                     "VMBX"
111 #define CSD_SIMPB_TYPE_MBDN                     "MBDN"
112 #define CSD_SIMPB_TYPE_EN                       "EN"
113 #define CSD_SIMPB_TYPE_MSISDN                   "MSISDN"
114
115 /* OHM plugin D-Bus definitions */
116 #define OHM_BUS_NAME            "com.nokia.NonGraphicFeedback1"
117 #define OHM_INTERFACE           "com.nokia.NonGraphicFeedback1"
118 #define OHM_PATH                "/com/nokia/NonGraphicFeedback1"
119
120 /* tone-genenerator D-Bus definitions */
121 #define TONEGEN_BUS_NAME        "com.Nokia.Telephony.Tones"
122 #define TONEGEN_INTERFACE       "com.Nokia.Telephony.Tones"
123 #define TONEGEN_PATH            "/com/Nokia/Telephony/Tones"
124
125 /* tone-generator DTMF definitions */
126 #define DTMF_ASTERISK   10
127 #define DTMF_HASHMARK   11
128 #define DTMF_A          12
129 #define DTMF_B          13
130 #define DTMF_C          14
131 #define DTMF_D          15
132
133 #define FEEDBACK_TONE_DURATION                  200
134
135 struct csd_call {
136         char *object_path;
137         int status;
138         gboolean originating;
139         gboolean emergency;
140         gboolean on_hold;
141         gboolean conference;
142         char *number;
143         gboolean setup;
144 };
145
146 static struct {
147         char *operator_name;
148         uint8_t status;
149         int32_t signal_bars;
150 } net = {
151         .operator_name = NULL,
152         .status = NETWORK_REG_STATUS_UNKOWN,
153         /* Init as 0 meaning inactive mode. In modem power off state
154          * can be be -1, but we treat all values as 0s regardless
155          * inactive or power off. */
156         .signal_bars = 0,
157 };
158
159 struct pending_req {
160         DBusPendingCall *call;
161         void *user_data;
162 };
163
164 static int get_property(const char *iface, const char *prop);
165
166 static DBusConnection *connection = NULL;
167
168 static GSList *calls = NULL;
169 static GSList *watches = NULL;
170 static GSList *pending = NULL;
171
172 /* Reference count for determining the call indicator status */
173 static GSList *active_calls = NULL;
174
175 /* Queue of DTMF tones to play */
176 static GSList *tones = NULL;
177 static guint create_tones_timer = 0;
178
179 static char *msisdn = NULL;     /* Subscriber number */
180 static char *vmbx = NULL;       /* Voice mailbox number */
181
182 /* HAL battery namespace key values */
183 static int battchg_cur = -1;    /* "battery.charge_level.current" */
184 static int battchg_last = -1;   /* "battery.charge_level.last_full" */
185 static int battchg_design = -1; /* "battery.charge_level.design" */
186
187 static gboolean get_calls_active = FALSE;
188
189 static gboolean events_enabled = FALSE;
190
191 /* Supported set of call hold operations */
192 static const char *chld_str = "0,1,1x,2,2x,3,4";
193
194 /* Timer for tracking call creation requests */
195 static guint create_request_timer = 0;
196
197 static struct indicator maemo_indicators[] =
198 {
199         { "battchg",    "0-5",  5,      TRUE },
200         /* signal strength in terms of bars */
201         { "signal",     "0-5",  0,      TRUE },
202         { "service",    "0,1",  0,      TRUE },
203         { "call",       "0,1",  0,      TRUE },
204         { "callsetup",  "0-3",  0,      TRUE },
205         { "callheld",   "0-2",  0,      FALSE },
206         { "roam",       "0,1",  0,      TRUE },
207         { NULL }
208 };
209
210 static char *call_status_str[] = {
211         "IDLE",
212         "CREATE",
213         "COMING",
214         "PROCEEDING",
215         "MO_ALERTING",
216         "MT_ALERTING",
217         "WAITING",
218         "ANSWERED",
219         "ACTIVE",
220         "MO_RELEASE",
221         "MT_RELEASE",
222         "HOLD_INITIATED",
223         "HOLD",
224         "RETRIEVE_INITIATED",
225         "RECONNECT_PENDING",
226         "TERMINATED",
227         "SWAP_INITIATED",
228         "???"
229 };
230
231 static int send_method_call(const char *dest, const char *path,
232                                 const char *interface, const char *method,
233                                 DBusPendingCallNotifyFunction cb,
234                                 void *user_data, int type, ...)
235 {
236         DBusMessage *msg;
237         DBusPendingCall *call;
238         va_list args;
239         struct pending_req *req;
240
241         msg = dbus_message_new_method_call(dest, path, interface, method);
242         if (!msg) {
243                 error("Unable to allocate new D-Bus %s message", method);
244                 return -ENOMEM;
245         }
246
247         va_start(args, type);
248
249         if (!dbus_message_append_args_valist(msg, type, args)) {
250                 dbus_message_unref(msg);
251                 va_end(args);
252                 return -EIO;
253         }
254
255         va_end(args);
256
257         if (!cb) {
258                 g_dbus_send_message(connection, msg);
259                 return 0;
260         }
261
262         if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
263                 error("Sending %s failed", method);
264                 dbus_message_unref(msg);
265                 return -EIO;
266         }
267
268         dbus_pending_call_set_notify(call, cb, user_data, NULL);
269
270         req = g_new0(struct pending_req, 1);
271         req->call = call;
272         req->user_data = user_data;
273
274         pending = g_slist_prepend(pending, req);
275         dbus_message_unref(msg);
276
277         return 0;
278 }
279
280 static struct csd_call *find_call(const char *path)
281 {
282         GSList *l;
283
284         for (l = calls; l != NULL; l = l->next) {
285                 struct csd_call *call = l->data;
286
287                 if (g_str_equal(call->object_path, path))
288                         return call;
289         }
290
291         return NULL;
292 }
293
294 static struct csd_call *find_non_held_call(void)
295 {
296         GSList *l;
297
298         for (l = calls; l != NULL; l = l->next) {
299                 struct csd_call *call = l->data;
300
301                 if (call->status == CSD_CALL_STATUS_IDLE)
302                         continue;
303
304                 if (call->status != CSD_CALL_STATUS_HOLD)
305                         return call;
306         }
307
308         return NULL;
309 }
310
311 static struct csd_call *find_non_idle_call(void)
312 {
313         GSList *l;
314
315         for (l = calls; l != NULL; l = l->next) {
316                 struct csd_call *call = l->data;
317
318                 if (call->status != CSD_CALL_STATUS_IDLE)
319                         return call;
320         }
321
322         return NULL;
323 }
324
325 static struct csd_call *find_call_with_status(int status)
326 {
327         GSList *l;
328
329         for (l = calls; l != NULL; l = l->next) {
330                 struct csd_call *call = l->data;
331
332                 if (call->status == status)
333                         return call;
334         }
335
336         return NULL;
337 }
338
339 static int release_conference(void)
340 {
341         DBusMessage *msg;
342
343         DBG("telephony-maemo6: releasing conference call");
344
345         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
346                                                 CSD_CALL_CONFERENCE_PATH,
347                                                 CSD_CALL_INSTANCE,
348                                                 "Release");
349         if (!msg) {
350                 error("Unable to allocate new D-Bus message");
351                 return -ENOMEM;
352         }
353
354         g_dbus_send_message(connection, msg);
355
356         return 0;
357 }
358
359 static int release_call(struct csd_call *call)
360 {
361         DBusMessage *msg;
362
363         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
364                                                 call->object_path,
365                                                 CSD_CALL_INSTANCE,
366                                                 "Release");
367         if (!msg) {
368                 error("Unable to allocate new D-Bus message");
369                 return -ENOMEM;
370         }
371
372         g_dbus_send_message(connection, msg);
373
374         return 0;
375 }
376
377 static int answer_call(struct csd_call *call)
378 {
379         DBusMessage *msg;
380
381         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
382                                                 call->object_path,
383                                                 CSD_CALL_INSTANCE,
384                                                 "Answer");
385         if (!msg) {
386                 error("Unable to allocate new D-Bus message");
387                 return -ENOMEM;
388         }
389
390         g_dbus_send_message(connection, msg);
391
392         return 0;
393 }
394
395 static struct pending_req *find_request(const DBusPendingCall *call)
396 {
397         GSList *l;
398
399         for (l = pending; l; l = l->next) {
400                 struct pending_req *req = l->data;
401
402                 if (req->call == call)
403                         return req;
404         }
405
406         return NULL;
407 }
408
409 static void pending_req_finalize(void *data)
410 {
411         struct pending_req *req = data;
412
413         if (!dbus_pending_call_get_completed(req->call))
414                 dbus_pending_call_cancel(req->call);
415
416         dbus_pending_call_unref(req->call);
417         g_free(req);
418 }
419
420 static void remove_pending(DBusPendingCall *call)
421 {
422         struct pending_req *req = find_request(call);
423
424         pending = g_slist_remove(pending, req);
425         pending_req_finalize(req);
426 }
427
428 static void stop_ringtone_reply(DBusPendingCall *call, void *user_data)
429 {
430         struct csd_call *coming = user_data;
431
432         remove_pending(call);
433         answer_call(coming);
434 }
435
436 static int stop_ringtone_and_answer(struct csd_call *call)
437 {
438         int ret;
439
440         ret = send_method_call(OHM_BUS_NAME, OHM_PATH,
441                                 OHM_INTERFACE, "StopRingtone",
442                                 stop_ringtone_reply, call,
443                                 DBUS_TYPE_INVALID);
444         if (ret < 0)
445                 return answer_call(call);
446
447         return 0;
448 }
449
450 static int split_call(struct csd_call *call)
451 {
452         DBusMessage *msg;
453
454         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
455                                                 call->object_path,
456                                                 CSD_CALL_INSTANCE,
457                                                 "Split");
458         if (!msg) {
459                 error("Unable to allocate new D-Bus message");
460                 return -ENOMEM;
461         }
462
463         g_dbus_send_message(connection, msg);
464
465         return 0;
466 }
467
468 static int unhold_call(struct csd_call *call)
469 {
470         DBusMessage *msg;
471
472         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
473                                                 CSD_CALL_INTERFACE,
474                                                 "Unhold");
475         if (!msg) {
476                 error("Unable to allocate new D-Bus message");
477                 return -ENOMEM;
478         }
479
480         g_dbus_send_message(connection, msg);
481
482         return 0;
483 }
484
485 static int hold_call(struct csd_call *call)
486 {
487         DBusMessage *msg;
488
489         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
490                                                 CSD_CALL_INTERFACE,
491                                                 "Hold");
492         if (!msg) {
493                 error("Unable to allocate new D-Bus message");
494                 return -ENOMEM;
495         }
496
497         g_dbus_send_message(connection, msg);
498
499         return 0;
500 }
501
502 static int swap_calls(void)
503 {
504         DBusMessage *msg;
505
506         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
507                                                 CSD_CALL_INTERFACE,
508                                                 "Swap");
509         if (!msg) {
510                 error("Unable to allocate new D-Bus message");
511                 return -ENOMEM;
512         }
513
514         g_dbus_send_message(connection, msg);
515
516         return 0;
517 }
518
519 static int create_conference(void)
520 {
521         DBusMessage *msg;
522
523         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
524                                                 CSD_CALL_INTERFACE,
525                                                 "Conference");
526         if (!msg) {
527                 error("Unable to allocate new D-Bus message");
528                 return -ENOMEM;
529         }
530
531         g_dbus_send_message(connection, msg);
532
533         return 0;
534 }
535
536 static int call_transfer(void)
537 {
538         DBusMessage *msg;
539
540         msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
541                                                 CSD_CALL_INTERFACE,
542                                                 "Transfer");
543         if (!msg) {
544                 error("Unable to allocate new D-Bus message");
545                 return -ENOMEM;
546         }
547
548         g_dbus_send_message(connection, msg);
549
550         return 0;
551 }
552
553 static int number_type(const char *number)
554 {
555         if (number == NULL)
556                 return NUMBER_TYPE_TELEPHONY;
557
558         if (number[0] == '+' || strncmp(number, "00", 2) == 0)
559                 return NUMBER_TYPE_INTERNATIONAL;
560
561         return NUMBER_TYPE_TELEPHONY;
562 }
563
564 void telephony_device_connected(void *telephony_device)
565 {
566         struct csd_call *coming;
567
568         DBG("telephony-maemo6: device %p connected", telephony_device);
569
570         coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
571         if (coming) {
572                 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
573                         telephony_call_waiting_ind(coming->number,
574                                                 number_type(coming->number));
575                 else
576                         telephony_incoming_call_ind(coming->number,
577                                                 number_type(coming->number));
578         }
579 }
580
581 static void remove_pending_by_data(gpointer data, gpointer user_data)
582 {
583         struct pending_req *req = data;
584
585         if (req->user_data == user_data) {
586                 pending = g_slist_remove(pending, req);
587                 pending_req_finalize(req);
588         }
589 }
590
591 void telephony_device_disconnected(void *telephony_device)
592 {
593         DBG("telephony-maemo6: device %p disconnected", telephony_device);
594         events_enabled = FALSE;
595
596         g_slist_foreach(pending, remove_pending_by_data, telephony_device);
597 }
598
599 void telephony_event_reporting_req(void *telephony_device, int ind)
600 {
601         events_enabled = ind == 1 ? TRUE : FALSE;
602
603         telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
604 }
605
606 void telephony_response_and_hold_req(void *telephony_device, int rh)
607 {
608         telephony_response_and_hold_rsp(telephony_device,
609                                                 CME_ERROR_NOT_SUPPORTED);
610 }
611
612 void telephony_terminate_call_req(void *telephony_device)
613 {
614         struct csd_call *call;
615         struct csd_call *alerting;
616         int err;
617
618         call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
619         if (!call)
620                 call = find_non_idle_call();
621
622         if (!call) {
623                 error("No active call");
624                 telephony_terminate_call_rsp(telephony_device,
625                                                 CME_ERROR_NOT_ALLOWED);
626                 return;
627         }
628
629         alerting = find_call_with_status(CSD_CALL_STATUS_MO_ALERTING);
630         if (call->on_hold && alerting)
631                 err = release_call(alerting);
632         else if (call->conference)
633                 err = release_conference();
634         else
635                 err = release_call(call);
636
637         if (err < 0)
638                 telephony_terminate_call_rsp(telephony_device,
639                                                 CME_ERROR_AG_FAILURE);
640         else
641                 telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
642 }
643
644 void telephony_answer_call_req(void *telephony_device)
645 {
646         struct csd_call *call;
647
648         call = find_call_with_status(CSD_CALL_STATUS_COMING);
649         if (!call)
650                 call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
651
652         if (!call)
653                 call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
654
655         if (!call)
656                 call = find_call_with_status(CSD_CALL_STATUS_WAITING);
657
658         if (!call) {
659                 telephony_answer_call_rsp(telephony_device,
660                                                 CME_ERROR_NOT_ALLOWED);
661                 return;
662         }
663
664         if (stop_ringtone_and_answer(call) < 0)
665                 telephony_answer_call_rsp(telephony_device,
666                                                 CME_ERROR_AG_FAILURE);
667         else
668                 telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
669 }
670
671 static void create_call_reply(DBusPendingCall *call, void *user_data)
672 {
673         DBusError err;
674         DBusMessage *reply;
675         void *telephony_device = user_data;
676
677         reply = dbus_pending_call_steal_reply(call);
678
679         dbus_error_init(&err);
680         if (dbus_set_error_from_message(&err, reply)) {
681                 error("csd replied with an error: %s, %s",
682                                 err.name, err.message);
683                 if (g_strcmp0(err.name,
684                                 "com.nokia.csd.Call.Error.CSInactive") == 0)
685                         telephony_dial_number_rsp(telephony_device,
686                                                 CME_ERROR_NO_NETWORK_SERVICE);
687                 else
688                         telephony_dial_number_rsp(telephony_device,
689                                                         CME_ERROR_AG_FAILURE);
690                 dbus_error_free(&err);
691         } else
692                 telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
693
694         dbus_message_unref(reply);
695         remove_pending(call);
696 }
697
698 void telephony_last_dialed_number_req(void *telephony_device)
699 {
700         int ret;
701
702         DBG("telephony-maemo6: last dialed number request");
703
704         ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
705                                 CSD_CALL_INTERFACE, "CreateFromLast",
706                                 create_call_reply, telephony_device,
707                                 DBUS_TYPE_INVALID);
708         if (ret < 0)
709                 telephony_dial_number_rsp(telephony_device,
710                                                 CME_ERROR_AG_FAILURE);
711 }
712
713 static const char *memory_dial_lookup(int location)
714 {
715         if (location == 1)
716                 return vmbx;
717         else
718                 return NULL;
719 }
720
721 void telephony_dial_number_req(void *telephony_device, const char *number)
722 {
723         int ret;
724
725         DBG("telephony-maemo6: dial request to %s", number);
726
727         if (strncmp(number, "*31#", 4) == 0)
728                 number += 4;
729         else if (strncmp(number, "#31#", 4) == 0)
730                 number += 4;
731         else if (number[0] == '>') {
732                 const char *location = &number[1];
733
734                 number = memory_dial_lookup(strtol(&number[1], NULL, 0));
735                 if (!number) {
736                         error("No number at memory location %s", location);
737                         telephony_dial_number_rsp(telephony_device,
738                                                 CME_ERROR_INVALID_INDEX);
739                         return;
740                 }
741         }
742
743         ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
744                                 CSD_CALL_INTERFACE, "Create",
745                                 create_call_reply, telephony_device,
746                                 DBUS_TYPE_STRING, &number,
747                                 DBUS_TYPE_INVALID);
748         if (ret < 0)
749                 telephony_dial_number_rsp(telephony_device,
750                                                 CME_ERROR_AG_FAILURE);
751 }
752
753 static void start_dtmf_reply(DBusPendingCall *call, void *user_data)
754 {
755         DBusError err;
756         DBusMessage *reply;
757
758         reply = dbus_pending_call_steal_reply(call);
759
760         dbus_error_init(&err);
761         if (dbus_set_error_from_message(&err, reply)) {
762                 error("csd replied with an error: %s, %s",
763                                 err.name, err.message);
764
765                 dbus_error_free(&err);
766         } else
767                 send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
768                                 CSD_CALL_INTERFACE, "StopDTMF",
769                                 NULL, NULL,
770                                 DBUS_TYPE_INVALID);
771
772         dbus_message_unref(reply);
773         remove_pending(call);
774 }
775
776 static void start_dtmf(void *telephony_device, char tone)
777 {
778         int ret;
779
780         /*
781          * Stop tone immediately, modem will place it in queue and play
782          * required time.
783          */
784         ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
785                                 CSD_CALL_INTERFACE, "StartDTMF",
786                                 start_dtmf_reply, NULL,
787                                 DBUS_TYPE_BYTE, &tone,
788                                 DBUS_TYPE_INVALID);
789         if (ret < 0) {
790                 telephony_transmit_dtmf_rsp(telephony_device,
791                                                 CME_ERROR_AG_FAILURE);
792                 return;
793         }
794
795         telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
796 }
797
798 static int tonegen_startevent(char tone)
799 {
800         int ret;
801         dbus_uint32_t event_tone;
802         dbus_int32_t dbm0 = -15;
803         dbus_uint32_t duration = 150;
804
805         switch (tone) {
806         case '*':
807                 event_tone = DTMF_ASTERISK;
808                 break;
809         case '#':
810                 event_tone = DTMF_HASHMARK;
811                 break;
812         case 'A':
813                 event_tone = DTMF_A;
814                 break;
815         case 'B':
816                 event_tone = DTMF_B;
817                 break;
818         case 'C':
819                 event_tone = DTMF_C;
820                 break;
821         case 'D':
822                 event_tone = DTMF_D;
823                 break;
824         default:
825                 ret = g_ascii_digit_value(tone);
826                 if (ret < 0)
827                         return -EINVAL;
828                 event_tone = ret;
829         }
830
831         ret = send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
832                                 TONEGEN_INTERFACE, "StartEventTone",
833                                 NULL, NULL,
834                                 DBUS_TYPE_UINT32, &event_tone,
835                                 DBUS_TYPE_INT32, &dbm0,
836                                 DBUS_TYPE_UINT32, &duration,
837                                 DBUS_TYPE_INVALID);
838         return ret;
839 }
840
841 static gboolean stop_feedback_tone(gpointer user_data)
842 {
843         if (g_slist_length(tones) > 0) {
844                 gpointer ptone;
845                 int ret;
846
847                 send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
848                                 TONEGEN_INTERFACE, "StopTone",
849                                 NULL, NULL,
850                                 DBUS_TYPE_INVALID);
851
852                 ptone = g_slist_nth_data(tones, 0);
853                 tones = g_slist_remove(tones, ptone);
854
855                 ret = tonegen_startevent(GPOINTER_TO_UINT(ptone));
856                 if (ret < 0)
857                         goto done;
858
859                 return TRUE;
860         }
861 done:
862         return FALSE;
863 }
864
865 static void tones_timer_notify(gpointer data)
866 {
867         send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
868                                 TONEGEN_INTERFACE, "StopTone",
869                                 NULL, NULL,
870                                 DBUS_TYPE_INVALID);
871         g_slist_free(tones);
872         tones = NULL;
873
874         create_tones_timer = 0;
875 }
876
877 static void start_feedback_tone(char tone)
878 {
879         if (!create_tones_timer) {
880                 int ret;
881
882                 ret = tonegen_startevent(tone);
883                 if (ret < 0)
884                         return;
885
886                 create_tones_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
887                                                 FEEDBACK_TONE_DURATION,
888                                                 stop_feedback_tone,
889                                                 NULL,
890                                                 tones_timer_notify);
891         } else {
892                 glong dtmf_tone = tone;
893
894                 DBG("add %c to queue", tone);
895                 tones = g_slist_append(tones, GUINT_TO_POINTER(dtmf_tone));
896         }
897 }
898
899 void telephony_transmit_dtmf_req(void *telephony_device, char tone)
900 {
901         DBG("telephony-maemo6: transmit dtmf: %c", tone);
902
903         start_dtmf(telephony_device, tone);
904
905         if (!find_call_with_status(CSD_CALL_STATUS_ACTIVE))
906                 error("No active call");
907         else
908                 start_feedback_tone(tone);
909 }
910
911 void telephony_subscriber_number_req(void *telephony_device)
912 {
913         DBG("telephony-maemo6: subscriber number request");
914         if (msisdn)
915                 telephony_subscriber_number_ind(msisdn,
916                                                 number_type(msisdn),
917                                                 SUBSCRIBER_SERVICE_VOICE);
918         telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
919 }
920
921 static int csd_status_to_hfp(struct csd_call *call)
922 {
923         switch (call->status) {
924         case CSD_CALL_STATUS_IDLE:
925         case CSD_CALL_STATUS_MO_RELEASE:
926         case CSD_CALL_STATUS_MT_RELEASE:
927         case CSD_CALL_STATUS_TERMINATED:
928                 return -1;
929         case CSD_CALL_STATUS_CREATE:
930                 return CALL_STATUS_DIALING;
931         case CSD_CALL_STATUS_WAITING:
932                 return CALL_STATUS_WAITING;
933         case CSD_CALL_STATUS_PROCEEDING:
934                 /* PROCEEDING can happen in outgoing/incoming */
935                 if (call->originating)
936                         return CALL_STATUS_DIALING;
937
938                 /*
939                  * PROCEEDING is followed by WAITING CSD status, therefore
940                  * second incoming call status indication is set immediately
941                  * to waiting.
942                  */
943                 if (g_slist_length(active_calls) > 0)
944                         return CALL_STATUS_WAITING;
945
946                 return CALL_STATUS_INCOMING;
947         case CSD_CALL_STATUS_COMING:
948                 if (g_slist_length(active_calls) > 0)
949                         return CALL_STATUS_WAITING;
950
951                 return CALL_STATUS_INCOMING;
952         case CSD_CALL_STATUS_MO_ALERTING:
953                 return CALL_STATUS_ALERTING;
954         case CSD_CALL_STATUS_MT_ALERTING:
955                 return CALL_STATUS_INCOMING;
956         case CSD_CALL_STATUS_ANSWERED:
957         case CSD_CALL_STATUS_ACTIVE:
958         case CSD_CALL_STATUS_RECONNECT_PENDING:
959         case CSD_CALL_STATUS_SWAP_INITIATED:
960         case CSD_CALL_STATUS_HOLD_INITIATED:
961                 return CALL_STATUS_ACTIVE;
962         case CSD_CALL_STATUS_RETRIEVE_INITIATED:
963         case CSD_CALL_STATUS_HOLD:
964                 return CALL_STATUS_HELD;
965         default:
966                 return -1;
967         }
968 }
969
970 void telephony_list_current_calls_req(void *telephony_device)
971 {
972         GSList *l;
973         int i;
974
975         DBG("telephony-maemo6: list current calls request");
976
977         for (l = calls, i = 1; l != NULL; l = l->next, i++) {
978                 struct csd_call *call = l->data;
979                 int status, direction, multiparty;
980
981                 status = csd_status_to_hfp(call);
982                 if (status < 0)
983                         continue;
984
985                 direction = call->originating ?
986                                 CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
987
988                 multiparty = call->conference ?
989                                 CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
990
991                 telephony_list_current_call_ind(i, direction, status,
992                                                 CALL_MODE_VOICE, multiparty,
993                                                 call->number,
994                                                 number_type(call->number));
995         }
996
997         telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
998 }
999
1000 void telephony_operator_selection_req(void *telephony_device)
1001 {
1002         telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
1003                                 net.operator_name ? net.operator_name : "");
1004         telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
1005 }
1006
1007 static void foreach_call_with_status(int status,
1008                                         int (*func)(struct csd_call *call))
1009 {
1010         GSList *l;
1011
1012         for (l = calls; l != NULL; l = l->next) {
1013                 struct csd_call *call = l->data;
1014
1015                 if (call->status == status)
1016                         func(call);
1017         }
1018 }
1019
1020 void telephony_call_hold_req(void *telephony_device, const char *cmd)
1021 {
1022         const char *idx;
1023         struct csd_call *call;
1024         int err = 0;
1025
1026         DBG("telephony-maemo6: got call hold request %s", cmd);
1027
1028         if (strlen(cmd) > 1)
1029                 idx = &cmd[1];
1030         else
1031                 idx = NULL;
1032
1033         if (idx)
1034                 call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
1035         else
1036                 call = NULL;
1037
1038         switch (cmd[0]) {
1039         case '0':
1040                 if (find_call_with_status(CSD_CALL_STATUS_WAITING))
1041                         foreach_call_with_status(CSD_CALL_STATUS_WAITING,
1042                                                                 release_call);
1043                 else
1044                         foreach_call_with_status(CSD_CALL_STATUS_HOLD,
1045                                                                 release_call);
1046                 break;
1047         case '1':
1048                 if (idx) {
1049                         if (call)
1050                                 err = release_call(call);
1051                         break;
1052                 }
1053                 foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
1054                 call = find_call_with_status(CSD_CALL_STATUS_WAITING);
1055                 if (call)
1056                         err = answer_call(call);
1057                 break;
1058         case '2':
1059                 if (idx) {
1060                         if (call)
1061                                 err = split_call(call);
1062                 } else {
1063                         struct csd_call *held, *wait;
1064
1065                         call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1066                         held = find_call_with_status(CSD_CALL_STATUS_HOLD);
1067                         wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
1068
1069                         if (wait)
1070                                 err = answer_call(wait);
1071                         else if (call && held)
1072                                 err = swap_calls();
1073                         else {
1074                                 if (call)
1075                                         err = hold_call(call);
1076                                 if (held)
1077                                         err = unhold_call(held);
1078                         }
1079                 }
1080                 break;
1081         case '3':
1082                 if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
1083                                 find_call_with_status(CSD_CALL_STATUS_WAITING))
1084                         err = create_conference();
1085                 break;
1086         case '4':
1087                 err = call_transfer();
1088                 break;
1089         default:
1090                 DBG("Unknown call hold request");
1091                 break;
1092         }
1093
1094         if (err)
1095                 telephony_call_hold_rsp(telephony_device,
1096                                         CME_ERROR_AG_FAILURE);
1097         else
1098                 telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
1099 }
1100
1101 void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
1102 {
1103         DBG("telephony-maemo6: got %s NR and EC request",
1104                         enable ? "enable" : "disable");
1105         telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
1106 }
1107
1108 void telephony_key_press_req(void *telephony_device, const char *keys)
1109 {
1110         struct csd_call *active, *waiting;
1111         int err;
1112
1113         DBG("telephony-maemo6: got key press request for %s", keys);
1114
1115         waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
1116         if (!waiting)
1117                 waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
1118         if (!waiting)
1119                 waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
1120
1121         active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1122
1123         if (waiting)
1124                 err = answer_call(waiting);
1125         else if (active)
1126                 err = release_call(active);
1127         else
1128                 err = 0;
1129
1130         if (err < 0)
1131                 telephony_key_press_rsp(telephony_device,
1132                                                         CME_ERROR_AG_FAILURE);
1133         else
1134                 telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
1135 }
1136
1137 void telephony_voice_dial_req(void *telephony_device, gboolean enable)
1138 {
1139         DBG("telephony-maemo6: got %s voice dial request",
1140                         enable ? "enable" : "disable");
1141
1142         telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
1143 }
1144
1145 static void handle_incoming_call(DBusMessage *msg)
1146 {
1147         const char *number, *call_path;
1148         struct csd_call *call;
1149
1150         if (!dbus_message_get_args(msg, NULL,
1151                                         DBUS_TYPE_OBJECT_PATH, &call_path,
1152                                         DBUS_TYPE_STRING, &number,
1153                                         DBUS_TYPE_INVALID)) {
1154                 error("Unexpected parameters in Call.Coming() signal");
1155                 return;
1156         }
1157
1158         call = find_call(call_path);
1159         if (!call) {
1160                 error("Didn't find any matching call object for %s",
1161                                 call_path);
1162                 return;
1163         }
1164
1165         DBG("Incoming call to %s from number %s", call_path, number);
1166
1167         g_free(call->number);
1168         call->number = g_strdup(number);
1169
1170         if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
1171                         find_call_with_status(CSD_CALL_STATUS_HOLD))
1172                 telephony_call_waiting_ind(call->number,
1173                                                 number_type(call->number));
1174         else
1175                 telephony_incoming_call_ind(call->number,
1176                                                 number_type(call->number));
1177
1178         telephony_update_indicator(maemo_indicators, "callsetup",
1179                                         EV_CALLSETUP_INCOMING);
1180 }
1181
1182 static void handle_outgoing_call(DBusMessage *msg)
1183 {
1184         const char *number, *call_path;
1185         struct csd_call *call;
1186
1187         if (!dbus_message_get_args(msg, NULL,
1188                                         DBUS_TYPE_OBJECT_PATH, &call_path,
1189                                         DBUS_TYPE_STRING, &number,
1190                                         DBUS_TYPE_INVALID)) {
1191                 error("Unexpected parameters in Call.Created() signal");
1192                 return;
1193         }
1194
1195         call = find_call(call_path);
1196         if (!call) {
1197                 error("Didn't find any matching call object for %s",
1198                                 call_path);
1199                 return;
1200         }
1201
1202         DBG("Outgoing call from %s to number %s", call_path, number);
1203
1204         g_free(call->number);
1205         call->number = g_strdup(number);
1206
1207         if (create_request_timer) {
1208                 g_source_remove(create_request_timer);
1209                 create_request_timer = 0;
1210         }
1211 }
1212
1213 static gboolean create_timeout(gpointer user_data)
1214 {
1215         telephony_update_indicator(maemo_indicators, "callsetup",
1216                                         EV_CALLSETUP_INACTIVE);
1217         create_request_timer = 0;
1218         return FALSE;
1219 }
1220
1221 static void handle_create_requested(DBusMessage *msg)
1222 {
1223         DBG("Call.CreateRequested()");
1224
1225         if (create_request_timer)
1226                 g_source_remove(create_request_timer);
1227
1228         create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
1229
1230         telephony_update_indicator(maemo_indicators, "callsetup",
1231                                         EV_CALLSETUP_OUTGOING);
1232 }
1233
1234 static void call_set_status(struct csd_call *call, dbus_uint32_t status)
1235 {
1236         dbus_uint32_t prev_status;
1237         int callheld = telephony_get_indicator(maemo_indicators, "callheld");
1238
1239         prev_status = call->status;
1240         DBG("Call %s changed from %s to %s", call->object_path,
1241                 call_status_str[prev_status], call_status_str[status]);
1242
1243         if (prev_status == status) {
1244                 DBG("Ignoring CSD Call state change to existing state");
1245                 return;
1246         }
1247
1248         call->status = (int) status;
1249
1250         switch (status) {
1251         case CSD_CALL_STATUS_IDLE:
1252                 if (call->setup) {
1253                         telephony_update_indicator(maemo_indicators,
1254                                                         "callsetup",
1255                                                         EV_CALLSETUP_INACTIVE);
1256                         if (!call->originating)
1257                                 telephony_calling_stopped_ind();
1258                 }
1259
1260                 g_free(call->number);
1261                 call->number = NULL;
1262                 call->originating = FALSE;
1263                 call->emergency = FALSE;
1264                 call->on_hold = FALSE;
1265                 call->conference = FALSE;
1266                 call->setup = FALSE;
1267                 break;
1268         case CSD_CALL_STATUS_CREATE:
1269                 call->originating = TRUE;
1270                 call->setup = TRUE;
1271                 break;
1272         case CSD_CALL_STATUS_COMING:
1273                 call->originating = FALSE;
1274                 call->setup = TRUE;
1275                 break;
1276         case CSD_CALL_STATUS_PROCEEDING:
1277                 break;
1278         case CSD_CALL_STATUS_MO_ALERTING:
1279                 telephony_update_indicator(maemo_indicators, "callsetup",
1280                                                 EV_CALLSETUP_ALERTING);
1281                 break;
1282         case CSD_CALL_STATUS_MT_ALERTING:
1283                 /* Some headsets expect incoming call notification before they
1284                  * can send ATA command. When call changed status from waiting
1285                  * to alerting we need to send missing notification. Otherwise
1286                  * headsets like Nokia BH-108 or BackBeat 903 are unable to
1287                  * answer incoming call that was previously waiting. */
1288                 if (prev_status == CSD_CALL_STATUS_WAITING)
1289                         telephony_incoming_call_ind(call->number,
1290                                                 number_type(call->number));
1291                 break;
1292         case CSD_CALL_STATUS_WAITING:
1293                 break;
1294         case CSD_CALL_STATUS_ANSWERED:
1295                 break;
1296         case CSD_CALL_STATUS_ACTIVE:
1297                 if (call->on_hold) {
1298                         call->on_hold = FALSE;
1299                         if (find_call_with_status(CSD_CALL_STATUS_HOLD))
1300                                 telephony_update_indicator(maemo_indicators,
1301                                                         "callheld",
1302                                                         EV_CALLHELD_MULTIPLE);
1303                         else
1304                                 telephony_update_indicator(maemo_indicators,
1305                                                         "callheld",
1306                                                         EV_CALLHELD_NONE);
1307                 } else {
1308                         if (!g_slist_find(active_calls, call))
1309                                 active_calls = g_slist_prepend(active_calls, call);
1310                         if (g_slist_length(active_calls) == 1)
1311                                 telephony_update_indicator(maemo_indicators,
1312                                                                 "call",
1313                                                                 EV_CALL_ACTIVE);
1314                         /* Upgrade callheld status if necessary */
1315                         if (callheld == EV_CALLHELD_ON_HOLD)
1316                                 telephony_update_indicator(maemo_indicators,
1317                                                         "callheld",
1318                                                         EV_CALLHELD_MULTIPLE);
1319                         telephony_update_indicator(maemo_indicators,
1320                                                         "callsetup",
1321                                                         EV_CALLSETUP_INACTIVE);
1322                         if (!call->originating)
1323                                 telephony_calling_stopped_ind();
1324                         call->setup = FALSE;
1325                 }
1326                 break;
1327         case CSD_CALL_STATUS_MO_RELEASE:
1328         case CSD_CALL_STATUS_MT_RELEASE:
1329                 active_calls = g_slist_remove(active_calls, call);
1330                 if (g_slist_length(active_calls) == 0)
1331                         telephony_update_indicator(maemo_indicators, "call",
1332                                                         EV_CALL_INACTIVE);
1333
1334                 if (create_tones_timer)
1335                         g_source_remove(create_tones_timer);
1336                 break;
1337         case CSD_CALL_STATUS_HOLD_INITIATED:
1338                 break;
1339         case CSD_CALL_STATUS_HOLD:
1340                 call->on_hold = TRUE;
1341                 if (find_non_held_call())
1342                         telephony_update_indicator(maemo_indicators,
1343                                                         "callheld",
1344                                                         EV_CALLHELD_MULTIPLE);
1345                 else
1346                         telephony_update_indicator(maemo_indicators,
1347                                                         "callheld",
1348                                                         EV_CALLHELD_ON_HOLD);
1349                 break;
1350         case CSD_CALL_STATUS_RETRIEVE_INITIATED:
1351                 break;
1352         case CSD_CALL_STATUS_RECONNECT_PENDING:
1353                 break;
1354         case CSD_CALL_STATUS_TERMINATED:
1355                 if (call->on_hold &&
1356                                 !find_call_with_status(CSD_CALL_STATUS_HOLD)) {
1357                         telephony_update_indicator(maemo_indicators,
1358                                                         "callheld",
1359                                                         EV_CALLHELD_NONE);
1360                         return;
1361                 }
1362
1363                 if (callheld == EV_CALLHELD_MULTIPLE &&
1364                                 find_call_with_status(CSD_CALL_STATUS_HOLD) &&
1365                                 !find_call_with_status(CSD_CALL_STATUS_ACTIVE))
1366                         telephony_update_indicator(maemo_indicators,
1367                                                         "callheld",
1368                                                         EV_CALLHELD_ON_HOLD);
1369                 break;
1370         case CSD_CALL_STATUS_SWAP_INITIATED:
1371                 break;
1372         default:
1373                 error("Unknown call status %u", status);
1374                 break;
1375         }
1376 }
1377
1378 static void handle_call_status(DBusMessage *msg, const char *call_path)
1379 {
1380         struct csd_call *call;
1381         dbus_uint32_t status, cause_type, cause;
1382
1383         if (!dbus_message_get_args(msg, NULL,
1384                                         DBUS_TYPE_UINT32, &status,
1385                                         DBUS_TYPE_UINT32, &cause_type,
1386                                         DBUS_TYPE_UINT32, &cause,
1387                                         DBUS_TYPE_INVALID)) {
1388                 error("Unexpected parameters in Instance.CallStatus() signal");
1389                 return;
1390         }
1391
1392         call = find_call(call_path);
1393         if (!call) {
1394                 error("Didn't find any matching call object for %s",
1395                                 call_path);
1396                 return;
1397         }
1398
1399         if (status > 16) {
1400                 error("Invalid call status %u", status);
1401                 return;
1402         }
1403
1404         call_set_status(call, status);
1405 }
1406
1407 static void handle_conference(DBusMessage *msg, gboolean joined)
1408 {
1409         const char *path;
1410         struct csd_call *call;
1411
1412         if (!dbus_message_get_args(msg, NULL,
1413                                         DBUS_TYPE_OBJECT_PATH, &path,
1414                                         DBUS_TYPE_INVALID)) {
1415                 error("Unexpected parameters in Conference.%s",
1416                                         dbus_message_get_member(msg));
1417                 return;
1418         }
1419
1420         call = find_call(path);
1421         if (!call) {
1422                 error("Conference signal for unknown call %s", path);
1423                 return;
1424         }
1425
1426         DBG("Call %s %s the conference", path, joined ? "joined" : "left");
1427
1428         call->conference = joined;
1429 }
1430
1431 static uint8_t str2status(const char *state)
1432 {
1433         if (g_strcmp0(state, "Home") == 0)
1434                 return NETWORK_REG_STATUS_HOME;
1435         else if (g_strcmp0(state, "Roaming") == 0)
1436                 return NETWORK_REG_STATUS_ROAMING;
1437         else if (g_strcmp0(state, "Offline") == 0)
1438                 return NETWORK_REG_STATUS_OFFLINE;
1439         else if (g_strcmp0(state, "Searching") == 0)
1440                 return NETWORK_REG_STATUS_SEARCHING;
1441         else if (g_strcmp0(state, "NoSim") == 0)
1442                 return NETWORK_REG_STATUS_NO_SIM;
1443         else if (g_strcmp0(state, "Poweroff") == 0)
1444                 return NETWORK_REG_STATUS_POWEROFF;
1445         else if (g_strcmp0(state, "Powersafe") == 0)
1446                 return NETWORK_REG_STATUS_POWERSAFE;
1447         else if (g_strcmp0(state, "NoCoverage") == 0)
1448                 return NETWORK_REG_STATUS_NO_COVERAGE;
1449         else if (g_strcmp0(state, "Reject") == 0)
1450                 return NETWORK_REG_STATUS_REJECTED;
1451         else
1452                 return NETWORK_REG_STATUS_UNKOWN;
1453 }
1454
1455 static void update_registration_status(const char *status)
1456 {
1457         uint8_t new_status;
1458
1459         new_status = str2status(status);
1460
1461         if (net.status == new_status)
1462                 return;
1463
1464         switch (new_status) {
1465         case NETWORK_REG_STATUS_HOME:
1466                 telephony_update_indicator(maemo_indicators, "roam",
1467                                                         EV_ROAM_INACTIVE);
1468                 if (net.status > NETWORK_REG_STATUS_ROAMING)
1469                         telephony_update_indicator(maemo_indicators,
1470                                                         "service",
1471                                                         EV_SERVICE_PRESENT);
1472                 break;
1473         case NETWORK_REG_STATUS_ROAMING:
1474                 telephony_update_indicator(maemo_indicators, "roam",
1475                                                         EV_ROAM_ACTIVE);
1476                 if (net.status > NETWORK_REG_STATUS_ROAMING)
1477                         telephony_update_indicator(maemo_indicators,
1478                                                         "service",
1479                                                         EV_SERVICE_PRESENT);
1480                 break;
1481         case NETWORK_REG_STATUS_OFFLINE:
1482         case NETWORK_REG_STATUS_SEARCHING:
1483         case NETWORK_REG_STATUS_NO_SIM:
1484         case NETWORK_REG_STATUS_POWEROFF:
1485         case NETWORK_REG_STATUS_POWERSAFE:
1486         case NETWORK_REG_STATUS_NO_COVERAGE:
1487         case NETWORK_REG_STATUS_REJECTED:
1488         case NETWORK_REG_STATUS_UNKOWN:
1489                 if (net.status < NETWORK_REG_STATUS_OFFLINE)
1490                         telephony_update_indicator(maemo_indicators,
1491                                                         "service",
1492                                                         EV_SERVICE_NONE);
1493                 break;
1494         }
1495
1496         net.status = new_status;
1497
1498         DBG("telephony-maemo6: registration status changed: %s", status);
1499 }
1500
1501 static void handle_registration_changed(DBusMessage *msg)
1502 {
1503         const char *status;
1504
1505         if (!dbus_message_get_args(msg, NULL,
1506                                         DBUS_TYPE_STRING, &status,
1507                                         DBUS_TYPE_INVALID)) {
1508                 error("Unexpected parameters in RegistrationChanged");
1509                 return;
1510         }
1511
1512         update_registration_status(status);
1513 }
1514
1515 static void update_signal_strength(int32_t signal_bars)
1516 {
1517         if (signal_bars < 0) {
1518                 DBG("signal strength smaller than expected: %d < 0",
1519                                                                 signal_bars);
1520                 signal_bars = 0;
1521         } else if (signal_bars > 5) {
1522                 DBG("signal strength greater than expected: %d > 5",
1523                                                                 signal_bars);
1524                 signal_bars = 5;
1525         }
1526
1527         if (net.signal_bars == signal_bars)
1528                 return;
1529
1530         telephony_update_indicator(maemo_indicators, "signal", signal_bars);
1531
1532         net.signal_bars = signal_bars;
1533         DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars);
1534 }
1535
1536 static void handle_signal_bars_changed(DBusMessage *msg)
1537 {
1538         int32_t signal_bars;
1539
1540         if (!dbus_message_get_args(msg, NULL,
1541                                         DBUS_TYPE_INT32, &signal_bars,
1542                                         DBUS_TYPE_INVALID)) {
1543                 error("Unexpected parameters in SignalBarsChanged");
1544                 return;
1545         }
1546
1547         update_signal_strength(signal_bars);
1548 }
1549
1550 static gboolean iter_get_basic_args(DBusMessageIter *iter,
1551                                         int first_arg_type, ...)
1552 {
1553         int type;
1554         va_list ap;
1555
1556         va_start(ap, first_arg_type);
1557
1558         for (type = first_arg_type; type != DBUS_TYPE_INVALID;
1559                         type = va_arg(ap, int)) {
1560                 void *value = va_arg(ap, void *);
1561                 int real_type = dbus_message_iter_get_arg_type(iter);
1562
1563                 if (real_type != type) {
1564                         error("iter_get_basic_args: expected %c but got %c",
1565                                         (char) type, (char) real_type);
1566                         break;
1567                 }
1568
1569                 dbus_message_iter_get_basic(iter, value);
1570                 dbus_message_iter_next(iter);
1571         }
1572
1573         va_end(ap);
1574
1575         return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
1576 }
1577
1578 static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
1579 {
1580         DBusError err;
1581         DBusMessage *reply;
1582         dbus_int32_t level;
1583         int *value = user_data;
1584
1585         reply = dbus_pending_call_steal_reply(call);
1586
1587         dbus_error_init(&err);
1588         if (dbus_set_error_from_message(&err, reply)) {
1589                 error("hald replied with an error: %s, %s",
1590                                 err.name, err.message);
1591                 dbus_error_free(&err);
1592                 goto done;
1593         }
1594
1595         if (!dbus_message_get_args(reply, NULL,
1596                                 DBUS_TYPE_INT32, &level,
1597                                 DBUS_TYPE_INVALID)) {
1598                 error("Unexpected args in hald reply");
1599                 goto done;
1600         }
1601
1602         *value = (int) level;
1603
1604         if (value == &battchg_last)
1605                 DBG("telephony-maemo6: battery.charge_level.last_full is %d",
1606                                 *value);
1607         else if (value == &battchg_design)
1608                 DBG("telephony-maemo6: battery.charge_level.design is %d",
1609                                 *value);
1610         else
1611                 DBG("telephony-maemo6: battery.charge_level.current is %d",
1612                                 *value);
1613
1614         if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
1615                 int new, max;
1616
1617                 if (battchg_last > 0)
1618                         max = battchg_last;
1619                 else
1620                         max = battchg_design;
1621
1622                 new = battchg_cur * 5 / max;
1623
1624                 telephony_update_indicator(maemo_indicators, "battchg", new);
1625         }
1626
1627 done:
1628         dbus_message_unref(reply);
1629         remove_pending(call);
1630 }
1631
1632 static void hal_get_integer(const char *path, const char *key, void *user_data)
1633 {
1634         send_method_call("org.freedesktop.Hal", path,
1635                                 "org.freedesktop.Hal.Device",
1636                                 "GetPropertyInteger",
1637                                 hal_battery_level_reply, user_data,
1638                                 DBUS_TYPE_STRING, &key,
1639                                 DBUS_TYPE_INVALID);
1640 }
1641
1642 static void handle_hal_property_modified(DBusMessage *msg)
1643 {
1644         DBusMessageIter iter, array;
1645         dbus_int32_t num_changes;
1646         const char *path;
1647
1648         path = dbus_message_get_path(msg);
1649
1650         dbus_message_iter_init(msg, &iter);
1651
1652         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
1653                 error("Unexpected signature in hal PropertyModified signal");
1654                 return;
1655         }
1656
1657         dbus_message_iter_get_basic(&iter, &num_changes);
1658         dbus_message_iter_next(&iter);
1659
1660         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1661                 error("Unexpected signature in hal PropertyModified signal");
1662                 return;
1663         }
1664
1665         dbus_message_iter_recurse(&iter, &array);
1666
1667         while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1668                 DBusMessageIter prop;
1669                 const char *name;
1670                 dbus_bool_t added, removed;
1671
1672                 dbus_message_iter_recurse(&array, &prop);
1673
1674                 if (!iter_get_basic_args(&prop,
1675                                         DBUS_TYPE_STRING, &name,
1676                                         DBUS_TYPE_BOOLEAN, &added,
1677                                         DBUS_TYPE_BOOLEAN, &removed,
1678                                         DBUS_TYPE_INVALID)) {
1679                         error("Invalid hal PropertyModified parameters");
1680                         break;
1681                 }
1682
1683                 if (g_str_equal(name, "battery.charge_level.last_full"))
1684                         hal_get_integer(path, name, &battchg_last);
1685                 else if (g_str_equal(name, "battery.charge_level.current"))
1686                         hal_get_integer(path, name, &battchg_cur);
1687                 else if (g_str_equal(name, "battery.charge_level.design"))
1688                         hal_get_integer(path, name, &battchg_design);
1689
1690                 dbus_message_iter_next(&array);
1691         }
1692 }
1693
1694 static void csd_call_free(void *data)
1695 {
1696         struct csd_call *call = data;
1697
1698         if (!call)
1699                 return;
1700
1701         g_free(call->object_path);
1702         g_free(call->number);
1703
1704         g_slist_foreach(pending, remove_pending_by_data, call);
1705
1706         g_free(call);
1707 }
1708
1709 static void parse_call_list(DBusMessageIter *iter)
1710 {
1711         do {
1712                 DBusMessageIter call_iter;
1713                 struct csd_call *call;
1714                 const char *object_path, *number;
1715                 dbus_uint32_t status;
1716                 dbus_bool_t originating, terminating, emerg, on_hold, conf;
1717
1718                 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
1719                         error("Unexpected signature in GetCallInfoAll reply");
1720                         break;
1721                 }
1722
1723                 dbus_message_iter_recurse(iter, &call_iter);
1724
1725                 if (!iter_get_basic_args(&call_iter,
1726                                         DBUS_TYPE_OBJECT_PATH, &object_path,
1727                                         DBUS_TYPE_UINT32, &status,
1728                                         DBUS_TYPE_BOOLEAN, &originating,
1729                                         DBUS_TYPE_BOOLEAN, &terminating,
1730                                         DBUS_TYPE_BOOLEAN, &emerg,
1731                                         DBUS_TYPE_BOOLEAN, &on_hold,
1732                                         DBUS_TYPE_BOOLEAN, &conf,
1733                                         DBUS_TYPE_STRING, &number,
1734                                         DBUS_TYPE_INVALID)) {
1735                         error("Parsing call D-Bus parameters failed");
1736                         break;
1737                 }
1738
1739                 call = find_call(object_path);
1740                 if (!call) {
1741                         call = g_new0(struct csd_call, 1);
1742                         call->object_path = g_strdup(object_path);
1743                         calls = g_slist_append(calls, call);
1744                         DBG("telephony-maemo6: new csd call instance at %s",
1745                                                                 object_path);
1746                 }
1747
1748                 if (status == CSD_CALL_STATUS_IDLE)
1749                         continue;
1750
1751                 /* CSD gives incorrect call_hold property sometimes */
1752                 if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
1753                                 (call->status == CSD_CALL_STATUS_HOLD &&
1754                                                                 !on_hold)) {
1755                         error("Conflicting call status and on_hold property!");
1756                         on_hold = call->status == CSD_CALL_STATUS_HOLD;
1757                 }
1758
1759                 call->originating = originating;
1760                 call->on_hold = on_hold;
1761                 call->conference = conf;
1762                 g_free(call->number);
1763                 call->number = g_strdup(number);
1764
1765                 /* Update indicators */
1766                 call_set_status(call, status);
1767
1768         } while (dbus_message_iter_next(iter));
1769 }
1770
1771 static void update_operator_name(const char *name)
1772 {
1773         if (name == NULL)
1774                 return;
1775
1776         g_free(net.operator_name);
1777         net.operator_name = g_strndup(name, 16);
1778         DBG("telephony-maemo6: operator name updated: %s", name);
1779 }
1780
1781 static void get_property_reply(DBusPendingCall *call, void *user_data)
1782 {
1783         char *prop = user_data;
1784         DBusError err;
1785         DBusMessage *reply;
1786         DBusMessageIter iter, sub;
1787
1788         reply = dbus_pending_call_steal_reply(call);
1789
1790         dbus_error_init(&err);
1791         if (dbus_set_error_from_message(&err, reply)) {
1792                 error("csd replied with an error: %s, %s",
1793                                 err.name, err.message);
1794                 dbus_error_free(&err);
1795                 goto done;
1796         }
1797
1798         dbus_message_iter_init(reply, &iter);
1799
1800         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1801                 error("Unexpected signature in Get return");
1802                 goto done;
1803         }
1804
1805         dbus_message_iter_recurse(&iter, &sub);
1806
1807         if (g_strcmp0(prop, "RegistrationStatus") == 0) {
1808                 const char *status;
1809
1810                 dbus_message_iter_get_basic(&sub, &status);
1811                 update_registration_status(status);
1812
1813                 get_property(CSD_CSNET_OPERATOR, "OperatorName");
1814                 get_property(CSD_CSNET_SIGNAL, "SignalBars");
1815         } else if (g_strcmp0(prop, "OperatorName") == 0) {
1816                 const char *name;
1817
1818                 dbus_message_iter_get_basic(&sub, &name);
1819                 update_operator_name(name);
1820         } else if (g_strcmp0(prop, "SignalBars") == 0) {
1821                 int32_t signal_bars;
1822
1823                 dbus_message_iter_get_basic(&sub, &signal_bars);
1824                 update_signal_strength(signal_bars);
1825         }
1826
1827 done:
1828         g_free(prop);
1829         dbus_message_unref(reply);
1830         remove_pending(call);
1831 }
1832
1833 static int get_property(const char *iface, const char *prop)
1834 {
1835         return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH,
1836                                 DBUS_INTERFACE_PROPERTIES, "Get",
1837                                 get_property_reply, g_strdup(prop),
1838                                 DBUS_TYPE_STRING, &iface,
1839                                 DBUS_TYPE_STRING, &prop,
1840                                 DBUS_TYPE_INVALID);
1841 }
1842
1843 static void handle_operator_name_changed(DBusMessage *msg)
1844 {
1845         const char *name;
1846
1847         if (!dbus_message_get_args(msg, NULL,
1848                                         DBUS_TYPE_STRING, &name,
1849                                         DBUS_TYPE_INVALID)) {
1850                 error("Unexpected parameters in OperatorNameChanged");
1851                 return;
1852         }
1853
1854         update_operator_name(name);
1855 }
1856
1857 static void call_info_reply(DBusPendingCall *call, void *user_data)
1858 {
1859         DBusError err;
1860         DBusMessage *reply;
1861         DBusMessageIter iter, sub;
1862
1863         get_calls_active = FALSE;
1864
1865         reply = dbus_pending_call_steal_reply(call);
1866
1867         dbus_error_init(&err);
1868         if (dbus_set_error_from_message(&err, reply)) {
1869                 error("csd replied with an error: %s, %s",
1870                                 err.name, err.message);
1871                 dbus_error_free(&err);
1872                 goto done;
1873         }
1874
1875         dbus_message_iter_init(reply, &iter);
1876
1877         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1878                 error("Unexpected signature in GetCallInfoAll return");
1879                 goto done;
1880         }
1881
1882         dbus_message_iter_recurse(&iter, &sub);
1883
1884         parse_call_list(&sub);
1885
1886         get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus");
1887
1888 done:
1889         dbus_message_unref(reply);
1890         remove_pending(call);
1891 }
1892
1893
1894 static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
1895 {
1896         DBusError derr;
1897         DBusMessage *reply;
1898         const char *name, *number, *secondname, *additionalnumber, *email;
1899         int index;
1900         char **number_type = user_data;
1901
1902         reply = dbus_pending_call_steal_reply(call);
1903
1904         dbus_error_init(&derr);
1905         if (dbus_set_error_from_message(&derr, reply)) {
1906                 error("%s.ReadFirst replied with an error: %s, %s",
1907                                 CSD_SIMPB_INTERFACE, derr.name, derr.message);
1908                 dbus_error_free(&derr);
1909                 if (number_type == &vmbx)
1910                         vmbx = g_strdup(getenv("VMBX_NUMBER"));
1911                 goto done;
1912         }
1913
1914         dbus_error_init(&derr);
1915         if (dbus_message_get_args(reply, NULL,
1916                                 DBUS_TYPE_INT32, &index,
1917                                 DBUS_TYPE_STRING, &name,
1918                                 DBUS_TYPE_STRING, &number,
1919                                 DBUS_TYPE_STRING, &secondname,
1920                                 DBUS_TYPE_STRING, &additionalnumber,
1921                                 DBUS_TYPE_STRING, &email,
1922                                 DBUS_TYPE_INVALID) == FALSE) {
1923                 error("Unable to parse %s.ReadFirst arguments: %s, %s",
1924                                 CSD_SIMPB_INTERFACE, derr.name, derr.message);
1925                 dbus_error_free(&derr);
1926                 goto done;
1927         }
1928
1929         if (number_type == &msisdn) {
1930                 g_free(msisdn);
1931                 msisdn = g_strdup(number);
1932                 DBG("Got MSISDN %s (%s)", number, name);
1933         } else {
1934                 g_free(vmbx);
1935                 vmbx = g_strdup(number);
1936                 DBG("Got voice mailbox number %s (%s)", number, name);
1937         }
1938
1939 done:
1940         dbus_message_unref(reply);
1941         remove_pending(call);
1942 }
1943
1944 static void csd_init(void)
1945 {
1946         const char *pb_type;
1947         int ret;
1948
1949         ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1950                                 CSD_CALL_INTERFACE, "GetCallInfoAll",
1951                                 call_info_reply, NULL, DBUS_TYPE_INVALID);
1952         if (ret < 0) {
1953                 error("Unable to sent GetCallInfoAll method call");
1954                 return;
1955         }
1956
1957         get_calls_active = TRUE;
1958
1959         pb_type = CSD_SIMPB_TYPE_MSISDN;
1960
1961         ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
1962                                 CSD_SIMPB_INTERFACE, "ReadFirst",
1963                                 phonebook_read_reply, &msisdn,
1964                                 DBUS_TYPE_STRING, &pb_type,
1965                                 DBUS_TYPE_INVALID);
1966         if (ret < 0) {
1967                 error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
1968                 return;
1969         }
1970
1971         /* Voicemail should be in MBDN index 0 */
1972         pb_type = CSD_SIMPB_TYPE_MBDN;
1973
1974         ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
1975                                 CSD_SIMPB_INTERFACE, "ReadFirst",
1976                                 phonebook_read_reply, &vmbx,
1977                                 DBUS_TYPE_STRING, &pb_type,
1978                                 DBUS_TYPE_INVALID);
1979         if (ret < 0) {
1980                 error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
1981                 return;
1982         }
1983 }
1984
1985 static void handle_modem_state(DBusMessage *msg)
1986 {
1987         const char *state;
1988
1989         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
1990                                                         DBUS_TYPE_INVALID)) {
1991                 error("Unexpected modem state parameters");
1992                 return;
1993         }
1994
1995         DBG("SSC modem state: %s", state);
1996
1997         if (calls != NULL || get_calls_active)
1998                 return;
1999
2000         if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
2001                 csd_init();
2002 }
2003
2004 static void modem_state_reply(DBusPendingCall *call, void *user_data)
2005 {
2006         DBusMessage *reply = dbus_pending_call_steal_reply(call);
2007         DBusError err;
2008
2009         dbus_error_init(&err);
2010         if (dbus_set_error_from_message(&err, reply)) {
2011                 error("get_modem_state: %s, %s", err.name, err.message);
2012                 dbus_error_free(&err);
2013         } else
2014                 handle_modem_state(reply);
2015
2016         dbus_message_unref(reply);
2017         remove_pending(call);
2018 }
2019
2020 static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
2021                                                                 void *data)
2022 {
2023         const char *path = dbus_message_get_path(msg);
2024
2025         if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
2026                 handle_incoming_call(msg);
2027         else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
2028                 handle_outgoing_call(msg);
2029         else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
2030                                                         "CreateRequested"))
2031                 handle_create_requested(msg);
2032         else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
2033                 handle_call_status(msg, path);
2034         else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
2035                 handle_conference(msg, TRUE);
2036         else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
2037                 handle_conference(msg, FALSE);
2038         else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
2039                                 "RegistrationChanged"))
2040                 handle_registration_changed(msg);
2041         else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
2042                                 "OperatorNameChanged"))
2043                 handle_operator_name_changed(msg);
2044         else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
2045                                 "SignalBarsChanged"))
2046                 handle_signal_bars_changed(msg);
2047         else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
2048                                         "PropertyModified"))
2049                 handle_hal_property_modified(msg);
2050         else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
2051                                                 "modem_state_changed_ind"))
2052                 handle_modem_state(msg);
2053
2054         return TRUE;
2055 }
2056
2057 static void add_watch(const char *sender, const char *path,
2058                                 const char *interface, const char *member)
2059 {
2060         guint watch;
2061
2062         watch = g_dbus_add_signal_watch(connection, sender, path, interface,
2063                                         member, signal_filter, NULL, NULL);
2064
2065         watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
2066 }
2067
2068 static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
2069 {
2070         DBusError err;
2071         DBusMessage *reply;
2072         DBusMessageIter iter, sub;
2073         const char *path;
2074         int type;
2075
2076         reply = dbus_pending_call_steal_reply(call);
2077
2078         dbus_error_init(&err);
2079         if (dbus_set_error_from_message(&err, reply)) {
2080                 error("hald replied with an error: %s, %s",
2081                                 err.name, err.message);
2082                 dbus_error_free(&err);
2083                 goto done;
2084         }
2085
2086         dbus_message_iter_init(reply, &iter);
2087
2088         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
2089                 error("Unexpected signature in FindDeviceByCapability return");
2090                 goto done;
2091         }
2092
2093         dbus_message_iter_recurse(&iter, &sub);
2094
2095         type = dbus_message_iter_get_arg_type(&sub);
2096
2097         if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
2098                 error("No hal device with battery capability found");
2099                 goto done;
2100         }
2101
2102         dbus_message_iter_get_basic(&sub, &path);
2103
2104         DBG("telephony-maemo6: found battery device at %s", path);
2105
2106         add_watch(NULL, path, "org.freedesktop.Hal.Device",
2107                                                         "PropertyModified");
2108
2109         hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
2110         hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
2111         hal_get_integer(path, "battery.charge_level.design", &battchg_design);
2112
2113 done:
2114         dbus_message_unref(reply);
2115         remove_pending(call);
2116 }
2117
2118 int telephony_init(void)
2119 {
2120         const char *battery_cap = "battery";
2121         uint32_t features = AG_FEATURE_EC_ANDOR_NR |
2122                                 AG_FEATURE_INBAND_RINGTONE |
2123                                 AG_FEATURE_REJECT_A_CALL |
2124                                 AG_FEATURE_ENHANCED_CALL_STATUS |
2125                                 AG_FEATURE_ENHANCED_CALL_CONTROL |
2126                                 AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
2127                                 AG_FEATURE_THREE_WAY_CALLING;
2128         int i;
2129
2130         DBG("");
2131
2132         connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
2133
2134         add_watch(NULL, NULL, CSD_CALL_INTERFACE, NULL);
2135         add_watch(NULL, NULL, CSD_CALL_INSTANCE, NULL);
2136         add_watch(NULL, NULL, CSD_CALL_CONFERENCE, NULL);
2137         add_watch(NULL, NULL, CSD_CSNET_REGISTRATION, "RegistrationChanged");
2138         add_watch(NULL, NULL, CSD_CSNET_OPERATOR, "OperatorNameChanged");
2139         add_watch(NULL, NULL, CSD_CSNET_SIGNAL, "SignalBarsChanged");
2140         add_watch(NULL, NULL, SSC_DBUS_IFACE, "modem_state_changed_ind");
2141
2142         if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
2143                                         "get_modem_state", modem_state_reply,
2144                                         NULL, DBUS_TYPE_INVALID) < 0)
2145                 error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
2146
2147         /* Reset indicators */
2148         for (i = 0; maemo_indicators[i].desc != NULL; i++) {
2149                 if (g_str_equal(maemo_indicators[i].desc, "battchg"))
2150                         maemo_indicators[i].val = 5;
2151                 else
2152                         maemo_indicators[i].val = 0;
2153         }
2154
2155         telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
2156                                                                 chld_str);
2157         if (send_method_call("org.freedesktop.Hal",
2158                                 "/org/freedesktop/Hal/Manager",
2159                                 "org.freedesktop.Hal.Manager",
2160                                 "FindDeviceByCapability",
2161                                 hal_find_device_reply, NULL,
2162                                 DBUS_TYPE_STRING, &battery_cap,
2163                                 DBUS_TYPE_INVALID) < 0)
2164                 error("Unable to send HAL method call");
2165
2166         return 0;
2167 }
2168
2169 static void remove_watch(gpointer data)
2170 {
2171         g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
2172 }
2173
2174 void telephony_exit(void)
2175 {
2176         DBG("");
2177
2178         g_free(net.operator_name);
2179         net.operator_name = NULL;
2180
2181         net.status = NETWORK_REG_STATUS_UNKOWN;
2182         net.signal_bars = 0;
2183
2184         g_slist_free(active_calls);
2185         active_calls = NULL;
2186
2187         g_slist_free_full(calls, csd_call_free);
2188         calls = NULL;
2189
2190         g_slist_free_full(pending, pending_req_finalize);
2191         pending = NULL;
2192
2193         g_slist_free_full(watches, remove_watch);
2194         watches = NULL;
2195
2196         dbus_connection_unref(connection);
2197         connection = NULL;
2198
2199         telephony_deinit();
2200 }