5 * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/device.h>
34 #include <connman/network.h>
35 #include <connman/inet.h>
36 #include <connman/dbus.h>
38 #define DUNDEE_SERVICE "org.ofono.dundee"
39 #define DUNDEE_MANAGER_INTERFACE DUNDEE_SERVICE ".Manager"
40 #define DUNDEE_DEVICE_INTERFACE DUNDEE_SERVICE ".Device"
42 #define DEVICE_ADDED "DeviceAdded"
43 #define DEVICE_REMOVED "DeviceRemoved"
44 #define PROPERTY_CHANGED "PropertyChanged"
46 #define GET_PROPERTIES "GetProperties"
47 #define SET_PROPERTY "SetProperty"
48 #define GET_DEVICES "GetDevices"
52 static DBusConnection *connection;
54 static GHashTable *dundee_devices = NULL;
60 struct connman_device *device;
61 struct connman_network *network;
63 connman_bool_t active;
68 enum connman_ipconfig_method method;
69 struct connman_ipaddress *address;
72 DBusPendingCall *call;
75 static char *get_ident(const char *path)
82 pos = strrchr(path, '/');
89 static void create_device(struct dundee_data *info)
91 struct connman_device *device;
94 DBG("%s", info->path);
96 ident = g_strdup(get_ident(info->path));
97 device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
101 DBG("device %p", device);
103 connman_device_set_ident(device, ident);
105 connman_device_set_string(device, "Path", info->path);
107 connman_device_set_data(device, info);
109 if (connman_device_register(device) < 0) {
110 connman_error("Failed to register DUN device");
111 connman_device_unref(device);
115 info->device = device;
121 static void destroy_device(struct dundee_data *info)
123 connman_device_set_powered(info->device, FALSE);
125 if (info->call != NULL)
126 dbus_pending_call_cancel(info->call);
128 if (info->network != NULL) {
129 connman_device_remove_network(info->device, info->network);
130 connman_network_unref(info->network);
131 info->network = NULL;
134 connman_device_unregister(info->device);
135 connman_device_unref(info->device);
140 static void device_destroy(gpointer data)
142 struct dundee_data *info = data;
144 if (info->device != NULL)
145 destroy_device(info);
153 static void create_network(struct dundee_data *info)
155 struct connman_network *network;
158 DBG("%s", info->path);
160 network = connman_network_create(info->path,
161 CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
165 DBG("network %p", network);
167 connman_network_set_data(network, info);
169 connman_network_set_string(network, "Path",
172 connman_network_set_name(network, info->name);
174 group = get_ident(info->path);
175 connman_network_set_group(network, group);
177 connman_network_set_available(network, TRUE);
179 if (connman_device_add_network(info->device, network) < 0) {
180 connman_network_unref(network);
184 info->network = network;
187 static void set_connected(struct dundee_data *info)
189 DBG("%s", info->path);
191 connman_inet_ifup(info->index);
193 connman_network_set_index(info->network, info->index);
194 connman_network_set_ipv4_method(info->network,
195 CONNMAN_IPCONFIG_METHOD_FIXED);
196 connman_network_set_ipaddress(info->network, info->address);
197 connman_network_set_nameservers(info->network, info->nameservers);
199 connman_network_set_connected(info->network, TRUE);
202 static void set_disconnected(struct dundee_data *info)
204 DBG("%s", info->path);
206 connman_network_set_connected(info->network, FALSE);
207 connman_inet_ifdown(info->index);
210 static void set_property_reply(DBusPendingCall *call, void *user_data)
212 struct dundee_data *info = user_data;
216 DBG("%s", info->path);
220 dbus_error_init(&error);
222 reply = dbus_pending_call_steal_reply(call);
224 if (dbus_set_error_from_message(&error, reply)) {
225 connman_error("Failed to change property: %s %s %s",
226 info->path, error.name, error.message);
227 dbus_error_free(&error);
229 connman_network_set_error(info->network,
230 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
233 dbus_message_unref(reply);
235 dbus_pending_call_unref(call);
238 static int set_property(struct dundee_data *info,
239 const char *property, int type, void *value)
241 DBusMessage *message;
242 DBusMessageIter iter;
244 DBG("%s %s", info->path, property);
246 message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
247 DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
251 dbus_message_iter_init_append(message, &iter);
252 connman_dbus_property_append_basic(&iter, property, type, value);
254 if (dbus_connection_send_with_reply(connection, message,
255 &info->call, TIMEOUT) == FALSE) {
256 connman_error("Failed to change property: %s %s",
257 info->path, property);
258 dbus_message_unref(message);
262 if (info->call == NULL) {
263 connman_error("D-Bus connection not available");
264 dbus_message_unref(message);
268 dbus_pending_call_set_notify(info->call, set_property_reply,
271 dbus_message_unref(message);
276 static int device_set_active(struct dundee_data *info)
278 dbus_bool_t active = TRUE;
280 DBG("%s", info->path);
282 return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
286 static int device_set_inactive(struct dundee_data *info)
288 dbus_bool_t active = FALSE;
291 DBG("%s", info->path);
293 err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
295 if (err == -EINPROGRESS)
301 static int network_probe(struct connman_network *network)
303 DBG("network %p", network);
308 static void network_remove(struct connman_network *network)
310 DBG("network %p", network);
313 static int network_connect(struct connman_network *network)
315 struct dundee_data *info = connman_network_get_data(network);
317 DBG("network %p", network);
319 return device_set_active(info);
322 static int network_disconnect(struct connman_network *network)
324 struct dundee_data *info = connman_network_get_data(network);
326 DBG("network %p", network);
328 return device_set_inactive(info);
331 static struct connman_network_driver network_driver = {
333 .type = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
334 .probe = network_probe,
335 .remove = network_remove,
336 .connect = network_connect,
337 .disconnect = network_disconnect,
340 static int dundee_probe(struct connman_device *device)
345 DBG("device %p", device);
347 if (dundee_devices == NULL)
350 g_hash_table_iter_init(&iter, dundee_devices);
352 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
353 struct dundee_data *info = value;
355 if (device == info->device)
362 static void dundee_remove(struct connman_device *device)
364 DBG("device %p", device);
367 static int dundee_enable(struct connman_device *device)
369 DBG("device %p", device);
374 static int dundee_disable(struct connman_device *device)
376 DBG("device %p", device);
381 static struct connman_device_driver dundee_driver = {
383 .type = CONNMAN_DEVICE_TYPE_BLUETOOTH,
384 .probe = dundee_probe,
385 .remove = dundee_remove,
386 .enable = dundee_enable,
387 .disable = dundee_disable,
390 static char *extract_nameservers(DBusMessageIter *array)
392 DBusMessageIter entry;
393 char *nameservers = NULL;
396 dbus_message_iter_recurse(array, &entry);
398 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
399 const char *nameserver;
401 dbus_message_iter_get_basic(&entry, &nameserver);
403 if (nameservers == NULL) {
404 nameservers = g_strdup(nameserver);
407 nameservers = g_strdup_printf("%s %s", tmp, nameserver);
411 dbus_message_iter_next(&entry);
417 static void extract_settings(DBusMessageIter *array,
418 struct dundee_data *info)
420 DBusMessageIter dict;
421 char *address = NULL, *gateway = NULL;
422 char *nameservers = NULL;
423 const char *interface = NULL;
426 if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
429 dbus_message_iter_recurse(array, &dict);
431 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
432 DBusMessageIter entry, value;
433 const char *key, *val;
435 dbus_message_iter_recurse(&dict, &entry);
436 dbus_message_iter_get_basic(&entry, &key);
438 dbus_message_iter_next(&entry);
439 dbus_message_iter_recurse(&entry, &value);
441 if (g_str_equal(key, "Interface") == TRUE) {
442 dbus_message_iter_get_basic(&value, &interface);
444 DBG("Interface %s", interface);
446 index = connman_inet_ifindex(interface);
448 DBG("index %d", index);
452 } else if (g_str_equal(key, "Address") == TRUE) {
453 dbus_message_iter_get_basic(&value, &val);
455 address = g_strdup(val);
457 DBG("Address %s", address);
458 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
459 nameservers = extract_nameservers(&value);
461 DBG("Nameservers %s", nameservers);
462 } else if (g_str_equal(key, "Gateway") == TRUE) {
463 dbus_message_iter_get_basic(&value, &val);
465 gateway = g_strdup(val);
467 DBG("Gateway %s", gateway);
470 dbus_message_iter_next(&dict);
476 info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
477 if (info->address == NULL)
481 connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
483 info->nameservers = nameservers;
486 if (info->nameservers != nameservers)
493 static gboolean device_changed(DBusConnection *connection,
494 DBusMessage *message,
497 const char *path = dbus_message_get_path(message);
498 struct dundee_data *info = NULL;
499 DBusMessageIter iter, value;
501 const char *signature = DBUS_TYPE_STRING_AS_STRING
502 DBUS_TYPE_VARIANT_AS_STRING;
504 if (dbus_message_has_signature(message, signature) == FALSE) {
505 connman_error("dundee signature does not match");
509 info = g_hash_table_lookup(dundee_devices, path);
513 if (dbus_message_iter_init(message, &iter) == FALSE)
516 dbus_message_iter_get_basic(&iter, &key);
518 dbus_message_iter_next(&iter);
519 dbus_message_iter_recurse(&iter, &value);
522 * Dundee guarantees the ordering of Settings and
523 * Active. Settings will always be send before Active = True.
524 * That means we don't have to order here.
526 if (g_str_equal(key, "Active") == TRUE) {
527 dbus_message_iter_get_basic(&value, &info->active);
529 DBG("%s Active %d", info->path, info->active);
531 if (info->active == TRUE)
534 set_disconnected(info);
535 } else if (g_str_equal(key, "Settings") == TRUE) {
536 DBG("%s Settings", info->path);
538 extract_settings(&value, info);
539 } else if (g_str_equal(key, "Name") == TRUE) {
542 dbus_message_iter_get_basic(&value, &name);
545 info->name = g_strdup(name);
547 DBG("%s Name %s", info->path, info->name);
549 connman_network_set_name(info->network, info->name);
550 connman_network_update(info->network);
556 static void add_device(const char *path, DBusMessageIter *properties)
558 struct dundee_data *info;
560 info = g_hash_table_lookup(dundee_devices, path);
564 info = g_try_new0(struct dundee_data, 1);
568 info->path = g_strdup(path);
570 while (dbus_message_iter_get_arg_type(properties) ==
571 DBUS_TYPE_DICT_ENTRY) {
572 DBusMessageIter entry, value;
575 dbus_message_iter_recurse(properties, &entry);
576 dbus_message_iter_get_basic(&entry, &key);
578 dbus_message_iter_next(&entry);
579 dbus_message_iter_recurse(&entry, &value);
581 if (g_str_equal(key, "Active") == TRUE) {
582 dbus_message_iter_get_basic(&value, &info->active);
584 DBG("%s Active %d", info->path, info->active);
585 } else if (g_str_equal(key, "Settings") == TRUE) {
586 DBG("%s Settings", info->path);
588 extract_settings(&value, info);
589 } else if (g_str_equal(key, "Name") == TRUE) {
592 dbus_message_iter_get_basic(&value, &name);
594 info->name = g_strdup(name);
596 DBG("%s Name %s", info->path, info->name);
599 dbus_message_iter_next(properties);
602 g_hash_table_insert(dundee_devices, g_strdup(path), info);
605 create_network(info);
607 if (info->active == TRUE)
611 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
614 DBusMessageIter iter, properties;
616 const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
617 DBUS_TYPE_ARRAY_AS_STRING
618 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
619 DBUS_TYPE_STRING_AS_STRING
620 DBUS_TYPE_VARIANT_AS_STRING
621 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
623 if (dbus_message_has_signature(message, signature) == FALSE) {
624 connman_error("dundee signature does not match");
630 if (dbus_message_iter_init(message, &iter) == FALSE)
633 dbus_message_iter_get_basic(&iter, &path);
635 dbus_message_iter_next(&iter);
636 dbus_message_iter_recurse(&iter, &properties);
638 add_device(path, &properties);
643 static void remove_device(DBusConnection *connection, const char *path)
645 DBG("path %s", path);
647 g_hash_table_remove(dundee_devices, path);
650 static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
654 const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
656 if (dbus_message_has_signature(message, signature) == FALSE) {
657 connman_error("dundee signature does not match");
661 dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
663 remove_device(connection, path);
667 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
671 DBusMessageIter array, dict;
672 const char *signature = DBUS_TYPE_ARRAY_AS_STRING
673 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
674 DBUS_TYPE_OBJECT_PATH_AS_STRING
675 DBUS_TYPE_ARRAY_AS_STRING
676 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
677 DBUS_TYPE_STRING_AS_STRING
678 DBUS_TYPE_VARIANT_AS_STRING
679 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
680 DBUS_STRUCT_END_CHAR_AS_STRING;
684 reply = dbus_pending_call_steal_reply(call);
686 if (dbus_message_has_signature(reply, signature) == FALSE) {
687 connman_error("dundee signature does not match");
691 dbus_error_init(&error);
693 if (dbus_set_error_from_message(&error, reply) == TRUE) {
694 connman_error("%s", error.message);
695 dbus_error_free(&error);
699 if (dbus_message_iter_init(reply, &array) == FALSE)
702 dbus_message_iter_recurse(&array, &dict);
704 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
705 DBusMessageIter value, properties;
708 dbus_message_iter_recurse(&dict, &value);
709 dbus_message_iter_get_basic(&value, &path);
711 dbus_message_iter_next(&value);
712 dbus_message_iter_recurse(&value, &properties);
714 add_device(path, &properties);
716 dbus_message_iter_next(&dict);
720 dbus_message_unref(reply);
722 dbus_pending_call_unref(call);
725 static int manager_get_devices(void)
727 DBusMessage *message;
728 DBusPendingCall *call;
732 message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
733 DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
737 if (dbus_connection_send_with_reply(connection, message,
738 &call, TIMEOUT) == FALSE) {
739 connman_error("Failed to call GetDevices()");
740 dbus_message_unref(message);
745 connman_error("D-Bus connection not available");
746 dbus_message_unref(message);
750 dbus_pending_call_set_notify(call, manager_get_devices_reply,
753 dbus_message_unref(message);
758 static void dundee_connect(DBusConnection *connection, void *user_data)
760 DBG("connection %p", connection);
762 dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
763 g_free, device_destroy);
765 manager_get_devices();
768 static void dundee_disconnect(DBusConnection *connection, void *user_data)
770 DBG("connection %p", connection);
772 g_hash_table_destroy(dundee_devices);
773 dundee_devices = NULL;
777 static guint added_watch;
778 static guint removed_watch;
779 static guint device_watch;
781 static int dundee_init(void)
785 connection = connman_dbus_get_connection();
786 if (connection == NULL)
789 watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
790 dundee_connect, dundee_disconnect, NULL, NULL);
792 added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
793 DUNDEE_MANAGER_INTERFACE,
794 DEVICE_ADDED, device_added,
797 removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
798 DUNDEE_MANAGER_INTERFACE,
799 DEVICE_REMOVED, device_removed,
802 device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
803 DUNDEE_DEVICE_INTERFACE,
809 if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
815 err = connman_network_driver_register(&network_driver);
819 err = connman_device_driver_register(&dundee_driver);
821 connman_network_driver_unregister(&network_driver);
828 g_dbus_remove_watch(connection, watch);
829 g_dbus_remove_watch(connection, added_watch);
830 g_dbus_remove_watch(connection, removed_watch);
831 g_dbus_remove_watch(connection, device_watch);
833 dbus_connection_unref(connection);
838 static void dundee_exit(void)
840 g_dbus_remove_watch(connection, watch);
841 g_dbus_remove_watch(connection, added_watch);
842 g_dbus_remove_watch(connection, removed_watch);
843 g_dbus_remove_watch(connection, device_watch);
845 connman_device_driver_unregister(&dundee_driver);
846 connman_network_driver_unregister(&network_driver);
848 dbus_connection_unref(connection);
851 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
852 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)