ofono: Power up modems
[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  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdlib.h>
29
30 #include <gdbus.h>
31 #include <string.h>
32 #include <stdint.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/device.h>
37 #include <connman/network.h>
38 #include <connman/dbus.h>
39 #include <connman/log.h>
40
41 #define OFONO_SERVICE                   "org.ofono"
42
43 #define OFONO_MANAGER_INTERFACE         OFONO_SERVICE ".Manager"
44 #define OFONO_MODEM_INTERFACE           OFONO_SERVICE ".Modem"
45 #define OFONO_SIM_INTERFACE             OFONO_SERVICE ".SimManager"
46 #define OFONO_NETREG_INTERFACE          OFONO_SERVICE ".NetworkRegistration"
47 #define OFONO_CM_INTERFACE              OFONO_SERVICE ".ConnectionManager"
48 #define OFONO_CONTEXT_INTERFACE         OFONO_SERVICE ".ConnectionContext"
49
50 #define MODEM_ADDED                     "ModemAdded"
51 #define MODEM_REMOVED                   "ModemRemoved"
52 #define PROPERTY_CHANGED                "PropertyChanged"
53 #define CONTEXT_ADDED                   "ContextAdded"
54 #define CONTEXT_REMOVED                 "ContextRemoved"
55
56 #define SET_PROPERTY                    "SetProperty"
57 #define GET_MODEMS                      "GetModems"
58
59 #define TIMEOUT 40000
60
61 enum ofono_api {
62         OFONO_API_SIM =         0x1,
63         OFONO_API_NETREG =      0x2,
64         OFONO_API_CM =          0x4,
65 };
66
67 static DBusConnection *connection;
68
69 static GHashTable *modem_hash;
70
71 struct modem_data {
72         char *path;
73
74         /* Modem Interface */
75         char *serial;
76         connman_bool_t powered;
77         connman_bool_t online;
78         uint8_t interfaces;
79
80         connman_bool_t set_powered;
81
82         /* pending calls */
83         DBusPendingCall *call_set_property;
84 };
85
86 typedef void (*set_property_cb)(struct modem_data *data,
87                                 connman_bool_t success);
88
89 struct property_info {
90         struct modem_data *modem;
91         const char *path;
92         const char *interface;
93         const char *property;
94         set_property_cb set_property_cb;
95 };
96
97 static void set_property_reply(DBusPendingCall *call, void *user_data)
98 {
99         struct property_info *info = user_data;
100         DBusMessage *reply;
101         DBusError error;
102         connman_bool_t success = TRUE;
103
104         DBG("%s path %s %s.%s", info->modem->path,
105                 info->path, info->interface, info->property);
106
107         info->modem->call_set_property = NULL;
108
109         dbus_error_init(&error);
110
111         reply = dbus_pending_call_steal_reply(call);
112
113         if (dbus_set_error_from_message(&error, reply)) {
114                 connman_error("Failed to change property: %s %s.%s: %s %s",
115                                 info->path, info->interface, info->property,
116                                 error.name, error.message);
117                 dbus_error_free(&error);
118                 success = FALSE;
119         }
120
121         if (info->set_property_cb != NULL)
122                 (*info->set_property_cb)(info->modem, success);
123
124         dbus_message_unref(reply);
125
126         dbus_pending_call_unref(call);
127 }
128
129 static int set_property(struct modem_data *modem,
130                         const char *path, const char *interface,
131                         const char *property, int type, void *value,
132                         set_property_cb notify)
133 {
134         DBusMessage *message;
135         DBusMessageIter iter;
136         struct property_info *info;
137
138         DBG("%s path %s %s.%s", modem->path, path, interface, property);
139
140         if (modem->call_set_property != NULL) {
141                 connman_error("Pending SetProperty");
142                 return -EBUSY;
143         }
144
145         message = dbus_message_new_method_call(OFONO_SERVICE, path,
146                                         interface, SET_PROPERTY);
147         if (message == NULL)
148                 return -ENOMEM;
149
150         dbus_message_iter_init_append(message, &iter);
151         connman_dbus_property_append_basic(&iter, property, type, value);
152
153         if (dbus_connection_send_with_reply(connection, message,
154                         &modem->call_set_property, TIMEOUT) == FALSE) {
155                 connman_error("Failed to change property: %s %s.%s",
156                                 path, interface, property);
157                 dbus_message_unref(message);
158                 return -EINVAL;
159         }
160
161         if (modem->call_set_property == NULL) {
162                 connman_error("D-Bus connection not available");
163                 dbus_message_unref(message);
164                 return -EINVAL;
165         }
166
167         info = g_try_new0(struct property_info, 1);
168         if (info == NULL) {
169                 dbus_message_unref(message);
170                 return -ENOMEM;
171         }
172
173         info->modem = modem;
174         info->path = path;
175         info->interface = interface;
176         info->property = property;
177         info->set_property_cb = notify;
178
179         dbus_pending_call_set_notify(modem->call_set_property,
180                                         set_property_reply, info, g_free);
181
182         dbus_message_unref(message);
183
184         return -EINPROGRESS;
185 }
186
187 static int modem_set_powered(struct modem_data *modem)
188 {
189         DBG("%s", modem->path);
190
191         modem->set_powered = TRUE;
192
193         return set_property(modem, modem->path,
194                                 OFONO_MODEM_INTERFACE,
195                                 "Powered", DBUS_TYPE_BOOLEAN,
196                                 &modem->set_powered,
197                                 NULL);
198 }
199
200 static uint8_t extract_interfaces(DBusMessageIter *array)
201 {
202         DBusMessageIter entry;
203         uint8_t interfaces = 0;
204
205         dbus_message_iter_recurse(array, &entry);
206
207         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
208                 const char *name;
209
210                 dbus_message_iter_get_basic(&entry, &name);
211
212                 if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
213                         interfaces |= OFONO_API_SIM;
214                 else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
215                         interfaces |= OFONO_API_NETREG;
216                 else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
217                         interfaces |= OFONO_API_CM;
218
219                 dbus_message_iter_next(&entry);
220         }
221
222         return interfaces;
223 }
224
225 static gboolean context_changed(DBusConnection *connection,
226                                 DBusMessage *message,
227                                 void *user_data)
228 {
229         return TRUE;
230 }
231
232 static gboolean cm_context_added(DBusConnection *connection,
233                                         DBusMessage *message,
234                                         void *user_data)
235 {
236         return TRUE;
237 }
238
239 static gboolean cm_context_removed(DBusConnection *connection,
240                                         DBusMessage *message,
241                                         void *user_data)
242 {
243         return TRUE;
244 }
245
246 static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
247                                 void *user_data)
248 {
249         return TRUE;
250 }
251
252 static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
253                                 void *user_data)
254 {
255         return TRUE;
256 }
257
258 static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
259                                 void *user_data)
260 {
261         return TRUE;
262 }
263
264 static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
265                                 void *user_data)
266 {
267         const char *path = dbus_message_get_path(message);
268         struct modem_data *modem;
269         DBusMessageIter iter, value;
270         const char *key;
271
272         modem = g_hash_table_lookup(modem_hash, path);
273         if (modem == NULL)
274                 return TRUE;
275
276         if (dbus_message_iter_init(message, &iter) == FALSE)
277                 return TRUE;
278
279         dbus_message_iter_get_basic(&iter, &key);
280
281         dbus_message_iter_next(&iter);
282         dbus_message_iter_recurse(&iter, &value);
283
284         if (g_str_equal(key, "Powered") == TRUE) {
285                 dbus_message_iter_get_basic(&value, &modem->powered);
286
287                 DBG("%s Powered %d", modem->path, modem->powered);
288
289                 if (modem->powered == FALSE)
290                         modem_set_powered(modem);
291         } else if (g_str_equal(key, "Online") == TRUE) {
292                 dbus_message_iter_get_basic(&value, &modem->online);
293
294                 DBG("%s Online %d", modem->path, modem->online);
295         } else if (g_str_equal(key, "Interfaces") == TRUE) {
296                 modem->interfaces = extract_interfaces(&value);
297
298                 DBG("%s Interfaces 0x%02x", modem->path,
299                         modem->interfaces);
300         } else if (g_str_equal(key, "Serial") == TRUE) {
301                 char *serial;
302
303                 dbus_message_iter_get_basic(&value, &serial);
304
305                 g_free(modem->serial);
306                 modem->serial = g_strdup(serial);
307
308                 DBG("%s Serial %s", modem->path, modem->serial);
309         }
310
311         return TRUE;
312 }
313
314 static void add_modem(const char *path, DBusMessageIter *prop)
315 {
316         struct modem_data *modem;
317
318         DBG("%s", path);
319
320         modem = g_hash_table_lookup(modem_hash, path);
321         if (modem != NULL) {
322                 /*
323                  * When oFono powers up we ask for the modems and oFono is
324                  * reporting with modem_added signal the modems. Only
325                  * handle them once.
326                  */
327                 return;
328         }
329
330         modem = g_try_new0(struct modem_data, 1);
331         if (modem == NULL)
332                 return;
333
334         modem->path = g_strdup(path);
335
336         g_hash_table_insert(modem_hash, g_strdup(path), modem);
337
338         while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
339                 DBusMessageIter entry, value;
340                 const char *key;
341
342                 dbus_message_iter_recurse(prop, &entry);
343                 dbus_message_iter_get_basic(&entry, &key);
344
345                 dbus_message_iter_next(&entry);
346                 dbus_message_iter_recurse(&entry, &value);
347
348                 if (g_str_equal(key, "Powered") == TRUE) {
349                         dbus_message_iter_get_basic(&value, &modem->powered);
350
351                         DBG("%s Powered %d", modem->path, modem->powered);
352                 } else if (g_str_equal(key, "Online") == TRUE) {
353                         dbus_message_iter_get_basic(&value, &modem->online);
354
355                         DBG("%s Online %d", modem->path, modem->online);
356                 } else if (g_str_equal(key, "Interfaces") == TRUE) {
357                         modem->interfaces = extract_interfaces(&value);
358
359                         DBG("%s Interfaces 0x%02x", modem->path,
360                                 modem->interfaces);
361                 } else if (g_str_equal(key, "Serial") == TRUE) {
362                         char *serial;
363
364                         dbus_message_iter_get_basic(&value, &serial);
365                         modem->serial = g_strdup(serial);
366
367                         DBG("%s Serial %s", modem->path, modem->serial);
368                 }
369
370                 dbus_message_iter_next(prop);
371         }
372
373         if (modem->powered == FALSE)
374                 modem_set_powered(modem);
375 }
376
377 static void remove_modem(gpointer data)
378 {
379         struct modem_data *modem = data;
380
381         DBG("%s", modem->path);
382
383         if (modem->call_set_property != NULL)
384                 dbus_pending_call_cancel(modem->call_set_property);
385
386         g_free(modem->serial);
387         g_free(modem->path);
388
389         g_free(modem);
390 }
391
392 static gboolean modem_added(DBusConnection *connection,
393                                 DBusMessage *message, void *user_data)
394 {
395         DBusMessageIter iter, properties;
396         const char *path;
397
398         DBG("");
399
400         if (dbus_message_iter_init(message, &iter) == FALSE)
401                 return TRUE;
402
403         dbus_message_iter_get_basic(&iter, &path);
404
405         dbus_message_iter_next(&iter);
406         dbus_message_iter_recurse(&iter, &properties);
407
408         add_modem(path, &properties);
409
410         return TRUE;
411 }
412
413 static gboolean modem_removed(DBusConnection *connection,
414                                 DBusMessage *message, void *user_data)
415 {
416         DBusMessageIter iter;
417         const char *path;
418
419         DBG("");
420
421         if (dbus_message_iter_init(message, &iter) == FALSE)
422                 return TRUE;
423
424         dbus_message_iter_get_basic(&iter, &path);
425
426         g_hash_table_remove(modem_hash, path);
427
428         return TRUE;
429 }
430
431 static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
432 {
433         DBusMessage *reply;
434         DBusError error;
435         DBusMessageIter array, dict;
436
437         DBG("");
438
439         reply = dbus_pending_call_steal_reply(call);
440
441         dbus_error_init(&error);
442
443         if (dbus_set_error_from_message(&error, reply) == TRUE) {
444                 connman_error("%s", error.message);
445                 dbus_error_free(&error);
446                 goto done;
447         }
448
449         if (dbus_message_iter_init(reply, &array) == FALSE)
450                 goto done;
451
452         dbus_message_iter_recurse(&array, &dict);
453
454         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
455                 DBusMessageIter value, properties;
456                 const char *path;
457
458                 dbus_message_iter_recurse(&dict, &value);
459                 dbus_message_iter_get_basic(&value, &path);
460
461                 dbus_message_iter_next(&value);
462                 dbus_message_iter_recurse(&value, &properties);
463
464                 add_modem(path, &properties);
465
466                 dbus_message_iter_next(&dict);
467         }
468
469 done:
470         dbus_message_unref(reply);
471
472         dbus_pending_call_unref(call);
473 }
474
475 static int manager_get_modems(void)
476 {
477         DBusMessage *message;
478         DBusPendingCall *call;
479
480         DBG("");
481
482         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
483                                         OFONO_MANAGER_INTERFACE, GET_MODEMS);
484         if (message == NULL)
485                 return -ENOMEM;
486
487         if (dbus_connection_send_with_reply(connection, message,
488                                                &call, TIMEOUT) == FALSE) {
489                 connman_error("Failed to call GetModems()");
490                 dbus_message_unref(message);
491                 return -EINVAL;
492         }
493
494         if (call == NULL) {
495                 connman_error("D-Bus connection not available");
496                 dbus_message_unref(message);
497                 return -EINVAL;
498         }
499
500         dbus_pending_call_set_notify(call, manager_get_modems_reply,
501                                         NULL, NULL);
502
503         dbus_message_unref(message);
504
505         return -EINPROGRESS;
506 }
507
508 static void ofono_connect(DBusConnection *conn, void *user_data)
509 {
510         DBG("");
511
512         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
513                                                 g_free, remove_modem);
514         if (modem_hash == NULL)
515                 return;
516
517         manager_get_modems();
518 }
519
520 static void ofono_disconnect(DBusConnection *conn, void *user_data)
521 {
522         DBG("");
523
524         if (modem_hash == NULL)
525                 return;
526
527         g_hash_table_destroy(modem_hash);
528         modem_hash = NULL;
529 }
530
531 static int network_probe(struct connman_network *network)
532 {
533         DBG("network %p", network);
534
535         return 0;
536 }
537
538 static void network_remove(struct connman_network *network)
539 {
540         DBG("network %p", network);
541 }
542
543 static int network_connect(struct connman_network *network)
544 {
545         DBG("network %p", network);
546
547         return 0;
548 }
549
550 static int network_disconnect(struct connman_network *network)
551 {
552         DBG("network %p", network);
553
554         return 0;
555 }
556
557 static struct connman_network_driver network_driver = {
558         .name           = "network",
559         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
560         .probe          = network_probe,
561         .remove         = network_remove,
562         .connect        = network_connect,
563         .disconnect     = network_disconnect,
564 };
565
566 static int modem_probe(struct connman_device *device)
567 {
568         DBG("device %p", device);
569
570         return 0;
571 }
572
573 static void modem_remove(struct connman_device *device)
574 {
575         DBG("device %p", device);
576 }
577
578 static int modem_enable(struct connman_device *device)
579 {
580         DBG("device %p", device);
581
582         return 0;
583 }
584
585 static int modem_disable(struct connman_device *device)
586 {
587         DBG("device %p", device);
588
589         return 0;
590 }
591
592 static struct connman_device_driver modem_driver = {
593         .name           = "modem",
594         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
595         .probe          = modem_probe,
596         .remove         = modem_remove,
597         .enable         = modem_enable,
598         .disable        = modem_disable,
599 };
600
601 static guint watch;
602 static guint modem_added_watch;
603 static guint modem_removed_watch;
604 static guint modem_watch;
605 static guint cm_watch;
606 static guint sim_watch;
607 static guint context_added_watch;
608 static guint context_removed_watch;
609 static guint netreg_watch;
610 static guint context_watch;
611
612 static int ofono_init(void)
613 {
614         int err;
615
616         DBG("");
617
618         connection = connman_dbus_get_connection();
619         if (connection == NULL)
620                 return -EIO;
621
622         watch = g_dbus_add_service_watch(connection,
623                                         OFONO_SERVICE, ofono_connect,
624                                         ofono_disconnect, NULL, NULL);
625
626         modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
627                                                 OFONO_MANAGER_INTERFACE,
628                                                 MODEM_ADDED,
629                                                 modem_added,
630                                                 NULL, NULL);
631
632         modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
633                                                 OFONO_MANAGER_INTERFACE,
634                                                 MODEM_REMOVED,
635                                                 modem_removed,
636                                                 NULL, NULL);
637
638         modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
639                                                 OFONO_MODEM_INTERFACE,
640                                                 PROPERTY_CHANGED,
641                                                 modem_changed,
642                                                 NULL, NULL);
643
644         cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
645                                                 OFONO_CM_INTERFACE,
646                                                 PROPERTY_CHANGED,
647                                                 cm_changed,
648                                                 NULL, NULL);
649
650         sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
651                                                 OFONO_SIM_INTERFACE,
652                                                 PROPERTY_CHANGED,
653                                                 sim_changed,
654                                                 NULL, NULL);
655
656         context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
657                                                 OFONO_CM_INTERFACE,
658                                                 CONTEXT_ADDED,
659                                                 cm_context_added,
660                                                 NULL, NULL);
661
662         context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
663                                                 OFONO_CM_INTERFACE,
664                                                 CONTEXT_REMOVED,
665                                                 cm_context_removed,
666                                                 NULL, NULL);
667
668         context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
669                                                 OFONO_CONTEXT_INTERFACE,
670                                                 PROPERTY_CHANGED,
671                                                 context_changed,
672                                                 NULL, NULL);
673
674         netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
675                                                 OFONO_NETREG_INTERFACE,
676                                                 PROPERTY_CHANGED,
677                                                 netreg_changed,
678                                                 NULL, NULL);
679
680
681         if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
682                         modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
683                         context_added_watch == 0 ||
684                         context_removed_watch == 0 ||
685                         context_watch == 0 || netreg_watch == 0) {
686                 err = -EIO;
687                 goto remove;
688         }
689
690         err = connman_network_driver_register(&network_driver);
691         if (err < 0)
692                 goto remove;
693
694         err = connman_device_driver_register(&modem_driver);
695         if (err < 0) {
696                 connman_network_driver_unregister(&network_driver);
697                 goto remove;
698         }
699
700         return 0;
701
702 remove:
703         g_dbus_remove_watch(connection, netreg_watch);
704         g_dbus_remove_watch(connection, context_watch);
705         g_dbus_remove_watch(connection, context_removed_watch);
706         g_dbus_remove_watch(connection, context_added_watch);
707         g_dbus_remove_watch(connection, sim_watch);
708         g_dbus_remove_watch(connection, cm_watch);
709         g_dbus_remove_watch(connection, modem_watch);
710         g_dbus_remove_watch(connection, modem_removed_watch);
711         g_dbus_remove_watch(connection, modem_added_watch);
712         g_dbus_remove_watch(connection, watch);
713         dbus_connection_unref(connection);
714
715         return err;
716 }
717
718 static void ofono_exit(void)
719 {
720         DBG("");
721
722         if (modem_hash != NULL) {
723                 g_hash_table_destroy(modem_hash);
724                 modem_hash = NULL;
725         }
726
727         connman_device_driver_unregister(&modem_driver);
728         connman_network_driver_unregister(&network_driver);
729
730         g_dbus_remove_watch(connection, netreg_watch);
731         g_dbus_remove_watch(connection, context_watch);
732         g_dbus_remove_watch(connection, context_removed_watch);
733         g_dbus_remove_watch(connection, context_added_watch);
734         g_dbus_remove_watch(connection, sim_watch);
735         g_dbus_remove_watch(connection, cm_watch);
736         g_dbus_remove_watch(connection, modem_watch);
737         g_dbus_remove_watch(connection, modem_added_watch);
738         g_dbus_remove_watch(connection, modem_removed_watch);
739         g_dbus_remove_watch(connection, watch);
740
741         dbus_connection_unref(connection);
742 }
743
744 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
745                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)