ofono: Cleanup connection_manager_init()
[platform/upstream/connman.git] / plugins / ofono.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
7  *  Copyright (C) 2011  BWM Car IT GmbH. All rights reserved.
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 version 2 as
11  *  published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <stdlib.h>
30
31 #include <gdbus.h>
32 #include <string.h>
33 #include <stdint.h>
34
35 #define CONNMAN_API_SUBJECT_TO_CHANGE
36 #include <connman/plugin.h>
37 #include <connman/device.h>
38 #include <connman/network.h>
39 #include <connman/inet.h>
40 #include <connman/dbus.h>
41 #include <connman/log.h>
42
43 #define uninitialized_var(x) x = x
44
45 #define OFONO_SERVICE                   "org.ofono"
46
47 #define OFONO_MANAGER_INTERFACE         OFONO_SERVICE ".Manager"
48 #define OFONO_MODEM_INTERFACE           OFONO_SERVICE ".Modem"
49 #define OFONO_SIM_INTERFACE             OFONO_SERVICE ".SimManager"
50 #define OFONO_NETREG_INTERFACE          OFONO_SERVICE ".NetworkRegistration"
51 #define OFONO_CM_INTERFACE              OFONO_SERVICE ".ConnectionManager"
52 #define OFONO_CONTEXT_INTERFACE         OFONO_SERVICE ".ConnectionContext"
53 #define OFONO_CDMA_CM_INTERFACE         OFONO_SERVICE ".cdma.ConnectionManager"
54 #define OFONO_CDMA_NETREG_INTERFACE     OFONO_SERVICE ".cdma.NetworkRegistration"
55
56 #define MODEM_ADDED                     "ModemAdded"
57 #define MODEM_REMOVED                   "ModemRemoved"
58 #define PROPERTY_CHANGED                "PropertyChanged"
59 #define CONTEXT_ADDED                   "ContextAdded"
60 #define CONTEXT_REMOVED                 "ContextRemoved"
61
62 #define GET_PROPERTIES                  "GetProperties"
63 #define SET_PROPERTY                    "SetProperty"
64 #define GET_MODEMS                      "GetModems"
65 #define GET_CONTEXTS                    "GetContexts"
66
67 #define TIMEOUT 40000
68
69 enum ofono_api {
70         OFONO_API_SIM =         0x1,
71         OFONO_API_NETREG =      0x2,
72         OFONO_API_CM =          0x4,
73         OFONO_API_CDMA_NETREG = 0x8,
74         OFONO_API_CDMA_CM =     0x10,
75 };
76
77 /*
78  * The way this plugin works is following:
79  *
80  *   powered -> SubscriberIdentity or Online = True -> gprs, context ->
81  *     attached -> netreg -> ready
82  *
83  * Enabling and disabling modems are steered through the rfkill
84  * interface. That means when ConnMan toggles the rfkill bit oFono
85  * will add or remove the modems.
86  *
87  * ConnMan will always power up (set Powered and Online) the
88  * modems. No need to power them down because this will be done
89  * through the rfkill inteface.
90  */
91
92 static DBusConnection *connection;
93
94 static GHashTable *modem_hash;
95 static GHashTable *context_hash;
96
97 struct network_context {
98         char *path;
99         int index;
100
101         enum connman_ipconfig_method ipv4_method;
102         struct connman_ipaddress *ipv4_address;
103         char *ipv4_nameservers;
104
105         enum connman_ipconfig_method ipv6_method;
106         struct connman_ipaddress *ipv6_address;
107         char *ipv6_nameservers;
108 };
109
110 struct modem_data {
111         char *path;
112
113         struct connman_device *device;
114         struct connman_network *network;
115
116         struct network_context *context;
117
118         /* Modem Interface */
119         char *serial;
120         connman_bool_t powered;
121         connman_bool_t online;
122         uint8_t interfaces;
123         connman_bool_t ignore;
124
125         connman_bool_t set_powered;
126         connman_bool_t set_online;
127
128         /* ConnectionManager Interface */
129         connman_bool_t attached;
130         connman_bool_t cm_powered;
131
132         connman_bool_t set_cm_powered;
133
134         /* ConnectionContext Interface */
135         connman_bool_t active;
136         connman_bool_t set_active;
137
138         /* SimManager Interface */
139         char *imsi;
140
141         /* Netreg Interface */
142         char *name;
143         uint8_t strength;
144         connman_bool_t roaming;
145
146         /* pending calls */
147         DBusPendingCall *call_set_property;
148         DBusPendingCall *call_get_properties;
149         DBusPendingCall *call_get_contexts;
150 };
151
152 static char *get_ident(const char *path)
153 {
154         char *pos;
155
156         if (*path != '/')
157                 return NULL;
158
159         pos = strrchr(path, '/');
160         if (pos == NULL)
161                 return NULL;
162
163         return pos + 1;
164 }
165
166 static struct network_context *network_context_alloc(const char *path)
167 {
168         struct network_context *context;
169
170         context = g_try_new0(struct network_context, 1);
171         if (context == NULL)
172                 return NULL;
173
174         context->path = g_strdup(path);
175         context->index = -1;
176
177         context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
178         context->ipv4_address = NULL;
179         context->ipv4_nameservers = NULL;
180
181         context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
182         context->ipv6_address = NULL;
183         context->ipv6_nameservers = NULL;
184
185         return context;
186 }
187
188 static void network_context_free(struct network_context *context)
189 {
190         g_free(context->path);
191
192         connman_ipaddress_free(context->ipv4_address);
193         g_free(context->ipv4_nameservers);
194
195         connman_ipaddress_free(context->ipv6_address);
196         g_free(context->ipv6_nameservers);
197
198         free(context);
199 }
200
201 static void set_connected(struct modem_data *modem)
202 {
203         connman_bool_t setip = FALSE;
204
205         DBG("%s", modem->path);
206
207         connman_network_set_index(modem->network, modem->context->index);
208
209         switch (modem->context->ipv4_method) {
210         case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
211         case CONNMAN_IPCONFIG_METHOD_OFF:
212         case CONNMAN_IPCONFIG_METHOD_MANUAL:
213         case CONNMAN_IPCONFIG_METHOD_AUTO:
214                 break;
215
216         case CONNMAN_IPCONFIG_METHOD_FIXED:
217                 connman_network_set_ipv4_method(modem->network,
218                                                 modem->context->ipv4_method);
219                 connman_network_set_ipaddress(modem->network,
220                                                 modem->context->ipv4_address);
221                 connman_network_set_nameservers(modem->network,
222                                         modem->context->ipv4_nameservers);
223                 setip = TRUE;
224                 break;
225
226         case CONNMAN_IPCONFIG_METHOD_DHCP:
227                 connman_network_set_ipv4_method(modem->network,
228                                                 modem->context->ipv4_method);
229                 setip = TRUE;
230                 break;
231         }
232
233         switch (modem->context->ipv6_method) {
234         case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
235         case CONNMAN_IPCONFIG_METHOD_OFF:
236         case CONNMAN_IPCONFIG_METHOD_MANUAL:
237         case CONNMAN_IPCONFIG_METHOD_DHCP:
238         case CONNMAN_IPCONFIG_METHOD_AUTO:
239                 break;
240
241         case CONNMAN_IPCONFIG_METHOD_FIXED:
242                 connman_network_set_ipv6_method(modem->network,
243                                                         modem->context->ipv6_method);
244                 connman_network_set_ipaddress(modem->network,
245                                                         modem->context->ipv6_address);
246                 setip = TRUE;
247                 break;
248         }
249
250         if (setip == TRUE)
251                 connman_network_set_connected(modem->network, TRUE);
252 }
253
254 static void set_disconnected(struct modem_data *modem)
255 {
256         DBG("%s", modem->path);
257
258         connman_network_set_connected(modem->network, FALSE);
259 }
260
261 typedef void (*set_property_cb)(struct modem_data *data,
262                                 connman_bool_t success);
263 typedef void (*get_properties_cb)(struct modem_data *data,
264                                 DBusMessageIter *dict);
265
266 struct property_info {
267         struct modem_data *modem;
268         const char *path;
269         const char *interface;
270         const char *property;
271         set_property_cb set_property_cb;
272         get_properties_cb get_properties_cb;
273 };
274
275 static void set_property_reply(DBusPendingCall *call, void *user_data)
276 {
277         struct property_info *info = user_data;
278         DBusMessage *reply;
279         DBusError error;
280         connman_bool_t success = TRUE;
281
282         DBG("%s path %s %s.%s", info->modem->path,
283                 info->path, info->interface, info->property);
284
285         info->modem->call_set_property = NULL;
286
287         dbus_error_init(&error);
288
289         reply = dbus_pending_call_steal_reply(call);
290
291         if (dbus_set_error_from_message(&error, reply)) {
292                 connman_error("Failed to change property: %s %s.%s: %s %s",
293                                 info->path, info->interface, info->property,
294                                 error.name, error.message);
295                 dbus_error_free(&error);
296                 success = FALSE;
297         }
298
299         if (info->set_property_cb != NULL)
300                 (*info->set_property_cb)(info->modem, success);
301
302         dbus_message_unref(reply);
303
304         dbus_pending_call_unref(call);
305 }
306
307 static int set_property(struct modem_data *modem,
308                         const char *path, const char *interface,
309                         const char *property, int type, void *value,
310                         set_property_cb notify)
311 {
312         DBusMessage *message;
313         DBusMessageIter iter;
314         struct property_info *info;
315
316         DBG("%s path %s %s.%s", modem->path, path, interface, property);
317
318         if (modem->call_set_property != NULL) {
319                 connman_error("Pending SetProperty");
320                 return -EBUSY;
321         }
322
323         message = dbus_message_new_method_call(OFONO_SERVICE, path,
324                                         interface, SET_PROPERTY);
325         if (message == NULL)
326                 return -ENOMEM;
327
328         dbus_message_iter_init_append(message, &iter);
329         connman_dbus_property_append_basic(&iter, property, type, value);
330
331         if (dbus_connection_send_with_reply(connection, message,
332                         &modem->call_set_property, TIMEOUT) == FALSE) {
333                 connman_error("Failed to change property: %s %s.%s",
334                                 path, interface, property);
335                 dbus_message_unref(message);
336                 return -EINVAL;
337         }
338
339         if (modem->call_set_property == NULL) {
340                 connman_error("D-Bus connection not available");
341                 dbus_message_unref(message);
342                 return -EINVAL;
343         }
344
345         info = g_try_new0(struct property_info, 1);
346         if (info == NULL) {
347                 dbus_message_unref(message);
348                 return -ENOMEM;
349         }
350
351         info->modem = modem;
352         info->path = path;
353         info->interface = interface;
354         info->property = property;
355         info->set_property_cb = notify;
356
357         dbus_pending_call_set_notify(modem->call_set_property,
358                                         set_property_reply, info, g_free);
359
360         dbus_message_unref(message);
361
362         return -EINPROGRESS;
363 }
364
365 static void get_properties_reply(DBusPendingCall *call, void *user_data)
366 {
367         struct property_info *info = user_data;
368         DBusMessageIter array, dict;
369         DBusMessage *reply;
370         DBusError error;
371
372         DBG("%s path %s %s", info->modem->path, info->path, info->interface);
373
374         info->modem->call_get_properties = NULL;
375
376         dbus_error_init(&error);
377
378         reply = dbus_pending_call_steal_reply(call);
379
380         if (dbus_set_error_from_message(&error, reply)) {
381                 connman_error("Failed to get properties: %s %s: %s %s",
382                                 info->path, info->interface,
383                                 error.name, error.message);
384                 dbus_error_free(&error);
385
386                 goto done;
387         }
388
389         if (dbus_message_iter_init(reply, &array) == FALSE)
390                 goto done;
391
392         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
393                 goto done;
394
395         dbus_message_iter_recurse(&array, &dict);
396
397         if (info->get_properties_cb != NULL)
398                 (*info->get_properties_cb)(info->modem, &dict);
399
400 done:
401
402         dbus_message_unref(reply);
403
404         dbus_pending_call_unref(call);
405 }
406
407 static int get_properties(const char *path, const char *interface,
408                                 get_properties_cb notify,
409                                 struct modem_data *modem)
410 {
411         DBusMessage *message;
412         struct property_info *info;
413
414         DBG("%s path %s %s", modem->path, path, interface);
415
416         if (modem->call_get_properties != NULL) {
417                 connman_error("Pending GetProperties");
418                 return -EBUSY;
419         }
420
421         message = dbus_message_new_method_call(OFONO_SERVICE, path,
422                                         interface, GET_PROPERTIES);
423         if (message == NULL)
424                 return -ENOMEM;
425
426         if (dbus_connection_send_with_reply(connection, message,
427                         &modem->call_get_properties, TIMEOUT) == FALSE) {
428                 connman_error("Failed to call %s.GetProperties()", interface);
429                 dbus_message_unref(message);
430                 return -EINVAL;
431         }
432
433         if (modem->call_get_properties == NULL) {
434                 connman_error("D-Bus connection not available");
435                 dbus_message_unref(message);
436                 return -EINVAL;
437         }
438
439         info = g_try_new0(struct property_info, 1);
440         if (info == NULL) {
441                 dbus_message_unref(message);
442                 return -ENOMEM;
443         }
444
445         info->modem = modem;
446         info->path = path;
447         info->interface = interface;
448         info->get_properties_cb = notify;
449
450         dbus_pending_call_set_notify(modem->call_get_properties,
451                                         get_properties_reply, info, g_free);
452
453         dbus_message_unref(message);
454
455         return -EINPROGRESS;
456 }
457
458 static void context_set_active_reply(struct modem_data *modem,
459                                         connman_bool_t success)
460 {
461         DBG("%s", modem->path);
462
463         if (success == TRUE) {
464                 /*
465                  * Don't handle do anything on success here. oFono will send
466                  * the change via PropertyChanged singal.
467                  */
468                 return;
469         }
470
471         /*
472          * Active = True might fail due a timeout. That means oFono
473          * still tries to go online. If we retry to set Active = True,
474          * we just get a InProgress error message. Should we power
475          * cycle the modem in such cases?
476          */
477
478         connman_network_set_error(modem->network,
479                                 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
480 }
481
482 static int context_set_active(struct modem_data *modem)
483 {
484         dbus_bool_t active = TRUE;
485
486         DBG("%s", modem->path);
487
488         return set_property(modem, modem->context->path,
489                                 OFONO_CONTEXT_INTERFACE,
490                                 "Active", DBUS_TYPE_BOOLEAN,
491                                 &active,
492                                 context_set_active_reply);
493 }
494
495 static int context_set_inactive(struct modem_data *modem)
496 {
497         dbus_bool_t active = FALSE;
498         int err;
499
500         DBG("%s", modem->path);
501
502         err = set_property(modem, modem->context->path,
503                                 OFONO_CONTEXT_INTERFACE,
504                                 "Active", DBUS_TYPE_BOOLEAN,
505                                 &active,
506                                 NULL);
507         if (err == -EINPROGRESS)
508                 return 0;
509
510         return err;
511 }
512
513 static void cdma_cm_set_powered_reply(struct modem_data *modem,
514                                         connman_bool_t success)
515 {
516         DBG("%s", modem->path);
517
518         if (success == TRUE) {
519                 /*
520                  * Don't handle do anything on success here. oFono will send
521                  * the change via PropertyChanged singal.
522                  */
523                 return;
524         }
525
526         /*
527          * Powered = True might fail due a timeout. That means oFono
528          * still tries to go online. If we retry to set Powered = True,
529          * we just get a InProgress error message. Should we power
530          * cycle the modem in such cases?
531          */
532
533         connman_network_set_error(modem->network,
534                                 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
535 }
536
537 static int cdma_cm_set_powered(struct modem_data *modem)
538 {
539         dbus_bool_t powered = TRUE;
540
541         DBG("%s", modem->path);
542
543         return set_property(modem, modem->path, OFONO_CDMA_CM_INTERFACE,
544                                 "Powered", DBUS_TYPE_BOOLEAN, &powered,
545                                 cdma_cm_set_powered_reply);
546 }
547
548 static int cdma_cm_shutdown(struct modem_data *modem)
549 {
550         dbus_bool_t powered = FALSE;
551         int err;
552
553         DBG("%s", modem->path);
554
555         err = set_property(modem, modem->path, OFONO_CDMA_CM_INTERFACE,
556                                 "Powered", DBUS_TYPE_BOOLEAN, &powered, NULL);
557         if (err == -EINPROGRESS)
558                 return 0;
559
560         return err;
561 }
562
563 static void modem_set_online_reply(struct modem_data *modem,
564                                         connman_bool_t success)
565 {
566         DBG("%s", modem->path);
567
568         if (success == TRUE) {
569                 /*
570                  * Don't handle do anything on success here. oFono will send
571                  * the change via PropertyChanged singal.
572                  */
573                 return;
574         }
575
576         modem->set_online = FALSE;
577 }
578
579 static int modem_set_online(struct modem_data *modem)
580 {
581         DBG("%s", modem->path);
582
583         modem->set_online = TRUE;
584
585         return set_property(modem, modem->path,
586                                 OFONO_MODEM_INTERFACE,
587                                 "Online", DBUS_TYPE_BOOLEAN,
588                                 &modem->set_online,
589                                 modem_set_online_reply);
590 }
591
592 static void cm_set_powered_reply(struct modem_data *modem,
593                                         connman_bool_t success)
594 {
595         DBG("%s", modem->path);
596
597         if (success == TRUE) {
598                 /*
599                  * Don't handle do anything on success here. oFono will send
600                  * the change via PropertyChanged singal.
601                  */
602                 return;
603         }
604
605         modem->set_cm_powered = FALSE;
606 }
607
608 static int cm_set_powered(struct modem_data *modem)
609 {
610         DBG("%s", modem->path);
611
612         modem->set_cm_powered = TRUE;
613
614         return set_property(modem, modem->path,
615                                 OFONO_CM_INTERFACE,
616                                 "Powered", DBUS_TYPE_BOOLEAN,
617                                 &modem->set_cm_powered,
618                                 cm_set_powered_reply);
619 }
620
621 static int modem_set_powered(struct modem_data *modem)
622 {
623         DBG("%s", modem->path);
624
625         modem->set_powered = TRUE;
626
627         return set_property(modem, modem->path,
628                                 OFONO_MODEM_INTERFACE,
629                                 "Powered", DBUS_TYPE_BOOLEAN,
630                                 &modem->set_powered,
631                                 NULL);
632 }
633
634 static int modem_set_unpowered(struct modem_data *modem)
635 {
636         DBG("%s", modem->path);
637
638         modem->set_powered = FALSE;
639
640         return set_property(modem, modem->path,
641                                 OFONO_MODEM_INTERFACE,
642                                 "Powered", DBUS_TYPE_BOOLEAN,
643                                 &modem->set_powered,
644                                 NULL);
645 }
646
647 static connman_bool_t has_interface(uint8_t interfaces,
648                                         enum ofono_api api)
649 {
650         if ((interfaces & api) == api)
651                 return TRUE;
652
653         return FALSE;
654 }
655
656 static uint8_t extract_interfaces(DBusMessageIter *array)
657 {
658         DBusMessageIter entry;
659         uint8_t interfaces = 0;
660
661         dbus_message_iter_recurse(array, &entry);
662
663         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
664                 const char *name;
665
666                 dbus_message_iter_get_basic(&entry, &name);
667
668                 if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
669                         interfaces |= OFONO_API_SIM;
670                 else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
671                         interfaces |= OFONO_API_NETREG;
672                 else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
673                         interfaces |= OFONO_API_CM;
674                 else if (g_str_equal(name, OFONO_CDMA_CM_INTERFACE) == TRUE)
675                         interfaces |= OFONO_API_CDMA_CM;
676                 else if (g_str_equal(name, OFONO_CDMA_NETREG_INTERFACE) == TRUE)
677                         interfaces |= OFONO_API_CDMA_NETREG;
678
679                 dbus_message_iter_next(&entry);
680         }
681
682         return interfaces;
683 }
684
685 static char *extract_nameservers(DBusMessageIter *array)
686 {
687         DBusMessageIter entry;
688         char *nameservers = NULL;
689         char *tmp;
690
691         dbus_message_iter_recurse(array, &entry);
692
693         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
694                 const char *nameserver;
695
696                 dbus_message_iter_get_basic(&entry, &nameserver);
697
698                 if (nameservers == NULL) {
699                         nameservers = g_strdup(nameserver);
700                 } else {
701                         tmp = nameservers;
702                         nameservers = g_strdup_printf("%s %s", tmp, nameserver);
703                         g_free(tmp);
704                 }
705
706                 dbus_message_iter_next(&entry);
707         }
708
709         return nameservers;
710 }
711
712 static void extract_ipv4_settings(DBusMessageIter *array,
713                                 struct network_context *context)
714 {
715         DBusMessageIter dict;
716         char *address = NULL, *netmask = NULL, *gateway = NULL;
717         char *nameservers = NULL;
718         const char *interface = NULL;
719         int index = -1;
720
721         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
722                 return;
723
724         dbus_message_iter_recurse(array, &dict);
725
726         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
727                 DBusMessageIter entry, value;
728                 const char *key, *val;
729
730                 dbus_message_iter_recurse(&dict, &entry);
731                 dbus_message_iter_get_basic(&entry, &key);
732
733                 dbus_message_iter_next(&entry);
734                 dbus_message_iter_recurse(&entry, &value);
735
736                 if (g_str_equal(key, "Interface") == TRUE) {
737                         dbus_message_iter_get_basic(&value, &interface);
738
739                         DBG("Interface %s", interface);
740
741                         index = connman_inet_ifindex(interface);
742
743                         DBG("index %d", index);
744                 } else if (g_str_equal(key, "Method") == TRUE) {
745                         dbus_message_iter_get_basic(&value, &val);
746
747                         DBG("Method %s", val);
748
749                         if (g_strcmp0(val, "static") == 0) {
750                                 context->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED;
751                         } else if (g_strcmp0(val, "dhcp") == 0) {
752                                 context->ipv4_method = CONNMAN_IPCONFIG_METHOD_DHCP;
753                                 break;
754                         }
755                 } else if (g_str_equal(key, "Address") == TRUE) {
756                         dbus_message_iter_get_basic(&value, &val);
757
758                         address = g_strdup(val);
759
760                         DBG("Address %s", address);
761                 } else if (g_str_equal(key, "Netmask") == TRUE) {
762                         dbus_message_iter_get_basic(&value, &val);
763
764                         netmask = g_strdup(val);
765
766                         DBG("Netmask %s", netmask);
767                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
768                         nameservers = extract_nameservers(&value);
769
770                         DBG("Nameservers %s", nameservers);
771                 } else if (g_str_equal(key, "Gateway") == TRUE) {
772                         dbus_message_iter_get_basic(&value, &val);
773
774                         gateway = g_strdup(val);
775
776                         DBG("Gateway %s", gateway);
777                 }
778
779                 dbus_message_iter_next(&dict);
780         }
781
782         if (index < 0)
783                 goto out;
784
785         if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED)
786                 goto out;
787
788         context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
789         if (context->ipv4_address == NULL)
790                 goto out;
791
792         context->index = index;
793         connman_ipaddress_set_ipv4(context->ipv4_address, address,
794                                 netmask, gateway);
795
796         context->ipv4_nameservers = nameservers;
797
798 out:
799         if (context->ipv4_nameservers != nameservers)
800                 g_free(nameservers);
801
802         g_free(address);
803         g_free(netmask);
804         g_free(gateway);
805 }
806
807 static void extract_ipv6_settings(DBusMessageIter *array,
808                                 struct network_context *context)
809 {
810         DBusMessageIter dict;
811         char *address = NULL, *gateway = NULL;
812         unsigned char prefix_length;
813         char *nameservers = NULL;
814         const char *interface = NULL;
815         int index = -1;
816
817         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
818                 return;
819
820         dbus_message_iter_recurse(array, &dict);
821
822         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
823                 DBusMessageIter entry, value;
824                 const char *key, *val;
825
826                 dbus_message_iter_recurse(&dict, &entry);
827                 dbus_message_iter_get_basic(&entry, &key);
828
829                 dbus_message_iter_next(&entry);
830                 dbus_message_iter_recurse(&entry, &value);
831
832                 if (g_str_equal(key, "Interface") == TRUE) {
833                         dbus_message_iter_get_basic(&value, &interface);
834
835                         DBG("Interface %s", interface);
836
837                         index = connman_inet_ifindex(interface);
838
839                         DBG("index %d", index);
840                 } else if (g_str_equal(key, "Address") == TRUE) {
841                         dbus_message_iter_get_basic(&value, &val);
842
843                         address = g_strdup(val);
844
845                         DBG("Address %s", address);
846                 } else if (g_str_equal(key, "PrefixLength") == TRUE) {
847                         dbus_message_iter_get_basic(&value, &prefix_length);
848
849                         DBG("prefix length %d", prefix_length);
850                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
851                         nameservers = extract_nameservers(&value);
852
853                         DBG("Nameservers %s", nameservers);
854                 } else if (g_str_equal(key, "Gateway") == TRUE) {
855                         dbus_message_iter_get_basic(&value, &val);
856
857                         gateway = g_strdup(val);
858
859                         DBG("Gateway %s", gateway);
860                 }
861
862                 dbus_message_iter_next(&dict);
863         }
864
865         if (index < 0)
866                 goto out;
867
868         context->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
869
870         context->ipv6_address =
871                 connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
872         if (context->ipv6_address == NULL)
873                 goto out;
874
875         context->index = index;
876         connman_ipaddress_set_ipv6(context->ipv6_address, address,
877                                 prefix_length, gateway);
878
879         context->ipv6_nameservers = nameservers;
880
881 out:
882         if (context->ipv6_nameservers != nameservers)
883                 g_free(nameservers);
884
885         g_free(address);
886         g_free(gateway);
887 }
888
889 static connman_bool_t ready_to_create_device(struct modem_data *modem)
890 {
891         /*
892          * There are three different modem types which behave slightly
893          * different:
894          * - GSM modems will expose the SIM interface then the
895          *   CM interface.
896          * - DUN modems will expose first a unique serial number (BDADDR)
897          *   and then the CM interface.
898          * - CDMA modems will expose CM first and sometime later
899          *   a unique serial number.
900          *
901          * This functions tests if we have the necessary information gathered
902          * before we are able to create a device.
903          */
904
905         if (modem->device != NULL)
906                 return FALSE;
907
908         if (modem->imsi != NULL || modem->serial != NULL)
909                 return TRUE;
910
911         return FALSE;
912 }
913
914 static void create_device(struct modem_data *modem)
915 {
916         struct connman_device *device;
917         char *uninitialized_var(ident);
918
919         DBG("%s", modem->path);
920
921         if (modem->imsi != NULL)
922                 ident = modem->imsi;
923         else if (modem->serial != NULL)
924                 ident = modem->serial;
925
926         if (connman_dbus_validate_ident(ident) == FALSE)
927                 ident = connman_dbus_encode_string(ident);
928         else
929                 ident = g_strdup(ident);
930
931         device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_CELLULAR);
932         if (device == NULL)
933                 goto out;
934
935         DBG("device %p", device);
936
937         connman_device_set_ident(device, ident);
938
939         connman_device_set_string(device, "Path", modem->path);
940
941         connman_device_set_data(device, modem);
942
943         if (connman_device_register(device) < 0) {
944                 connman_error("Failed to register cellular device");
945                 connman_device_unref(device);
946                 goto out;
947         }
948
949         modem->device = device;
950
951 out:
952         g_free(ident);
953 }
954
955 static void destroy_device(struct modem_data *modem)
956 {
957         DBG("%s", modem->path);
958
959         connman_device_set_powered(modem->device, FALSE);
960
961         if (modem->network != NULL) {
962                 connman_device_remove_network(modem->device, modem->network);
963                 connman_network_unref(modem->network);
964                 modem->network = NULL;
965         }
966
967         connman_device_unregister(modem->device);
968         connman_device_unref(modem->device);
969
970         modem->device = NULL;
971 }
972
973 static void add_network(struct modem_data *modem)
974 {
975         const char *group;
976
977         DBG("%s", modem->path);
978
979         if (modem->network != NULL)
980                 return;
981
982         modem->network = connman_network_create(modem->context->path,
983                                                 CONNMAN_NETWORK_TYPE_CELLULAR);
984         if (modem->network == NULL)
985                 return;
986
987         DBG("network %p", modem->network);
988
989         connman_network_set_data(modem->network, modem);
990
991         connman_network_set_string(modem->network, "Path",
992                                         modem->context->path);
993
994         connman_network_set_index(modem->network, modem->context->index);
995
996         if (modem->name != NULL)
997                 connman_network_set_name(modem->network, modem->name);
998         else
999                 connman_network_set_name(modem->network, "");
1000
1001         connman_network_set_strength(modem->network, modem->strength);
1002
1003         group = get_ident(modem->context->path);
1004         connman_network_set_group(modem->network, group);
1005
1006         connman_network_set_available(modem->network, TRUE);
1007
1008         connman_network_set_bool(modem->network, "Roaming",
1009                                         modem->roaming);
1010
1011         if (connman_device_add_network(modem->device, modem->network) < 0) {
1012                 connman_network_unref(modem->network);
1013                 modem->network = NULL;
1014                 return;
1015         }
1016 }
1017
1018 static int add_cm_context(struct modem_data *modem, const char *context_path,
1019                                 DBusMessageIter *dict)
1020 {
1021         const char *context_type;
1022         struct network_context *context = NULL;
1023         connman_bool_t active = FALSE;
1024
1025         DBG("%s context path %s", modem->path, context_path);
1026
1027         if (modem->context != NULL) {
1028                 /*
1029                  * We have already assigned a context to this modem
1030                  * and we do only support one Internet context.
1031                  */
1032                 return -EALREADY;
1033         }
1034
1035         context = network_context_alloc(context_path);
1036         if (context == NULL)
1037                 return -ENOMEM;
1038
1039         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
1040                 DBusMessageIter entry, value;
1041                 const char *key;
1042
1043                 dbus_message_iter_recurse(dict, &entry);
1044                 dbus_message_iter_get_basic(&entry, &key);
1045
1046                 dbus_message_iter_next(&entry);
1047                 dbus_message_iter_recurse(&entry, &value);
1048
1049                 if (g_str_equal(key, "Type") == TRUE) {
1050                         dbus_message_iter_get_basic(&value, &context_type);
1051
1052                         DBG("%s context %s type %s", modem->path,
1053                                 context_path, context_type);
1054                 } else if (g_str_equal(key, "Settings") == TRUE) {
1055                         DBG("%s Settings", modem->path);
1056
1057                         extract_ipv4_settings(&value, context);
1058                 } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
1059                         DBG("%s IPv6.Settings", modem->path);
1060
1061                         extract_ipv6_settings(&value, context);
1062                 } else if (g_str_equal(key, "Active") == TRUE) {
1063                         dbus_message_iter_get_basic(&value, &active);
1064
1065                         DBG("%s Active %d", modem->path, active);
1066                 }
1067
1068                 dbus_message_iter_next(dict);
1069         }
1070
1071         if (g_strcmp0(context_type, "internet") != 0) {
1072                 network_context_free(context);
1073                 return -EINVAL;
1074         }
1075
1076         modem->context = context;
1077         modem->active = active;
1078
1079         g_hash_table_replace(context_hash, g_strdup(context_path), modem);
1080
1081         return 0;
1082 }
1083
1084 static void remove_cm_context(struct modem_data *modem,
1085                                 const char *context_path)
1086 {
1087         if (modem->context == NULL)
1088                 return;
1089
1090         g_hash_table_remove(context_hash, context_path);
1091
1092         network_context_free(modem->context);
1093         modem->context = NULL;
1094 }
1095
1096 static gboolean context_changed(DBusConnection *connection,
1097                                 DBusMessage *message,
1098                                 void *user_data)
1099 {
1100         const char *context_path = dbus_message_get_path(message);
1101         struct modem_data *modem = NULL;
1102         DBusMessageIter iter, value;
1103         const char *key;
1104
1105         DBG("context_path %s", context_path);
1106
1107         modem = g_hash_table_lookup(context_hash, context_path);
1108         if (modem == NULL)
1109                 return TRUE;
1110
1111         if (dbus_message_iter_init(message, &iter) == FALSE)
1112                 return TRUE;
1113
1114         dbus_message_iter_get_basic(&iter, &key);
1115
1116         dbus_message_iter_next(&iter);
1117         dbus_message_iter_recurse(&iter, &value);
1118
1119         /*
1120          * oFono guarantees the ordering of Settings and
1121          * Active. Settings will always be send before Active = True.
1122          * That means we don't have to order here.
1123          */
1124         if (g_str_equal(key, "Settings") == TRUE) {
1125                 DBG("%s Settings", modem->path);
1126
1127                 extract_ipv4_settings(&value, modem->context);
1128         } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
1129                 DBG("%s IPv6.Settings", modem->path);
1130
1131                 extract_ipv6_settings(&value, modem->context);
1132         } else if (g_str_equal(key, "Active") == TRUE) {
1133                 dbus_message_iter_get_basic(&value, &modem->active);
1134
1135                 DBG("%s Active %d", modem->path, modem->active);
1136
1137                 if (modem->active == TRUE)
1138                         set_connected(modem);
1139                 else
1140                         set_disconnected(modem);
1141         }
1142
1143         return TRUE;
1144 }
1145
1146 static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data)
1147 {
1148         struct modem_data *modem = user_data;
1149         DBusMessageIter array, dict, entry, value;
1150         DBusMessage *reply;
1151         DBusError error;
1152
1153         DBG("%s", modem->path);
1154
1155         modem->call_get_contexts = NULL;
1156
1157         reply = dbus_pending_call_steal_reply(call);
1158
1159         dbus_error_init(&error);
1160
1161         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1162                 connman_error("%s", error.message);
1163                 dbus_error_free(&error);
1164                 goto done;
1165         }
1166
1167         if (dbus_message_iter_init(reply, &array) == FALSE)
1168                 goto done;
1169
1170         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
1171                 goto done;
1172
1173         dbus_message_iter_recurse(&array, &dict);
1174
1175         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
1176                 const char *context_path;
1177
1178                 dbus_message_iter_recurse(&dict, &entry);
1179                 dbus_message_iter_get_basic(&entry, &context_path);
1180
1181                 dbus_message_iter_next(&entry);
1182                 dbus_message_iter_recurse(&entry, &value);
1183
1184                 if (add_cm_context(modem, context_path, &value) == 0)
1185                         break;
1186
1187                 dbus_message_iter_next(&dict);
1188         }
1189
1190 done:
1191         dbus_message_unref(reply);
1192
1193         dbus_pending_call_unref(call);
1194 }
1195
1196 static int cm_get_contexts(struct modem_data *modem)
1197 {
1198         DBusMessage *message;
1199
1200         DBG("%s", modem->path);
1201
1202         if (modem->call_get_contexts != NULL)
1203                 return -EBUSY;
1204
1205         message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
1206                                         OFONO_CM_INTERFACE, GET_CONTEXTS);
1207         if (message == NULL)
1208                 return -ENOMEM;
1209
1210         if (dbus_connection_send_with_reply(connection, message,
1211                         &modem->call_get_contexts, TIMEOUT) == FALSE) {
1212                 connman_error("Failed to call GetContexts()");
1213                 dbus_message_unref(message);
1214                 return -EINVAL;
1215         }
1216
1217         if (modem->call_get_contexts == NULL) {
1218                 connman_error("D-Bus connection not available");
1219                 dbus_message_unref(message);
1220                 return -EINVAL;
1221         }
1222
1223         dbus_pending_call_set_notify(modem->call_get_contexts,
1224                                         cm_get_contexts_reply,
1225                                         modem, NULL);
1226
1227         dbus_message_unref(message);
1228
1229         return -EINPROGRESS;
1230 }
1231
1232 static gboolean cm_context_added(DBusConnection *connection,
1233                                         DBusMessage *message,
1234                                         void *user_data)
1235 {
1236         const char *path = dbus_message_get_path(message);
1237         char *context_path;
1238         struct modem_data *modem;
1239         DBusMessageIter iter, properties;
1240
1241         DBG("%s", path);
1242
1243         modem = g_hash_table_lookup(modem_hash, context_path);
1244         if (modem == NULL)
1245                 return TRUE;
1246
1247         if (dbus_message_iter_init(message, &iter) == FALSE)
1248                 return TRUE;
1249
1250         dbus_message_iter_get_basic(&iter, &context_path);
1251
1252         dbus_message_iter_next(&iter);
1253         dbus_message_iter_recurse(&iter, &properties);
1254
1255         if (add_cm_context(modem, context_path, &properties) != 0)
1256                 return TRUE;
1257
1258         return TRUE;
1259 }
1260
1261 static gboolean cm_context_removed(DBusConnection *connection,
1262                                         DBusMessage *message,
1263                                         void *user_data)
1264 {
1265         const char *path = dbus_message_get_path(message);
1266         const char *context_path;
1267         struct modem_data *modem;
1268         DBusMessageIter iter;
1269
1270         DBG("context path %s", path);
1271
1272         if (dbus_message_iter_init(message, &iter) == FALSE)
1273                 return TRUE;
1274
1275         dbus_message_iter_get_basic(&iter, &context_path);
1276
1277         modem = g_hash_table_lookup(context_hash, context_path);
1278         if (modem == NULL)
1279                 return TRUE;
1280
1281         remove_cm_context(modem, context_path);
1282
1283         return TRUE;
1284 }
1285
1286 static void netreg_update_name(struct modem_data *modem,
1287                                 DBusMessageIter* value)
1288 {
1289         char *name;
1290
1291         dbus_message_iter_get_basic(value, &name);
1292
1293         DBG("%s Name %s", modem->path, name);
1294
1295         g_free(modem->name);
1296         modem->name = g_strdup(name);
1297
1298         if (modem->network == NULL)
1299                 return;
1300
1301         connman_network_set_name(modem->network, modem->name);
1302         connman_network_update(modem->network);
1303 }
1304
1305 static void netreg_update_strength(struct modem_data *modem,
1306                                         DBusMessageIter *value)
1307 {
1308         dbus_message_iter_get_basic(value, &modem->strength);
1309
1310         DBG("%s Strength %d", modem->path, modem->strength);
1311
1312         if (modem->network == NULL)
1313                 return;
1314
1315         connman_network_set_strength(modem->network, modem->strength);
1316         connman_network_update(modem->network);
1317 }
1318
1319 static void netreg_update_roaming(struct modem_data *modem,
1320                                         DBusMessageIter *value)
1321 {
1322         char *status;
1323         connman_bool_t roaming;
1324
1325         dbus_message_iter_get_basic(value, &status);
1326
1327         if (g_str_equal(status, "roaming") == TRUE)
1328                 roaming = TRUE;
1329         else
1330                 roaming = FALSE;
1331
1332         if (roaming == modem->roaming)
1333                 return;
1334
1335         modem->roaming = roaming;
1336
1337         if (modem->network == NULL)
1338                 return;
1339
1340         connman_network_set_bool(modem->network,
1341                                 "Roaming", modem->roaming);
1342         connman_network_update(modem->network);
1343 }
1344
1345 static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
1346                                 void *user_data)
1347 {
1348         const char *path = dbus_message_get_path(message);
1349         struct modem_data *modem;
1350         DBusMessageIter iter, value;
1351         const char *key;
1352
1353         modem = g_hash_table_lookup(modem_hash, path);
1354         if (modem == NULL)
1355                 return TRUE;
1356
1357         if (modem->ignore == TRUE)
1358                 return TRUE;
1359
1360         if (dbus_message_iter_init(message, &iter) == FALSE)
1361                 return TRUE;
1362
1363         dbus_message_iter_get_basic(&iter, &key);
1364
1365         dbus_message_iter_next(&iter);
1366         dbus_message_iter_recurse(&iter, &value);
1367
1368         if (g_str_equal(key, "Name") == TRUE)
1369                 netreg_update_name(modem, &value);
1370         else if (g_str_equal(key, "Strength") == TRUE)
1371                 netreg_update_strength(modem, &value);
1372         else if (g_str_equal(key, "Status") == TRUE)
1373                 netreg_update_roaming(modem, &value);
1374
1375         return TRUE;
1376 }
1377
1378 static void netreg_properties_reply(struct modem_data *modem,
1379                                         DBusMessageIter *dict)
1380 {
1381         DBG("%s", modem->path);
1382
1383         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
1384                 DBusMessageIter entry, value;
1385                 const char *key;
1386
1387                 dbus_message_iter_recurse(dict, &entry);
1388                 dbus_message_iter_get_basic(&entry, &key);
1389
1390                 dbus_message_iter_next(&entry);
1391                 dbus_message_iter_recurse(&entry, &value);
1392
1393                 if (g_str_equal(key, "Name") == TRUE)
1394                         netreg_update_name(modem, &value);
1395                 else if (g_str_equal(key, "Strength") == TRUE)
1396                         netreg_update_strength(modem, &value);
1397                 else if (g_str_equal(key, "Status") == TRUE)
1398                         netreg_update_roaming(modem, &value);
1399
1400                 dbus_message_iter_next(dict);
1401         }
1402
1403         if (modem->context == NULL) {
1404                 /*
1405                  * netgreg_get_properties() was issued after we got
1406                  * cm_get_contexts_reply() where we create the
1407                  * context. Though before we got the
1408                  * netreg_properties_reply the context was removed
1409                  * again. Therefore we have to skip the network
1410                  * creation.
1411                  */
1412                 return;
1413         }
1414
1415         add_network(modem);
1416
1417         if (modem->active == TRUE)
1418                 set_connected(modem);
1419 }
1420
1421 static int netreg_get_properties(struct modem_data *modem)
1422 {
1423         return get_properties(modem->path, OFONO_NETREG_INTERFACE,
1424                         netreg_properties_reply, modem);
1425 }
1426
1427 static int cdma_netreg_get_properties(struct modem_data *modem)
1428 {
1429         return -EINVAL;
1430 }
1431
1432 static void cm_update_attached(struct modem_data *modem,
1433                                 DBusMessageIter *value)
1434 {
1435         dbus_message_iter_get_basic(value, &modem->attached);
1436
1437         DBG("%s Attached %d", modem->path, modem->attached);
1438
1439         if (modem->attached == FALSE)
1440                 return;
1441
1442         if (has_interface(modem->interfaces,
1443                                 OFONO_API_NETREG) == FALSE) {
1444                 return;
1445         }
1446
1447         netreg_get_properties(modem);
1448 }
1449
1450 static void cm_update_powered(struct modem_data *modem,
1451                                 DBusMessageIter *value)
1452 {
1453         dbus_message_iter_get_basic(value, &modem->cm_powered);
1454
1455         DBG("%s ConnnectionManager Powered %d", modem->path,
1456                 modem->cm_powered);
1457
1458         if (modem->cm_powered == TRUE)
1459                 return;
1460
1461         cm_set_powered(modem);
1462 }
1463
1464 static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
1465                                 void *user_data)
1466 {
1467         const char *path = dbus_message_get_path(message);
1468         struct modem_data *modem;
1469         DBusMessageIter iter, value;
1470         const char *key;
1471
1472         modem = g_hash_table_lookup(modem_hash, path);
1473         if (modem == NULL)
1474                 return TRUE;
1475
1476         if (modem->ignore == TRUE)
1477                 return TRUE;
1478
1479         if (dbus_message_iter_init(message, &iter) == FALSE)
1480                 return TRUE;
1481
1482         dbus_message_iter_get_basic(&iter, &key);
1483
1484         dbus_message_iter_next(&iter);
1485         dbus_message_iter_recurse(&iter, &value);
1486
1487         if (g_str_equal(key, "Attached") == TRUE)
1488                 cm_update_attached(modem, &value);
1489         else if (g_str_equal(key, "Powered") == TRUE)
1490                 cm_update_powered(modem, &value);
1491
1492         return TRUE;
1493 }
1494
1495 static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict)
1496 {
1497         DBG("%s", modem->path);
1498
1499         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
1500                 DBusMessageIter entry, value;
1501                 const char *key;
1502
1503                 dbus_message_iter_recurse(dict, &entry);
1504                 dbus_message_iter_get_basic(&entry, &key);
1505
1506                 dbus_message_iter_next(&entry);
1507                 dbus_message_iter_recurse(&entry, &value);
1508
1509                 if (g_str_equal(key, "Attached") == TRUE)
1510                         cm_update_attached(modem, &value);
1511                 else if (g_str_equal(key, "Powered") == TRUE)
1512                         cm_update_powered(modem, &value);
1513
1514                 dbus_message_iter_next(dict);
1515         }
1516 }
1517
1518 static int cm_get_properties(struct modem_data *modem)
1519 {
1520         return get_properties(modem->path, OFONO_CM_INTERFACE,
1521                                 cm_properties_reply, modem);
1522 }
1523
1524 static int cdma_cm_get_properties(struct modem_data *modem)
1525 {
1526         return -EINVAL;
1527 }
1528
1529 static int connection_manager_init(struct modem_data *modem)
1530 {
1531         if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
1532                 if (ready_to_create_device(modem) == TRUE)
1533                         create_device(modem);
1534
1535                 if (modem->device != NULL) {
1536                         cm_get_properties(modem);
1537                         cm_get_contexts(modem);
1538                 }
1539
1540                 return 0;
1541         }
1542
1543         if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE) {
1544                 if (ready_to_create_device(modem) == TRUE)
1545                         create_device(modem);
1546
1547                 if (modem->device != NULL)
1548                         cdma_cm_get_properties(modem);
1549
1550                 return 0;
1551         }
1552
1553         return 0;
1554 }
1555
1556 static void update_sim_imsi(struct modem_data *modem,
1557                                 const char *imsi)
1558 {
1559         DBG("%s imsi %s", modem->path, imsi);
1560
1561         if (g_strcmp0(modem->imsi, imsi) == 0)
1562                 return;
1563
1564         g_free(modem->imsi);
1565         modem->imsi = g_strdup(imsi);
1566 }
1567
1568 static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
1569                                 void *user_data)
1570 {
1571         const char *path = dbus_message_get_path(message);
1572         struct modem_data *modem;
1573         DBusMessageIter iter, value;
1574         const char *key;
1575
1576         modem = g_hash_table_lookup(modem_hash, path);
1577         if (modem == NULL)
1578                 return TRUE;
1579
1580         if (modem->ignore == TRUE)
1581                 return TRUE;
1582
1583         if (dbus_message_iter_init(message, &iter) == FALSE)
1584                 return TRUE;
1585
1586         dbus_message_iter_get_basic(&iter, &key);
1587
1588         dbus_message_iter_next(&iter);
1589         dbus_message_iter_recurse(&iter, &value);
1590
1591         if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
1592                 char *imsi;
1593
1594                 dbus_message_iter_get_basic(&value, &imsi);
1595
1596                 update_sim_imsi(modem, imsi);
1597
1598                 if (modem->online == FALSE) {
1599                         modem_set_online(modem);
1600                 } else if (has_interface(modem->interfaces,
1601                                                 OFONO_API_CM) == TRUE) {
1602                         if (ready_to_create_device(modem) == TRUE)
1603                                 create_device(modem);
1604                 } else if (has_interface(modem->interfaces,
1605                                                 OFONO_API_CDMA_CM) == TRUE) {
1606                         if (ready_to_create_device(modem) == TRUE)
1607                                 create_device(modem);
1608                 }
1609         }
1610
1611         return TRUE;
1612 }
1613
1614 static void sim_properties_reply(struct modem_data *modem,
1615                                         DBusMessageIter *dict)
1616 {
1617         DBG("%s", modem->path);
1618
1619         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
1620                 DBusMessageIter entry, value;
1621                 const char *key;
1622
1623                 dbus_message_iter_recurse(dict, &entry);
1624                 dbus_message_iter_get_basic(&entry, &key);
1625
1626                 dbus_message_iter_next(&entry);
1627                 dbus_message_iter_recurse(&entry, &value);
1628
1629                 if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
1630                         char *imsi;
1631
1632                         dbus_message_iter_get_basic(&value, &imsi);
1633
1634                         update_sim_imsi(modem, imsi);
1635
1636                         if (modem->online == FALSE) {
1637                                 modem_set_online(modem);
1638                                 break;
1639                         }
1640
1641                         connection_manager_init(modem);
1642
1643                         return;
1644                 }
1645
1646                 dbus_message_iter_next(dict);
1647         }
1648 }
1649
1650 static int sim_get_properties(struct modem_data *modem)
1651 {
1652         return get_properties(modem->path, OFONO_SIM_INTERFACE,
1653                         sim_properties_reply, modem);
1654 }
1655
1656 static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
1657                                 void *user_data)
1658 {
1659         const char *path = dbus_message_get_path(message);
1660         struct modem_data *modem;
1661         DBusMessageIter iter, value;
1662         const char *key;
1663
1664         modem = g_hash_table_lookup(modem_hash, path);
1665         if (modem == NULL)
1666                 return TRUE;
1667
1668         if (modem->ignore == TRUE)
1669                 return TRUE;
1670
1671         if (dbus_message_iter_init(message, &iter) == FALSE)
1672                 return TRUE;
1673
1674         dbus_message_iter_get_basic(&iter, &key);
1675
1676         dbus_message_iter_next(&iter);
1677         dbus_message_iter_recurse(&iter, &value);
1678
1679         if (g_str_equal(key, "Powered") == TRUE) {
1680                 dbus_message_iter_get_basic(&value, &modem->powered);
1681
1682                 DBG("%s Powered %d", modem->path, modem->powered);
1683
1684                 if (modem->powered == FALSE)
1685                         modem_set_powered(modem);
1686         } else if (g_str_equal(key, "Online") == TRUE) {
1687                 dbus_message_iter_get_basic(&value, &modem->online);
1688
1689                 DBG("%s Online %d", modem->path, modem->online);
1690
1691                 if (modem->online == FALSE)
1692                         return TRUE;
1693
1694                 connection_manager_init(modem);
1695         } else if (g_str_equal(key, "Interfaces") == TRUE) {
1696                 modem->interfaces = extract_interfaces(&value);
1697
1698                 DBG("%s Interfaces 0x%02x", modem->path,
1699                         modem->interfaces);
1700
1701                 if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
1702                         if (modem->imsi == NULL &&
1703                                         modem->set_powered == FALSE) {
1704                                 /*
1705                                  * Only use do GetProperties() when
1706                                  * device has not been powered up.
1707                                  */
1708                                 sim_get_properties(modem);
1709                                 return TRUE;
1710                         }
1711                 }
1712
1713                 if (connection_manager_init(modem) == FALSE) {
1714                         if (modem->context != NULL) {
1715                                 remove_cm_context(modem,
1716                                                 modem->context->path);
1717                         }
1718
1719                         if (modem->device != NULL)
1720                                 destroy_device(modem);
1721
1722                         return TRUE;
1723                 }
1724
1725                 if (has_interface(modem->interfaces, OFONO_API_NETREG) == TRUE) {
1726                         if (modem->attached == TRUE)
1727                                 netreg_get_properties(modem);
1728                 } else if (has_interface(modem->interfaces,
1729                                 OFONO_API_CDMA_NETREG) == TRUE)
1730                         cdma_netreg_get_properties(modem);
1731         } else if (g_str_equal(key, "Serial") == TRUE) {
1732                 char *serial;
1733
1734                 dbus_message_iter_get_basic(&value, &serial);
1735
1736                 g_free(modem->serial);
1737                 modem->serial = g_strdup(serial);
1738
1739                 DBG("%s Serial %s", modem->path, modem->serial);
1740
1741                 connection_manager_init(modem);
1742         }
1743
1744         return TRUE;
1745 }
1746
1747 static void add_modem(const char *path, DBusMessageIter *prop)
1748 {
1749         struct modem_data *modem;
1750
1751         DBG("%s", path);
1752
1753         modem = g_hash_table_lookup(modem_hash, path);
1754         if (modem != NULL) {
1755                 /*
1756                  * When oFono powers up we ask for the modems and oFono is
1757                  * reporting with modem_added signal the modems. Only
1758                  * handle them once.
1759                  */
1760                 return;
1761         }
1762
1763         modem = g_try_new0(struct modem_data, 1);
1764         if (modem == NULL)
1765                 return;
1766
1767         modem->path = g_strdup(path);
1768
1769         g_hash_table_insert(modem_hash, g_strdup(path), modem);
1770
1771         while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
1772                 DBusMessageIter entry, value;
1773                 const char *key;
1774
1775                 dbus_message_iter_recurse(prop, &entry);
1776                 dbus_message_iter_get_basic(&entry, &key);
1777
1778                 dbus_message_iter_next(&entry);
1779                 dbus_message_iter_recurse(&entry, &value);
1780
1781                 if (g_str_equal(key, "Powered") == TRUE) {
1782                         dbus_message_iter_get_basic(&value, &modem->powered);
1783
1784                         DBG("%s Powered %d", modem->path, modem->powered);
1785                 } else if (g_str_equal(key, "Online") == TRUE) {
1786                         dbus_message_iter_get_basic(&value, &modem->online);
1787
1788                         DBG("%s Online %d", modem->path, modem->online);
1789                 } else if (g_str_equal(key, "Interfaces") == TRUE) {
1790                         modem->interfaces = extract_interfaces(&value);
1791
1792                         DBG("%s Interfaces 0x%02x", modem->path,
1793                                 modem->interfaces);
1794                 } else if (g_str_equal(key, "Serial") == TRUE) {
1795                         char *serial;
1796
1797                         dbus_message_iter_get_basic(&value, &serial);
1798                         modem->serial = g_strdup(serial);
1799
1800                         DBG("%s Serial %s", modem->path, modem->serial);
1801                 } else if (g_str_equal(key, "Type") == TRUE) {
1802                         char *type;
1803
1804                         dbus_message_iter_get_basic(&value, &type);
1805
1806                         DBG("%s Type %s", modem->path, type);
1807                         if (g_strcmp0(type, "hardware") != 0) {
1808                                 DBG("%s Ignore this modem", modem->path);
1809                                 modem->ignore = TRUE;
1810                         }
1811                 }
1812
1813                 dbus_message_iter_next(prop);
1814         }
1815
1816         if (modem->ignore == TRUE)
1817                 return;
1818
1819         if (modem->powered == FALSE) {
1820                 modem_set_powered(modem);
1821         } else if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
1822                 sim_get_properties(modem);
1823         } else
1824                 connection_manager_init(modem);
1825 }
1826
1827 static void modem_power_down(gpointer key, gpointer value, gpointer user_data)
1828 {
1829         struct modem_data *modem = value;
1830
1831         DBG("%s", modem->path);
1832
1833         if (modem->ignore ==  TRUE)
1834                 return;
1835
1836         modem_set_unpowered(modem);
1837 }
1838
1839 static void remove_modem(gpointer data)
1840 {
1841         struct modem_data *modem = data;
1842
1843         DBG("%s", modem->path);
1844
1845         if (modem->call_set_property != NULL)
1846                 dbus_pending_call_cancel(modem->call_set_property);
1847
1848         if (modem->call_get_properties != NULL)
1849                 dbus_pending_call_cancel(modem->call_get_properties);
1850
1851         if (modem->call_get_contexts != NULL)
1852                 dbus_pending_call_cancel(modem->call_get_contexts);
1853
1854         if (modem->device != NULL)
1855                 destroy_device(modem);
1856
1857         if (modem->context != NULL)
1858                 remove_cm_context(modem, modem->context->path);
1859
1860         g_free(modem->serial);
1861         g_free(modem->name);
1862         g_free(modem->imsi);
1863         g_free(modem->path);
1864
1865         g_free(modem);
1866 }
1867
1868 static gboolean modem_added(DBusConnection *connection,
1869                                 DBusMessage *message, void *user_data)
1870 {
1871         DBusMessageIter iter, properties;
1872         const char *path;
1873
1874         DBG("");
1875
1876         if (dbus_message_iter_init(message, &iter) == FALSE)
1877                 return TRUE;
1878
1879         dbus_message_iter_get_basic(&iter, &path);
1880
1881         dbus_message_iter_next(&iter);
1882         dbus_message_iter_recurse(&iter, &properties);
1883
1884         add_modem(path, &properties);
1885
1886         return TRUE;
1887 }
1888
1889 static gboolean modem_removed(DBusConnection *connection,
1890                                 DBusMessage *message, void *user_data)
1891 {
1892         DBusMessageIter iter;
1893         const char *path;
1894
1895         DBG("");
1896
1897         if (dbus_message_iter_init(message, &iter) == FALSE)
1898                 return TRUE;
1899
1900         dbus_message_iter_get_basic(&iter, &path);
1901
1902         g_hash_table_remove(modem_hash, path);
1903
1904         return TRUE;
1905 }
1906
1907 static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
1908 {
1909         DBusMessage *reply;
1910         DBusError error;
1911         DBusMessageIter array, dict;
1912
1913         DBG("");
1914
1915         reply = dbus_pending_call_steal_reply(call);
1916
1917         dbus_error_init(&error);
1918
1919         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1920                 connman_error("%s", error.message);
1921                 dbus_error_free(&error);
1922                 goto done;
1923         }
1924
1925         if (dbus_message_iter_init(reply, &array) == FALSE)
1926                 goto done;
1927
1928         dbus_message_iter_recurse(&array, &dict);
1929
1930         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
1931                 DBusMessageIter value, properties;
1932                 const char *path;
1933
1934                 dbus_message_iter_recurse(&dict, &value);
1935                 dbus_message_iter_get_basic(&value, &path);
1936
1937                 dbus_message_iter_next(&value);
1938                 dbus_message_iter_recurse(&value, &properties);
1939
1940                 add_modem(path, &properties);
1941
1942                 dbus_message_iter_next(&dict);
1943         }
1944
1945 done:
1946         dbus_message_unref(reply);
1947
1948         dbus_pending_call_unref(call);
1949 }
1950
1951 static int manager_get_modems(void)
1952 {
1953         DBusMessage *message;
1954         DBusPendingCall *call;
1955
1956         DBG("");
1957
1958         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
1959                                         OFONO_MANAGER_INTERFACE, GET_MODEMS);
1960         if (message == NULL)
1961                 return -ENOMEM;
1962
1963         if (dbus_connection_send_with_reply(connection, message,
1964                                                &call, TIMEOUT) == FALSE) {
1965                 connman_error("Failed to call GetModems()");
1966                 dbus_message_unref(message);
1967                 return -EINVAL;
1968         }
1969
1970         if (call == NULL) {
1971                 connman_error("D-Bus connection not available");
1972                 dbus_message_unref(message);
1973                 return -EINVAL;
1974         }
1975
1976         dbus_pending_call_set_notify(call, manager_get_modems_reply,
1977                                         NULL, NULL);
1978
1979         dbus_message_unref(message);
1980
1981         return -EINPROGRESS;
1982 }
1983
1984 static void ofono_connect(DBusConnection *conn, void *user_data)
1985 {
1986         DBG("");
1987
1988         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1989                                                 g_free, remove_modem);
1990         if (modem_hash == NULL)
1991                 return;
1992
1993         context_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1994                                                 g_free, NULL);
1995         if (context_hash == NULL) {
1996                 g_hash_table_destroy(modem_hash);
1997                 return;
1998         }
1999
2000         manager_get_modems();
2001 }
2002
2003 static void ofono_disconnect(DBusConnection *conn, void *user_data)
2004 {
2005         DBG("");
2006
2007         if (modem_hash == NULL || context_hash == NULL)
2008                 return;
2009
2010         g_hash_table_destroy(modem_hash);
2011         modem_hash = NULL;
2012
2013         g_hash_table_destroy(context_hash);
2014         context_hash = NULL;
2015 }
2016
2017 static int network_probe(struct connman_network *network)
2018 {
2019         struct modem_data *modem = connman_network_get_data(network);
2020
2021         DBG("%s network %p", modem->path, network);
2022
2023         return 0;
2024 }
2025
2026 static void network_remove(struct connman_network *network)
2027 {
2028         struct modem_data *modem = connman_network_get_data(network);
2029
2030         DBG("%s network %p", modem->path, network);
2031 }
2032
2033 static int network_connect(struct connman_network *network)
2034 {
2035         struct modem_data *modem = connman_network_get_data(network);
2036
2037         DBG("%s network %p", modem->path, network);
2038
2039         if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
2040                 return context_set_active(modem);
2041         else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
2042                 return cdma_cm_set_powered(modem);
2043
2044         connman_error("Connection manager interface not available");
2045
2046         return -ENOSYS;
2047 }
2048
2049 static int network_disconnect(struct connman_network *network)
2050 {
2051         struct modem_data *modem = connman_network_get_data(network);
2052
2053         DBG("%s network %p", modem->path, network);
2054
2055         if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
2056                 return context_set_inactive(modem);
2057         else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
2058                 return cdma_cm_shutdown(modem);
2059
2060         connman_error("Connection manager interface not available");
2061
2062         return -ENOSYS;
2063 }
2064
2065 static struct connman_network_driver network_driver = {
2066         .name           = "network",
2067         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
2068         .probe          = network_probe,
2069         .remove         = network_remove,
2070         .connect        = network_connect,
2071         .disconnect     = network_disconnect,
2072 };
2073
2074 static int modem_probe(struct connman_device *device)
2075 {
2076         struct modem_data *modem = connman_device_get_data(device);
2077
2078         DBG("%s device %p", modem->path, device);
2079
2080         return 0;
2081 }
2082
2083 static void modem_remove(struct connman_device *device)
2084 {
2085         struct modem_data *modem = connman_device_get_data(device);
2086
2087         DBG("%s device %p", modem->path, device);
2088 }
2089
2090 static int modem_enable(struct connman_device *device)
2091 {
2092         struct modem_data *modem = connman_device_get_data(device);
2093
2094         DBG("%s device %p", modem->path, device);
2095
2096         return 0;
2097 }
2098
2099 static int modem_disable(struct connman_device *device)
2100 {
2101         struct modem_data *modem = connman_device_get_data(device);
2102
2103         DBG("%s device %p", modem->path, device);
2104
2105         return 0;
2106 }
2107
2108 static struct connman_device_driver modem_driver = {
2109         .name           = "modem",
2110         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
2111         .probe          = modem_probe,
2112         .remove         = modem_remove,
2113         .enable         = modem_enable,
2114         .disable        = modem_disable,
2115 };
2116
2117 static guint watch;
2118 static guint modem_added_watch;
2119 static guint modem_removed_watch;
2120 static guint modem_watch;
2121 static guint cm_watch;
2122 static guint sim_watch;
2123 static guint context_added_watch;
2124 static guint context_removed_watch;
2125 static guint netreg_watch;
2126 static guint context_watch;
2127
2128 static int ofono_init(void)
2129 {
2130         int err;
2131
2132         DBG("");
2133
2134         connection = connman_dbus_get_connection();
2135         if (connection == NULL)
2136                 return -EIO;
2137
2138         watch = g_dbus_add_service_watch(connection,
2139                                         OFONO_SERVICE, ofono_connect,
2140                                         ofono_disconnect, NULL, NULL);
2141
2142         modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2143                                                 OFONO_MANAGER_INTERFACE,
2144                                                 MODEM_ADDED,
2145                                                 modem_added,
2146                                                 NULL, NULL);
2147
2148         modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2149                                                 OFONO_MANAGER_INTERFACE,
2150                                                 MODEM_REMOVED,
2151                                                 modem_removed,
2152                                                 NULL, NULL);
2153
2154         modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2155                                                 OFONO_MODEM_INTERFACE,
2156                                                 PROPERTY_CHANGED,
2157                                                 modem_changed,
2158                                                 NULL, NULL);
2159
2160         cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2161                                                 OFONO_CM_INTERFACE,
2162                                                 PROPERTY_CHANGED,
2163                                                 cm_changed,
2164                                                 NULL, NULL);
2165
2166         sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2167                                                 OFONO_SIM_INTERFACE,
2168                                                 PROPERTY_CHANGED,
2169                                                 sim_changed,
2170                                                 NULL, NULL);
2171
2172         context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2173                                                 OFONO_CM_INTERFACE,
2174                                                 CONTEXT_ADDED,
2175                                                 cm_context_added,
2176                                                 NULL, NULL);
2177
2178         context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2179                                                 OFONO_CM_INTERFACE,
2180                                                 CONTEXT_REMOVED,
2181                                                 cm_context_removed,
2182                                                 NULL, NULL);
2183
2184         context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2185                                                 OFONO_CONTEXT_INTERFACE,
2186                                                 PROPERTY_CHANGED,
2187                                                 context_changed,
2188                                                 NULL, NULL);
2189
2190         netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2191                                                 OFONO_NETREG_INTERFACE,
2192                                                 PROPERTY_CHANGED,
2193                                                 netreg_changed,
2194                                                 NULL, NULL);
2195
2196
2197         if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
2198                         modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
2199                         context_added_watch == 0 ||
2200                         context_removed_watch == 0 ||
2201                         context_watch == 0 || netreg_watch == 0) {
2202                 err = -EIO;
2203                 goto remove;
2204         }
2205
2206         err = connman_network_driver_register(&network_driver);
2207         if (err < 0)
2208                 goto remove;
2209
2210         err = connman_device_driver_register(&modem_driver);
2211         if (err < 0) {
2212                 connman_network_driver_unregister(&network_driver);
2213                 goto remove;
2214         }
2215
2216         return 0;
2217
2218 remove:
2219         g_dbus_remove_watch(connection, netreg_watch);
2220         g_dbus_remove_watch(connection, context_watch);
2221         g_dbus_remove_watch(connection, context_removed_watch);
2222         g_dbus_remove_watch(connection, context_added_watch);
2223         g_dbus_remove_watch(connection, sim_watch);
2224         g_dbus_remove_watch(connection, cm_watch);
2225         g_dbus_remove_watch(connection, modem_watch);
2226         g_dbus_remove_watch(connection, modem_removed_watch);
2227         g_dbus_remove_watch(connection, modem_added_watch);
2228         g_dbus_remove_watch(connection, watch);
2229         dbus_connection_unref(connection);
2230
2231         return err;
2232 }
2233
2234 static void ofono_exit(void)
2235 {
2236         DBG("");
2237
2238         if (modem_hash != NULL) {
2239                 /*
2240                  * We should propably wait for the SetProperty() reply
2241                  * message, because ...
2242                  */
2243                 g_hash_table_foreach(modem_hash, modem_power_down, NULL);
2244
2245                 /*
2246                  * ... here we will cancel the call.
2247                  */
2248                 g_hash_table_destroy(modem_hash);
2249                 modem_hash = NULL;
2250         }
2251
2252         if (context_hash != NULL) {
2253                 g_hash_table_destroy(context_hash);
2254                 context_hash = NULL;
2255         }
2256
2257         connman_device_driver_unregister(&modem_driver);
2258         connman_network_driver_unregister(&network_driver);
2259
2260         g_dbus_remove_watch(connection, netreg_watch);
2261         g_dbus_remove_watch(connection, context_watch);
2262         g_dbus_remove_watch(connection, context_removed_watch);
2263         g_dbus_remove_watch(connection, context_added_watch);
2264         g_dbus_remove_watch(connection, sim_watch);
2265         g_dbus_remove_watch(connection, cm_watch);
2266         g_dbus_remove_watch(connection, modem_watch);
2267         g_dbus_remove_watch(connection, modem_added_watch);
2268         g_dbus_remove_watch(connection, modem_removed_watch);
2269         g_dbus_remove_watch(connection, watch);
2270
2271         dbus_connection_unref(connection);
2272 }
2273
2274 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
2275                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)