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/service.h>
36 #include <connman/inet.h>
37 #include <connman/dbus.h>
39 #define DUNDEE_SERVICE "org.ofono.dundee"
40 #define DUNDEE_MANAGER_INTERFACE DUNDEE_SERVICE ".Manager"
41 #define DUNDEE_DEVICE_INTERFACE DUNDEE_SERVICE ".Device"
43 #define DEVICE_ADDED "DeviceAdded"
44 #define DEVICE_REMOVED "DeviceRemoved"
45 #define PROPERTY_CHANGED "PropertyChanged"
47 #define GET_PROPERTIES "GetProperties"
48 #define SET_PROPERTY "SetProperty"
49 #define GET_DEVICES "GetDevices"
53 static DBusConnection *connection;
55 static GHashTable *dundee_devices = NULL;
61 struct connman_device *device;
62 struct connman_network *network;
64 connman_bool_t active;
69 enum connman_ipconfig_method method;
70 struct connman_ipaddress *address;
73 DBusPendingCall *call;
76 static char *get_ident(const char *path)
83 pos = strrchr(path, '/');
90 static int create_device(struct dundee_data *info)
92 struct connman_device *device;
96 DBG("%s", info->path);
98 ident = g_strdup(get_ident(info->path));
99 device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
100 if (device == NULL) {
105 DBG("device %p", device);
107 connman_device_set_ident(device, ident);
109 connman_device_set_string(device, "Path", info->path);
111 connman_device_set_data(device, info);
113 err = connman_device_register(device);
115 connman_error("Failed to register DUN device");
116 connman_device_unref(device);
120 info->device = device;
127 static void destroy_device(struct dundee_data *info)
129 connman_device_set_powered(info->device, FALSE);
131 if (info->call != NULL)
132 dbus_pending_call_cancel(info->call);
134 if (info->network != NULL) {
135 connman_device_remove_network(info->device, info->network);
136 connman_network_unref(info->network);
137 info->network = NULL;
140 connman_device_unregister(info->device);
141 connman_device_unref(info->device);
146 static void device_destroy(gpointer data)
148 struct dundee_data *info = data;
150 if (info->device != NULL)
151 destroy_device(info);
159 static int create_network(struct dundee_data *info)
161 struct connman_network *network;
165 DBG("%s", info->path);
167 network = connman_network_create(info->path,
168 CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
172 DBG("network %p", network);
174 connman_network_set_data(network, info);
176 connman_network_set_string(network, "Path",
179 connman_network_set_name(network, info->name);
181 group = get_ident(info->path);
182 connman_network_set_group(network, group);
184 connman_network_set_available(network, TRUE);
186 err = connman_device_add_network(info->device, network);
188 connman_network_unref(network);
192 info->network = network;
197 static void set_connected(struct dundee_data *info)
199 struct connman_service *service;
201 DBG("%s", info->path);
203 connman_inet_ifup(info->index);
205 service = connman_service_lookup_from_network(info->network);
209 connman_service_create_ip4config(service, info->index);
210 connman_network_set_index(info->network, info->index);
211 connman_network_set_ipv4_method(info->network,
212 CONNMAN_IPCONFIG_METHOD_FIXED);
213 connman_network_set_ipaddress(info->network, info->address);
214 connman_network_set_nameservers(info->network, info->nameservers);
216 connman_network_set_connected(info->network, TRUE);
219 static void set_disconnected(struct dundee_data *info)
221 DBG("%s", info->path);
223 connman_network_set_connected(info->network, FALSE);
224 connman_inet_ifdown(info->index);
227 static void set_property_reply(DBusPendingCall *call, void *user_data)
229 struct dundee_data *info = user_data;
233 DBG("%s", info->path);
237 dbus_error_init(&error);
239 reply = dbus_pending_call_steal_reply(call);
241 if (dbus_set_error_from_message(&error, reply)) {
242 connman_error("Failed to change property: %s %s %s",
243 info->path, error.name, error.message);
244 dbus_error_free(&error);
246 connman_network_set_error(info->network,
247 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
250 dbus_message_unref(reply);
252 dbus_pending_call_unref(call);
255 static int set_property(struct dundee_data *info,
256 const char *property, int type, void *value)
258 DBusMessage *message;
259 DBusMessageIter iter;
261 DBG("%s %s", info->path, property);
263 message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
264 DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
268 dbus_message_iter_init_append(message, &iter);
269 connman_dbus_property_append_basic(&iter, property, type, value);
271 if (dbus_connection_send_with_reply(connection, message,
272 &info->call, TIMEOUT) == FALSE) {
273 connman_error("Failed to change property: %s %s",
274 info->path, property);
275 dbus_message_unref(message);
279 if (info->call == NULL) {
280 connman_error("D-Bus connection not available");
281 dbus_message_unref(message);
285 dbus_pending_call_set_notify(info->call, set_property_reply,
288 dbus_message_unref(message);
293 static int device_set_active(struct dundee_data *info)
295 dbus_bool_t active = TRUE;
297 DBG("%s", info->path);
299 return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
303 static int device_set_inactive(struct dundee_data *info)
305 dbus_bool_t active = FALSE;
308 DBG("%s", info->path);
310 err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
312 if (err == -EINPROGRESS)
318 static int network_probe(struct connman_network *network)
320 DBG("network %p", network);
325 static void network_remove(struct connman_network *network)
327 DBG("network %p", network);
330 static int network_connect(struct connman_network *network)
332 struct dundee_data *info = connman_network_get_data(network);
334 DBG("network %p", network);
336 return device_set_active(info);
339 static int network_disconnect(struct connman_network *network)
341 struct dundee_data *info = connman_network_get_data(network);
343 DBG("network %p", network);
345 return device_set_inactive(info);
348 static struct connman_network_driver network_driver = {
350 .type = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
351 .probe = network_probe,
352 .remove = network_remove,
353 .connect = network_connect,
354 .disconnect = network_disconnect,
357 static int dundee_probe(struct connman_device *device)
362 DBG("device %p", device);
364 if (dundee_devices == NULL)
367 g_hash_table_iter_init(&iter, dundee_devices);
369 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
370 struct dundee_data *info = value;
372 if (device == info->device)
379 static void dundee_remove(struct connman_device *device)
381 DBG("device %p", device);
384 static int dundee_enable(struct connman_device *device)
386 DBG("device %p", device);
391 static int dundee_disable(struct connman_device *device)
393 DBG("device %p", device);
398 static struct connman_device_driver dundee_driver = {
400 .type = CONNMAN_DEVICE_TYPE_BLUETOOTH,
401 .probe = dundee_probe,
402 .remove = dundee_remove,
403 .enable = dundee_enable,
404 .disable = dundee_disable,
407 static char *extract_nameservers(DBusMessageIter *array)
409 DBusMessageIter entry;
410 char *nameservers = NULL;
413 dbus_message_iter_recurse(array, &entry);
415 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
416 const char *nameserver;
418 dbus_message_iter_get_basic(&entry, &nameserver);
420 if (nameservers == NULL) {
421 nameservers = g_strdup(nameserver);
424 nameservers = g_strdup_printf("%s %s", tmp, nameserver);
428 dbus_message_iter_next(&entry);
434 static void extract_settings(DBusMessageIter *array,
435 struct dundee_data *info)
437 DBusMessageIter dict;
438 char *address = NULL, *gateway = NULL;
439 char *nameservers = NULL;
440 const char *interface = NULL;
443 if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
446 dbus_message_iter_recurse(array, &dict);
448 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
449 DBusMessageIter entry, value;
450 const char *key, *val;
452 dbus_message_iter_recurse(&dict, &entry);
453 dbus_message_iter_get_basic(&entry, &key);
455 dbus_message_iter_next(&entry);
456 dbus_message_iter_recurse(&entry, &value);
458 if (g_str_equal(key, "Interface") == TRUE) {
459 dbus_message_iter_get_basic(&value, &interface);
461 DBG("Interface %s", interface);
463 index = connman_inet_ifindex(interface);
465 DBG("index %d", index);
469 } else if (g_str_equal(key, "Address") == TRUE) {
470 dbus_message_iter_get_basic(&value, &val);
472 address = g_strdup(val);
474 DBG("Address %s", address);
475 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
476 nameservers = extract_nameservers(&value);
478 DBG("Nameservers %s", nameservers);
479 } else if (g_str_equal(key, "Gateway") == TRUE) {
480 dbus_message_iter_get_basic(&value, &val);
482 gateway = g_strdup(val);
484 DBG("Gateway %s", gateway);
487 dbus_message_iter_next(&dict);
493 info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
494 if (info->address == NULL)
498 connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
500 info->nameservers = nameservers;
503 if (info->nameservers != nameservers)
510 static gboolean device_changed(DBusConnection *conn,
511 DBusMessage *message,
514 const char *path = dbus_message_get_path(message);
515 struct dundee_data *info = NULL;
516 DBusMessageIter iter, value;
518 const char *signature = DBUS_TYPE_STRING_AS_STRING
519 DBUS_TYPE_VARIANT_AS_STRING;
521 if (dbus_message_has_signature(message, signature) == FALSE) {
522 connman_error("dundee signature does not match");
526 info = g_hash_table_lookup(dundee_devices, path);
530 if (dbus_message_iter_init(message, &iter) == FALSE)
533 dbus_message_iter_get_basic(&iter, &key);
535 dbus_message_iter_next(&iter);
536 dbus_message_iter_recurse(&iter, &value);
539 * Dundee guarantees the ordering of Settings and
540 * Active. Settings will always be send before Active = True.
541 * That means we don't have to order here.
543 if (g_str_equal(key, "Active") == TRUE) {
544 dbus_message_iter_get_basic(&value, &info->active);
546 DBG("%s Active %d", info->path, info->active);
548 if (info->active == TRUE)
551 set_disconnected(info);
552 } else if (g_str_equal(key, "Settings") == TRUE) {
553 DBG("%s Settings", info->path);
555 extract_settings(&value, info);
556 } else if (g_str_equal(key, "Name") == TRUE) {
559 dbus_message_iter_get_basic(&value, &name);
562 info->name = g_strdup(name);
564 DBG("%s Name %s", info->path, info->name);
566 connman_network_set_name(info->network, info->name);
567 connman_network_update(info->network);
573 static void add_device(const char *path, DBusMessageIter *properties)
575 struct dundee_data *info;
578 info = g_hash_table_lookup(dundee_devices, path);
582 info = g_try_new0(struct dundee_data, 1);
586 info->path = g_strdup(path);
588 while (dbus_message_iter_get_arg_type(properties) ==
589 DBUS_TYPE_DICT_ENTRY) {
590 DBusMessageIter entry, value;
593 dbus_message_iter_recurse(properties, &entry);
594 dbus_message_iter_get_basic(&entry, &key);
596 dbus_message_iter_next(&entry);
597 dbus_message_iter_recurse(&entry, &value);
599 if (g_str_equal(key, "Active") == TRUE) {
600 dbus_message_iter_get_basic(&value, &info->active);
602 DBG("%s Active %d", info->path, info->active);
603 } else if (g_str_equal(key, "Settings") == TRUE) {
604 DBG("%s Settings", info->path);
606 extract_settings(&value, info);
607 } else if (g_str_equal(key, "Name") == TRUE) {
610 dbus_message_iter_get_basic(&value, &name);
612 info->name = g_strdup(name);
614 DBG("%s Name %s", info->path, info->name);
617 dbus_message_iter_next(properties);
620 g_hash_table_insert(dundee_devices, g_strdup(path), info);
622 err = create_device(info);
626 err = create_network(info);
628 destroy_device(info);
632 if (info->active == TRUE)
638 g_hash_table_remove(dundee_devices, path);
641 static gboolean device_added(DBusConnection *conn, DBusMessage *message,
644 DBusMessageIter iter, properties;
646 const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
647 DBUS_TYPE_ARRAY_AS_STRING
648 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
649 DBUS_TYPE_STRING_AS_STRING
650 DBUS_TYPE_VARIANT_AS_STRING
651 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
653 if (dbus_message_has_signature(message, signature) == FALSE) {
654 connman_error("dundee signature does not match");
660 if (dbus_message_iter_init(message, &iter) == FALSE)
663 dbus_message_iter_get_basic(&iter, &path);
665 dbus_message_iter_next(&iter);
666 dbus_message_iter_recurse(&iter, &properties);
668 add_device(path, &properties);
673 static void remove_device(DBusConnection *conn, const char *path)
675 DBG("path %s", path);
677 g_hash_table_remove(dundee_devices, path);
680 static gboolean device_removed(DBusConnection *conn, DBusMessage *message,
684 const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
686 if (dbus_message_has_signature(message, signature) == FALSE) {
687 connman_error("dundee signature does not match");
691 dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
693 remove_device(conn, path);
697 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
701 DBusMessageIter array, dict;
702 const char *signature = DBUS_TYPE_ARRAY_AS_STRING
703 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
704 DBUS_TYPE_OBJECT_PATH_AS_STRING
705 DBUS_TYPE_ARRAY_AS_STRING
706 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
707 DBUS_TYPE_STRING_AS_STRING
708 DBUS_TYPE_VARIANT_AS_STRING
709 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
710 DBUS_STRUCT_END_CHAR_AS_STRING;
714 reply = dbus_pending_call_steal_reply(call);
716 if (dbus_message_has_signature(reply, signature) == FALSE) {
717 connman_error("dundee signature does not match");
721 dbus_error_init(&error);
723 if (dbus_set_error_from_message(&error, reply) == TRUE) {
724 connman_error("%s", error.message);
725 dbus_error_free(&error);
729 if (dbus_message_iter_init(reply, &array) == FALSE)
732 dbus_message_iter_recurse(&array, &dict);
734 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
735 DBusMessageIter value, properties;
738 dbus_message_iter_recurse(&dict, &value);
739 dbus_message_iter_get_basic(&value, &path);
741 dbus_message_iter_next(&value);
742 dbus_message_iter_recurse(&value, &properties);
744 add_device(path, &properties);
746 dbus_message_iter_next(&dict);
750 dbus_message_unref(reply);
752 dbus_pending_call_unref(call);
755 static int manager_get_devices(void)
757 DBusMessage *message;
758 DBusPendingCall *call;
762 message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
763 DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
767 if (dbus_connection_send_with_reply(connection, message,
768 &call, TIMEOUT) == FALSE) {
769 connman_error("Failed to call GetDevices()");
770 dbus_message_unref(message);
775 connman_error("D-Bus connection not available");
776 dbus_message_unref(message);
780 dbus_pending_call_set_notify(call, manager_get_devices_reply,
783 dbus_message_unref(message);
788 static void dundee_connect(DBusConnection *conn, void *user_data)
790 DBG("connection %p", conn);
792 dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
793 g_free, device_destroy);
795 manager_get_devices();
798 static void dundee_disconnect(DBusConnection *conn, void *user_data)
800 DBG("connection %p", conn);
802 g_hash_table_destroy(dundee_devices);
803 dundee_devices = NULL;
807 static guint added_watch;
808 static guint removed_watch;
809 static guint device_watch;
811 static int dundee_init(void)
815 connection = connman_dbus_get_connection();
816 if (connection == NULL)
819 watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
820 dundee_connect, dundee_disconnect, NULL, NULL);
822 added_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE, NULL,
823 DUNDEE_MANAGER_INTERFACE,
824 DEVICE_ADDED, device_added,
827 removed_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
828 NULL, DUNDEE_MANAGER_INTERFACE,
829 DEVICE_REMOVED, device_removed,
832 device_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
833 NULL, DUNDEE_DEVICE_INTERFACE,
839 if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
845 err = connman_network_driver_register(&network_driver);
849 err = connman_device_driver_register(&dundee_driver);
851 connman_network_driver_unregister(&network_driver);
858 g_dbus_remove_watch(connection, watch);
859 g_dbus_remove_watch(connection, added_watch);
860 g_dbus_remove_watch(connection, removed_watch);
861 g_dbus_remove_watch(connection, device_watch);
863 dbus_connection_unref(connection);
868 static void dundee_exit(void)
870 g_dbus_remove_watch(connection, watch);
871 g_dbus_remove_watch(connection, added_watch);
872 g_dbus_remove_watch(connection, removed_watch);
873 g_dbus_remove_watch(connection, device_watch);
875 connman_device_driver_unregister(&dundee_driver);
876 connman_network_driver_unregister(&network_driver);
878 dbus_connection_unref(connection);
881 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
882 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)