ofono: Extract modem properties
[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 GET_MODEMS                      "GetModems"
57
58 #define TIMEOUT 40000
59
60 enum ofono_api {
61         OFONO_API_SIM =         0x1,
62         OFONO_API_NETREG =      0x2,
63         OFONO_API_CM =          0x4,
64 };
65
66 static DBusConnection *connection;
67
68 static GHashTable *modem_hash;
69
70 struct modem_data {
71         char *path;
72
73         /* Modem Interface */
74         char *serial;
75         connman_bool_t powered;
76         connman_bool_t online;
77         uint8_t interfaces;
78 };
79
80 static uint8_t extract_interfaces(DBusMessageIter *array)
81 {
82         DBusMessageIter entry;
83         uint8_t interfaces = 0;
84
85         dbus_message_iter_recurse(array, &entry);
86
87         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
88                 const char *name;
89
90                 dbus_message_iter_get_basic(&entry, &name);
91
92                 if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
93                         interfaces |= OFONO_API_SIM;
94                 else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
95                         interfaces |= OFONO_API_NETREG;
96                 else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
97                         interfaces |= OFONO_API_CM;
98
99                 dbus_message_iter_next(&entry);
100         }
101
102         return interfaces;
103 }
104
105 static gboolean context_changed(DBusConnection *connection,
106                                 DBusMessage *message,
107                                 void *user_data)
108 {
109         return TRUE;
110 }
111
112 static gboolean cm_context_added(DBusConnection *connection,
113                                         DBusMessage *message,
114                                         void *user_data)
115 {
116         return TRUE;
117 }
118
119 static gboolean cm_context_removed(DBusConnection *connection,
120                                         DBusMessage *message,
121                                         void *user_data)
122 {
123         return TRUE;
124 }
125
126 static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
127                                 void *user_data)
128 {
129         return TRUE;
130 }
131
132 static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
133                                 void *user_data)
134 {
135         return TRUE;
136 }
137
138 static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
139                                 void *user_data)
140 {
141         return TRUE;
142 }
143
144 static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
145                                 void *user_data)
146 {
147         const char *path = dbus_message_get_path(message);
148         struct modem_data *modem;
149         DBusMessageIter iter, value;
150         const char *key;
151
152         modem = g_hash_table_lookup(modem_hash, path);
153         if (modem == NULL)
154                 return TRUE;
155
156         if (dbus_message_iter_init(message, &iter) == FALSE)
157                 return TRUE;
158
159         dbus_message_iter_get_basic(&iter, &key);
160
161         dbus_message_iter_next(&iter);
162         dbus_message_iter_recurse(&iter, &value);
163
164         if (g_str_equal(key, "Powered") == TRUE) {
165                 dbus_message_iter_get_basic(&value, &modem->powered);
166
167                 DBG("%s Powered %d", modem->path, modem->powered);
168         } else if (g_str_equal(key, "Online") == TRUE) {
169                 dbus_message_iter_get_basic(&value, &modem->online);
170
171                 DBG("%s Online %d", modem->path, modem->online);
172         } else if (g_str_equal(key, "Interfaces") == TRUE) {
173                 modem->interfaces = extract_interfaces(&value);
174
175                 DBG("%s Interfaces 0x%02x", modem->path,
176                         modem->interfaces);
177         } else if (g_str_equal(key, "Serial") == TRUE) {
178                 char *serial;
179
180                 dbus_message_iter_get_basic(&value, &serial);
181
182                 g_free(modem->serial);
183                 modem->serial = g_strdup(serial);
184
185                 DBG("%s Serial %s", modem->path, modem->serial);
186         }
187
188         return TRUE;
189 }
190
191 static void add_modem(const char *path, DBusMessageIter *prop)
192 {
193         struct modem_data *modem;
194
195         DBG("%s", path);
196
197         modem = g_hash_table_lookup(modem_hash, path);
198         if (modem != NULL) {
199                 /*
200                  * When oFono powers up we ask for the modems and oFono is
201                  * reporting with modem_added signal the modems. Only
202                  * handle them once.
203                  */
204                 return;
205         }
206
207         modem = g_try_new0(struct modem_data, 1);
208         if (modem == NULL)
209                 return;
210
211         modem->path = g_strdup(path);
212
213         g_hash_table_insert(modem_hash, g_strdup(path), modem);
214
215         while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
216                 DBusMessageIter entry, value;
217                 const char *key;
218
219                 dbus_message_iter_recurse(prop, &entry);
220                 dbus_message_iter_get_basic(&entry, &key);
221
222                 dbus_message_iter_next(&entry);
223                 dbus_message_iter_recurse(&entry, &value);
224
225                 if (g_str_equal(key, "Powered") == TRUE) {
226                         dbus_message_iter_get_basic(&value, &modem->powered);
227
228                         DBG("%s Powered %d", modem->path, modem->powered);
229                 } else if (g_str_equal(key, "Online") == TRUE) {
230                         dbus_message_iter_get_basic(&value, &modem->online);
231
232                         DBG("%s Online %d", modem->path, modem->online);
233                 } else if (g_str_equal(key, "Interfaces") == TRUE) {
234                         modem->interfaces = extract_interfaces(&value);
235
236                         DBG("%s Interfaces 0x%02x", modem->path,
237                                 modem->interfaces);
238                 } else if (g_str_equal(key, "Serial") == TRUE) {
239                         char *serial;
240
241                         dbus_message_iter_get_basic(&value, &serial);
242                         modem->serial = g_strdup(serial);
243
244                         DBG("%s Serial %s", modem->path, modem->serial);
245                 }
246
247                 dbus_message_iter_next(prop);
248         }
249 }
250
251 static void remove_modem(gpointer data)
252 {
253         struct modem_data *modem = data;
254
255         DBG("%s", modem->path);
256
257         g_free(modem->serial);
258         g_free(modem->path);
259
260         g_free(modem);
261 }
262
263 static gboolean modem_added(DBusConnection *connection,
264                                 DBusMessage *message, void *user_data)
265 {
266         DBusMessageIter iter, properties;
267         const char *path;
268
269         DBG("");
270
271         if (dbus_message_iter_init(message, &iter) == FALSE)
272                 return TRUE;
273
274         dbus_message_iter_get_basic(&iter, &path);
275
276         dbus_message_iter_next(&iter);
277         dbus_message_iter_recurse(&iter, &properties);
278
279         add_modem(path, &properties);
280
281         return TRUE;
282 }
283
284 static gboolean modem_removed(DBusConnection *connection,
285                                 DBusMessage *message, void *user_data)
286 {
287         DBusMessageIter iter;
288         const char *path;
289
290         DBG("");
291
292         if (dbus_message_iter_init(message, &iter) == FALSE)
293                 return TRUE;
294
295         dbus_message_iter_get_basic(&iter, &path);
296
297         g_hash_table_remove(modem_hash, path);
298
299         return TRUE;
300 }
301
302 static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
303 {
304         DBusMessage *reply;
305         DBusError error;
306         DBusMessageIter array, dict;
307
308         DBG("");
309
310         reply = dbus_pending_call_steal_reply(call);
311
312         dbus_error_init(&error);
313
314         if (dbus_set_error_from_message(&error, reply) == TRUE) {
315                 connman_error("%s", error.message);
316                 dbus_error_free(&error);
317                 goto done;
318         }
319
320         if (dbus_message_iter_init(reply, &array) == FALSE)
321                 goto done;
322
323         dbus_message_iter_recurse(&array, &dict);
324
325         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
326                 DBusMessageIter value, properties;
327                 const char *path;
328
329                 dbus_message_iter_recurse(&dict, &value);
330                 dbus_message_iter_get_basic(&value, &path);
331
332                 dbus_message_iter_next(&value);
333                 dbus_message_iter_recurse(&value, &properties);
334
335                 add_modem(path, &properties);
336
337                 dbus_message_iter_next(&dict);
338         }
339
340 done:
341         dbus_message_unref(reply);
342
343         dbus_pending_call_unref(call);
344 }
345
346 static int manager_get_modems(void)
347 {
348         DBusMessage *message;
349         DBusPendingCall *call;
350
351         DBG("");
352
353         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
354                                         OFONO_MANAGER_INTERFACE, GET_MODEMS);
355         if (message == NULL)
356                 return -ENOMEM;
357
358         if (dbus_connection_send_with_reply(connection, message,
359                                                &call, TIMEOUT) == FALSE) {
360                 connman_error("Failed to call GetModems()");
361                 dbus_message_unref(message);
362                 return -EINVAL;
363         }
364
365         if (call == NULL) {
366                 connman_error("D-Bus connection not available");
367                 dbus_message_unref(message);
368                 return -EINVAL;
369         }
370
371         dbus_pending_call_set_notify(call, manager_get_modems_reply,
372                                         NULL, NULL);
373
374         dbus_message_unref(message);
375
376         return -EINPROGRESS;
377 }
378
379 static void ofono_connect(DBusConnection *conn, void *user_data)
380 {
381         DBG("");
382
383         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
384                                                 g_free, remove_modem);
385         if (modem_hash == NULL)
386                 return;
387
388         manager_get_modems();
389 }
390
391 static void ofono_disconnect(DBusConnection *conn, void *user_data)
392 {
393         DBG("");
394
395         if (modem_hash == NULL)
396                 return;
397
398         g_hash_table_destroy(modem_hash);
399         modem_hash = NULL;
400 }
401
402 static int network_probe(struct connman_network *network)
403 {
404         DBG("network %p", network);
405
406         return 0;
407 }
408
409 static void network_remove(struct connman_network *network)
410 {
411         DBG("network %p", network);
412 }
413
414 static int network_connect(struct connman_network *network)
415 {
416         DBG("network %p", network);
417
418         return 0;
419 }
420
421 static int network_disconnect(struct connman_network *network)
422 {
423         DBG("network %p", network);
424
425         return 0;
426 }
427
428 static struct connman_network_driver network_driver = {
429         .name           = "network",
430         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
431         .probe          = network_probe,
432         .remove         = network_remove,
433         .connect        = network_connect,
434         .disconnect     = network_disconnect,
435 };
436
437 static int modem_probe(struct connman_device *device)
438 {
439         DBG("device %p", device);
440
441         return 0;
442 }
443
444 static void modem_remove(struct connman_device *device)
445 {
446         DBG("device %p", device);
447 }
448
449 static int modem_enable(struct connman_device *device)
450 {
451         DBG("device %p", device);
452
453         return 0;
454 }
455
456 static int modem_disable(struct connman_device *device)
457 {
458         DBG("device %p", device);
459
460         return 0;
461 }
462
463 static struct connman_device_driver modem_driver = {
464         .name           = "modem",
465         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
466         .probe          = modem_probe,
467         .remove         = modem_remove,
468         .enable         = modem_enable,
469         .disable        = modem_disable,
470 };
471
472 static guint watch;
473 static guint modem_added_watch;
474 static guint modem_removed_watch;
475 static guint modem_watch;
476 static guint cm_watch;
477 static guint sim_watch;
478 static guint context_added_watch;
479 static guint context_removed_watch;
480 static guint netreg_watch;
481 static guint context_watch;
482
483 static int ofono_init(void)
484 {
485         int err;
486
487         DBG("");
488
489         connection = connman_dbus_get_connection();
490         if (connection == NULL)
491                 return -EIO;
492
493         watch = g_dbus_add_service_watch(connection,
494                                         OFONO_SERVICE, ofono_connect,
495                                         ofono_disconnect, NULL, NULL);
496
497         modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
498                                                 OFONO_MANAGER_INTERFACE,
499                                                 MODEM_ADDED,
500                                                 modem_added,
501                                                 NULL, NULL);
502
503         modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
504                                                 OFONO_MANAGER_INTERFACE,
505                                                 MODEM_REMOVED,
506                                                 modem_removed,
507                                                 NULL, NULL);
508
509         modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
510                                                 OFONO_MODEM_INTERFACE,
511                                                 PROPERTY_CHANGED,
512                                                 modem_changed,
513                                                 NULL, NULL);
514
515         cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
516                                                 OFONO_CM_INTERFACE,
517                                                 PROPERTY_CHANGED,
518                                                 cm_changed,
519                                                 NULL, NULL);
520
521         sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
522                                                 OFONO_SIM_INTERFACE,
523                                                 PROPERTY_CHANGED,
524                                                 sim_changed,
525                                                 NULL, NULL);
526
527         context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
528                                                 OFONO_CM_INTERFACE,
529                                                 CONTEXT_ADDED,
530                                                 cm_context_added,
531                                                 NULL, NULL);
532
533         context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
534                                                 OFONO_CM_INTERFACE,
535                                                 CONTEXT_REMOVED,
536                                                 cm_context_removed,
537                                                 NULL, NULL);
538
539         context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
540                                                 OFONO_CONTEXT_INTERFACE,
541                                                 PROPERTY_CHANGED,
542                                                 context_changed,
543                                                 NULL, NULL);
544
545         netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
546                                                 OFONO_NETREG_INTERFACE,
547                                                 PROPERTY_CHANGED,
548                                                 netreg_changed,
549                                                 NULL, NULL);
550
551
552         if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
553                         modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
554                         context_added_watch == 0 ||
555                         context_removed_watch == 0 ||
556                         context_watch == 0 || netreg_watch == 0) {
557                 err = -EIO;
558                 goto remove;
559         }
560
561         err = connman_network_driver_register(&network_driver);
562         if (err < 0)
563                 goto remove;
564
565         err = connman_device_driver_register(&modem_driver);
566         if (err < 0) {
567                 connman_network_driver_unregister(&network_driver);
568                 goto remove;
569         }
570
571         return 0;
572
573 remove:
574         g_dbus_remove_watch(connection, netreg_watch);
575         g_dbus_remove_watch(connection, context_watch);
576         g_dbus_remove_watch(connection, context_removed_watch);
577         g_dbus_remove_watch(connection, context_added_watch);
578         g_dbus_remove_watch(connection, sim_watch);
579         g_dbus_remove_watch(connection, cm_watch);
580         g_dbus_remove_watch(connection, modem_watch);
581         g_dbus_remove_watch(connection, modem_removed_watch);
582         g_dbus_remove_watch(connection, modem_added_watch);
583         g_dbus_remove_watch(connection, watch);
584         dbus_connection_unref(connection);
585
586         return err;
587 }
588
589 static void ofono_exit(void)
590 {
591         DBG("");
592
593         if (modem_hash != NULL) {
594                 g_hash_table_destroy(modem_hash);
595                 modem_hash = NULL;
596         }
597
598         connman_device_driver_unregister(&modem_driver);
599         connman_network_driver_unregister(&network_driver);
600
601         g_dbus_remove_watch(connection, netreg_watch);
602         g_dbus_remove_watch(connection, context_watch);
603         g_dbus_remove_watch(connection, context_removed_watch);
604         g_dbus_remove_watch(connection, context_added_watch);
605         g_dbus_remove_watch(connection, sim_watch);
606         g_dbus_remove_watch(connection, cm_watch);
607         g_dbus_remove_watch(connection, modem_watch);
608         g_dbus_remove_watch(connection, modem_added_watch);
609         g_dbus_remove_watch(connection, modem_removed_watch);
610         g_dbus_remove_watch(connection, watch);
611
612         dbus_connection_unref(connection);
613 }
614
615 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
616                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)