ofono: Factorize Connection Managers intialization
[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 gboolean connection_managers_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                 if (modem->device != NULL) {
1535                         cm_get_properties(modem);
1536                         cm_get_contexts(modem);
1537                 }
1538
1539                 return TRUE;
1540         } else if (has_interface(modem->interfaces,
1541                         OFONO_API_CDMA_CM) == TRUE) {
1542                 if (ready_to_create_device(modem) == TRUE)
1543                         create_device(modem);
1544                 if (modem->device != NULL)
1545                         cdma_cm_get_properties(modem);
1546
1547                 return TRUE;
1548         } else
1549                 return FALSE;
1550 }
1551
1552 static void update_sim_imsi(struct modem_data *modem,
1553                                 const char *imsi)
1554 {
1555         DBG("%s imsi %s", modem->path, imsi);
1556
1557         if (g_strcmp0(modem->imsi, imsi) == 0)
1558                 return;
1559
1560         g_free(modem->imsi);
1561         modem->imsi = g_strdup(imsi);
1562 }
1563
1564 static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
1565                                 void *user_data)
1566 {
1567         const char *path = dbus_message_get_path(message);
1568         struct modem_data *modem;
1569         DBusMessageIter iter, value;
1570         const char *key;
1571
1572         modem = g_hash_table_lookup(modem_hash, path);
1573         if (modem == NULL)
1574                 return TRUE;
1575
1576         if (modem->ignore == TRUE)
1577                 return TRUE;
1578
1579         if (dbus_message_iter_init(message, &iter) == FALSE)
1580                 return TRUE;
1581
1582         dbus_message_iter_get_basic(&iter, &key);
1583
1584         dbus_message_iter_next(&iter);
1585         dbus_message_iter_recurse(&iter, &value);
1586
1587         if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
1588                 char *imsi;
1589
1590                 dbus_message_iter_get_basic(&value, &imsi);
1591
1592                 update_sim_imsi(modem, imsi);
1593
1594                 if (modem->online == FALSE) {
1595                         modem_set_online(modem);
1596                 } else if (has_interface(modem->interfaces,
1597                                                 OFONO_API_CM) == TRUE) {
1598                         if (ready_to_create_device(modem) == TRUE)
1599                                 create_device(modem);
1600                 } else if (has_interface(modem->interfaces,
1601                                                 OFONO_API_CDMA_CM) == TRUE) {
1602                         if (ready_to_create_device(modem) == TRUE)
1603                                 create_device(modem);
1604                 }
1605         }
1606
1607         return TRUE;
1608 }
1609
1610 static void sim_properties_reply(struct modem_data *modem,
1611                                         DBusMessageIter *dict)
1612 {
1613         DBG("%s", modem->path);
1614
1615         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
1616                 DBusMessageIter entry, value;
1617                 const char *key;
1618
1619                 dbus_message_iter_recurse(dict, &entry);
1620                 dbus_message_iter_get_basic(&entry, &key);
1621
1622                 dbus_message_iter_next(&entry);
1623                 dbus_message_iter_recurse(&entry, &value);
1624
1625                 if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
1626                         char *imsi;
1627
1628                         dbus_message_iter_get_basic(&value, &imsi);
1629
1630                         update_sim_imsi(modem, imsi);
1631
1632                         if (modem->online == FALSE) {
1633                                 modem_set_online(modem);
1634                                 break;
1635                         }
1636
1637                         connection_managers_init(modem);
1638
1639                         return;
1640                 }
1641
1642                 dbus_message_iter_next(dict);
1643         }
1644 }
1645
1646 static int sim_get_properties(struct modem_data *modem)
1647 {
1648         return get_properties(modem->path, OFONO_SIM_INTERFACE,
1649                         sim_properties_reply, modem);
1650 }
1651
1652 static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
1653                                 void *user_data)
1654 {
1655         const char *path = dbus_message_get_path(message);
1656         struct modem_data *modem;
1657         DBusMessageIter iter, value;
1658         const char *key;
1659
1660         modem = g_hash_table_lookup(modem_hash, path);
1661         if (modem == NULL)
1662                 return TRUE;
1663
1664         if (modem->ignore == TRUE)
1665                 return TRUE;
1666
1667         if (dbus_message_iter_init(message, &iter) == FALSE)
1668                 return TRUE;
1669
1670         dbus_message_iter_get_basic(&iter, &key);
1671
1672         dbus_message_iter_next(&iter);
1673         dbus_message_iter_recurse(&iter, &value);
1674
1675         if (g_str_equal(key, "Powered") == TRUE) {
1676                 dbus_message_iter_get_basic(&value, &modem->powered);
1677
1678                 DBG("%s Powered %d", modem->path, modem->powered);
1679
1680                 if (modem->powered == FALSE)
1681                         modem_set_powered(modem);
1682         } else if (g_str_equal(key, "Online") == TRUE) {
1683                 dbus_message_iter_get_basic(&value, &modem->online);
1684
1685                 DBG("%s Online %d", modem->path, modem->online);
1686
1687                 if (modem->online == FALSE)
1688                         return TRUE;
1689
1690                 connection_managers_init(modem);
1691         } else if (g_str_equal(key, "Interfaces") == TRUE) {
1692                 modem->interfaces = extract_interfaces(&value);
1693
1694                 DBG("%s Interfaces 0x%02x", modem->path,
1695                         modem->interfaces);
1696
1697                 if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
1698                         if (modem->imsi == NULL &&
1699                                         modem->set_powered == FALSE) {
1700                                 /*
1701                                  * Only use do GetProperties() when
1702                                  * device has not been powered up.
1703                                  */
1704                                 sim_get_properties(modem);
1705                                 return TRUE;
1706                         }
1707                 }
1708
1709                 if (connection_managers_init(modem) == FALSE) {
1710                         if (modem->context != NULL) {
1711                                 remove_cm_context(modem,
1712                                                 modem->context->path);
1713                         }
1714
1715                         if (modem->device != NULL)
1716                                 destroy_device(modem);
1717
1718                         return TRUE;
1719                 }
1720
1721                 if (has_interface(modem->interfaces, OFONO_API_NETREG) == TRUE) {
1722                         if (modem->attached == TRUE)
1723                                 netreg_get_properties(modem);
1724                 } else if (has_interface(modem->interfaces,
1725                                 OFONO_API_CDMA_NETREG) == TRUE)
1726                         cdma_netreg_get_properties(modem);
1727         } else if (g_str_equal(key, "Serial") == TRUE) {
1728                 char *serial;
1729
1730                 dbus_message_iter_get_basic(&value, &serial);
1731
1732                 g_free(modem->serial);
1733                 modem->serial = g_strdup(serial);
1734
1735                 DBG("%s Serial %s", modem->path, modem->serial);
1736
1737                 connection_managers_init(modem);
1738         }
1739
1740         return TRUE;
1741 }
1742
1743 static void add_modem(const char *path, DBusMessageIter *prop)
1744 {
1745         struct modem_data *modem;
1746
1747         DBG("%s", path);
1748
1749         modem = g_hash_table_lookup(modem_hash, path);
1750         if (modem != NULL) {
1751                 /*
1752                  * When oFono powers up we ask for the modems and oFono is
1753                  * reporting with modem_added signal the modems. Only
1754                  * handle them once.
1755                  */
1756                 return;
1757         }
1758
1759         modem = g_try_new0(struct modem_data, 1);
1760         if (modem == NULL)
1761                 return;
1762
1763         modem->path = g_strdup(path);
1764
1765         g_hash_table_insert(modem_hash, g_strdup(path), modem);
1766
1767         while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
1768                 DBusMessageIter entry, value;
1769                 const char *key;
1770
1771                 dbus_message_iter_recurse(prop, &entry);
1772                 dbus_message_iter_get_basic(&entry, &key);
1773
1774                 dbus_message_iter_next(&entry);
1775                 dbus_message_iter_recurse(&entry, &value);
1776
1777                 if (g_str_equal(key, "Powered") == TRUE) {
1778                         dbus_message_iter_get_basic(&value, &modem->powered);
1779
1780                         DBG("%s Powered %d", modem->path, modem->powered);
1781                 } else if (g_str_equal(key, "Online") == TRUE) {
1782                         dbus_message_iter_get_basic(&value, &modem->online);
1783
1784                         DBG("%s Online %d", modem->path, modem->online);
1785                 } else if (g_str_equal(key, "Interfaces") == TRUE) {
1786                         modem->interfaces = extract_interfaces(&value);
1787
1788                         DBG("%s Interfaces 0x%02x", modem->path,
1789                                 modem->interfaces);
1790                 } else if (g_str_equal(key, "Serial") == TRUE) {
1791                         char *serial;
1792
1793                         dbus_message_iter_get_basic(&value, &serial);
1794                         modem->serial = g_strdup(serial);
1795
1796                         DBG("%s Serial %s", modem->path, modem->serial);
1797                 } else if (g_str_equal(key, "Type") == TRUE) {
1798                         char *type;
1799
1800                         dbus_message_iter_get_basic(&value, &type);
1801
1802                         DBG("%s Type %s", modem->path, type);
1803                         if (g_strcmp0(type, "hardware") != 0) {
1804                                 DBG("%s Ignore this modem", modem->path);
1805                                 modem->ignore = TRUE;
1806                         }
1807                 }
1808
1809                 dbus_message_iter_next(prop);
1810         }
1811
1812         if (modem->ignore == TRUE)
1813                 return;
1814
1815         if (modem->powered == FALSE) {
1816                 modem_set_powered(modem);
1817         } else if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
1818                 sim_get_properties(modem);
1819         } else
1820                 connection_managers_init(modem);
1821 }
1822
1823 static void modem_power_down(gpointer key, gpointer value, gpointer user_data)
1824 {
1825         struct modem_data *modem = value;
1826
1827         DBG("%s", modem->path);
1828
1829         if (modem->ignore ==  TRUE)
1830                 return;
1831
1832         modem_set_unpowered(modem);
1833 }
1834
1835 static void remove_modem(gpointer data)
1836 {
1837         struct modem_data *modem = data;
1838
1839         DBG("%s", modem->path);
1840
1841         if (modem->call_set_property != NULL)
1842                 dbus_pending_call_cancel(modem->call_set_property);
1843
1844         if (modem->call_get_properties != NULL)
1845                 dbus_pending_call_cancel(modem->call_get_properties);
1846
1847         if (modem->call_get_contexts != NULL)
1848                 dbus_pending_call_cancel(modem->call_get_contexts);
1849
1850         if (modem->device != NULL)
1851                 destroy_device(modem);
1852
1853         if (modem->context != NULL)
1854                 remove_cm_context(modem, modem->context->path);
1855
1856         g_free(modem->serial);
1857         g_free(modem->name);
1858         g_free(modem->imsi);
1859         g_free(modem->path);
1860
1861         g_free(modem);
1862 }
1863
1864 static gboolean modem_added(DBusConnection *connection,
1865                                 DBusMessage *message, void *user_data)
1866 {
1867         DBusMessageIter iter, properties;
1868         const char *path;
1869
1870         DBG("");
1871
1872         if (dbus_message_iter_init(message, &iter) == FALSE)
1873                 return TRUE;
1874
1875         dbus_message_iter_get_basic(&iter, &path);
1876
1877         dbus_message_iter_next(&iter);
1878         dbus_message_iter_recurse(&iter, &properties);
1879
1880         add_modem(path, &properties);
1881
1882         return TRUE;
1883 }
1884
1885 static gboolean modem_removed(DBusConnection *connection,
1886                                 DBusMessage *message, void *user_data)
1887 {
1888         DBusMessageIter iter;
1889         const char *path;
1890
1891         DBG("");
1892
1893         if (dbus_message_iter_init(message, &iter) == FALSE)
1894                 return TRUE;
1895
1896         dbus_message_iter_get_basic(&iter, &path);
1897
1898         g_hash_table_remove(modem_hash, path);
1899
1900         return TRUE;
1901 }
1902
1903 static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
1904 {
1905         DBusMessage *reply;
1906         DBusError error;
1907         DBusMessageIter array, dict;
1908
1909         DBG("");
1910
1911         reply = dbus_pending_call_steal_reply(call);
1912
1913         dbus_error_init(&error);
1914
1915         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1916                 connman_error("%s", error.message);
1917                 dbus_error_free(&error);
1918                 goto done;
1919         }
1920
1921         if (dbus_message_iter_init(reply, &array) == FALSE)
1922                 goto done;
1923
1924         dbus_message_iter_recurse(&array, &dict);
1925
1926         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
1927                 DBusMessageIter value, properties;
1928                 const char *path;
1929
1930                 dbus_message_iter_recurse(&dict, &value);
1931                 dbus_message_iter_get_basic(&value, &path);
1932
1933                 dbus_message_iter_next(&value);
1934                 dbus_message_iter_recurse(&value, &properties);
1935
1936                 add_modem(path, &properties);
1937
1938                 dbus_message_iter_next(&dict);
1939         }
1940
1941 done:
1942         dbus_message_unref(reply);
1943
1944         dbus_pending_call_unref(call);
1945 }
1946
1947 static int manager_get_modems(void)
1948 {
1949         DBusMessage *message;
1950         DBusPendingCall *call;
1951
1952         DBG("");
1953
1954         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
1955                                         OFONO_MANAGER_INTERFACE, GET_MODEMS);
1956         if (message == NULL)
1957                 return -ENOMEM;
1958
1959         if (dbus_connection_send_with_reply(connection, message,
1960                                                &call, TIMEOUT) == FALSE) {
1961                 connman_error("Failed to call GetModems()");
1962                 dbus_message_unref(message);
1963                 return -EINVAL;
1964         }
1965
1966         if (call == NULL) {
1967                 connman_error("D-Bus connection not available");
1968                 dbus_message_unref(message);
1969                 return -EINVAL;
1970         }
1971
1972         dbus_pending_call_set_notify(call, manager_get_modems_reply,
1973                                         NULL, NULL);
1974
1975         dbus_message_unref(message);
1976
1977         return -EINPROGRESS;
1978 }
1979
1980 static void ofono_connect(DBusConnection *conn, void *user_data)
1981 {
1982         DBG("");
1983
1984         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1985                                                 g_free, remove_modem);
1986         if (modem_hash == NULL)
1987                 return;
1988
1989         context_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1990                                                 g_free, NULL);
1991         if (context_hash == NULL) {
1992                 g_hash_table_destroy(modem_hash);
1993                 return;
1994         }
1995
1996         manager_get_modems();
1997 }
1998
1999 static void ofono_disconnect(DBusConnection *conn, void *user_data)
2000 {
2001         DBG("");
2002
2003         if (modem_hash == NULL || context_hash == NULL)
2004                 return;
2005
2006         g_hash_table_destroy(modem_hash);
2007         modem_hash = NULL;
2008
2009         g_hash_table_destroy(context_hash);
2010         context_hash = NULL;
2011 }
2012
2013 static int network_probe(struct connman_network *network)
2014 {
2015         struct modem_data *modem = connman_network_get_data(network);
2016
2017         DBG("%s network %p", modem->path, network);
2018
2019         return 0;
2020 }
2021
2022 static void network_remove(struct connman_network *network)
2023 {
2024         struct modem_data *modem = connman_network_get_data(network);
2025
2026         DBG("%s network %p", modem->path, network);
2027 }
2028
2029 static int network_connect(struct connman_network *network)
2030 {
2031         struct modem_data *modem = connman_network_get_data(network);
2032
2033         DBG("%s network %p", modem->path, network);
2034
2035         if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
2036                 return context_set_active(modem);
2037         else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
2038                 return cdma_cm_set_powered(modem);
2039
2040         connman_error("Connection manager interface not available");
2041
2042         return -ENOSYS;
2043 }
2044
2045 static int network_disconnect(struct connman_network *network)
2046 {
2047         struct modem_data *modem = connman_network_get_data(network);
2048
2049         DBG("%s network %p", modem->path, network);
2050
2051         if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
2052                 return context_set_inactive(modem);
2053         else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
2054                 return cdma_cm_shutdown(modem);
2055
2056         connman_error("Connection manager interface not available");
2057
2058         return -ENOSYS;
2059 }
2060
2061 static struct connman_network_driver network_driver = {
2062         .name           = "network",
2063         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
2064         .probe          = network_probe,
2065         .remove         = network_remove,
2066         .connect        = network_connect,
2067         .disconnect     = network_disconnect,
2068 };
2069
2070 static int modem_probe(struct connman_device *device)
2071 {
2072         struct modem_data *modem = connman_device_get_data(device);
2073
2074         DBG("%s device %p", modem->path, device);
2075
2076         return 0;
2077 }
2078
2079 static void modem_remove(struct connman_device *device)
2080 {
2081         struct modem_data *modem = connman_device_get_data(device);
2082
2083         DBG("%s device %p", modem->path, device);
2084 }
2085
2086 static int modem_enable(struct connman_device *device)
2087 {
2088         struct modem_data *modem = connman_device_get_data(device);
2089
2090         DBG("%s device %p", modem->path, device);
2091
2092         return 0;
2093 }
2094
2095 static int modem_disable(struct connman_device *device)
2096 {
2097         struct modem_data *modem = connman_device_get_data(device);
2098
2099         DBG("%s device %p", modem->path, device);
2100
2101         return 0;
2102 }
2103
2104 static struct connman_device_driver modem_driver = {
2105         .name           = "modem",
2106         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
2107         .probe          = modem_probe,
2108         .remove         = modem_remove,
2109         .enable         = modem_enable,
2110         .disable        = modem_disable,
2111 };
2112
2113 static guint watch;
2114 static guint modem_added_watch;
2115 static guint modem_removed_watch;
2116 static guint modem_watch;
2117 static guint cm_watch;
2118 static guint sim_watch;
2119 static guint context_added_watch;
2120 static guint context_removed_watch;
2121 static guint netreg_watch;
2122 static guint context_watch;
2123
2124 static int ofono_init(void)
2125 {
2126         int err;
2127
2128         DBG("");
2129
2130         connection = connman_dbus_get_connection();
2131         if (connection == NULL)
2132                 return -EIO;
2133
2134         watch = g_dbus_add_service_watch(connection,
2135                                         OFONO_SERVICE, ofono_connect,
2136                                         ofono_disconnect, NULL, NULL);
2137
2138         modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2139                                                 OFONO_MANAGER_INTERFACE,
2140                                                 MODEM_ADDED,
2141                                                 modem_added,
2142                                                 NULL, NULL);
2143
2144         modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2145                                                 OFONO_MANAGER_INTERFACE,
2146                                                 MODEM_REMOVED,
2147                                                 modem_removed,
2148                                                 NULL, NULL);
2149
2150         modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2151                                                 OFONO_MODEM_INTERFACE,
2152                                                 PROPERTY_CHANGED,
2153                                                 modem_changed,
2154                                                 NULL, NULL);
2155
2156         cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2157                                                 OFONO_CM_INTERFACE,
2158                                                 PROPERTY_CHANGED,
2159                                                 cm_changed,
2160                                                 NULL, NULL);
2161
2162         sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2163                                                 OFONO_SIM_INTERFACE,
2164                                                 PROPERTY_CHANGED,
2165                                                 sim_changed,
2166                                                 NULL, NULL);
2167
2168         context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2169                                                 OFONO_CM_INTERFACE,
2170                                                 CONTEXT_ADDED,
2171                                                 cm_context_added,
2172                                                 NULL, NULL);
2173
2174         context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2175                                                 OFONO_CM_INTERFACE,
2176                                                 CONTEXT_REMOVED,
2177                                                 cm_context_removed,
2178                                                 NULL, NULL);
2179
2180         context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2181                                                 OFONO_CONTEXT_INTERFACE,
2182                                                 PROPERTY_CHANGED,
2183                                                 context_changed,
2184                                                 NULL, NULL);
2185
2186         netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
2187                                                 OFONO_NETREG_INTERFACE,
2188                                                 PROPERTY_CHANGED,
2189                                                 netreg_changed,
2190                                                 NULL, NULL);
2191
2192
2193         if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
2194                         modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
2195                         context_added_watch == 0 ||
2196                         context_removed_watch == 0 ||
2197                         context_watch == 0 || netreg_watch == 0) {
2198                 err = -EIO;
2199                 goto remove;
2200         }
2201
2202         err = connman_network_driver_register(&network_driver);
2203         if (err < 0)
2204                 goto remove;
2205
2206         err = connman_device_driver_register(&modem_driver);
2207         if (err < 0) {
2208                 connman_network_driver_unregister(&network_driver);
2209                 goto remove;
2210         }
2211
2212         return 0;
2213
2214 remove:
2215         g_dbus_remove_watch(connection, netreg_watch);
2216         g_dbus_remove_watch(connection, context_watch);
2217         g_dbus_remove_watch(connection, context_removed_watch);
2218         g_dbus_remove_watch(connection, context_added_watch);
2219         g_dbus_remove_watch(connection, sim_watch);
2220         g_dbus_remove_watch(connection, cm_watch);
2221         g_dbus_remove_watch(connection, modem_watch);
2222         g_dbus_remove_watch(connection, modem_removed_watch);
2223         g_dbus_remove_watch(connection, modem_added_watch);
2224         g_dbus_remove_watch(connection, watch);
2225         dbus_connection_unref(connection);
2226
2227         return err;
2228 }
2229
2230 static void ofono_exit(void)
2231 {
2232         DBG("");
2233
2234         if (modem_hash != NULL) {
2235                 /*
2236                  * We should propably wait for the SetProperty() reply
2237                  * message, because ...
2238                  */
2239                 g_hash_table_foreach(modem_hash, modem_power_down, NULL);
2240
2241                 /*
2242                  * ... here we will cancel the call.
2243                  */
2244                 g_hash_table_destroy(modem_hash);
2245                 modem_hash = NULL;
2246         }
2247
2248         if (context_hash != NULL) {
2249                 g_hash_table_destroy(context_hash);
2250                 context_hash = NULL;
2251         }
2252
2253         connman_device_driver_unregister(&modem_driver);
2254         connman_network_driver_unregister(&network_driver);
2255
2256         g_dbus_remove_watch(connection, netreg_watch);
2257         g_dbus_remove_watch(connection, context_watch);
2258         g_dbus_remove_watch(connection, context_removed_watch);
2259         g_dbus_remove_watch(connection, context_added_watch);
2260         g_dbus_remove_watch(connection, sim_watch);
2261         g_dbus_remove_watch(connection, cm_watch);
2262         g_dbus_remove_watch(connection, modem_watch);
2263         g_dbus_remove_watch(connection, modem_added_watch);
2264         g_dbus_remove_watch(connection, modem_removed_watch);
2265         g_dbus_remove_watch(connection, watch);
2266
2267         dbus_connection_unref(connection);
2268 }
2269
2270 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
2271                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)