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 int create_device(struct dundee_data *info)
91 struct connman_device *device;
95 DBG("%s", info->path);
97 ident = g_strdup(get_ident(info->path));
98 device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
104 DBG("device %p", device);
106 connman_device_set_ident(device, ident);
108 connman_device_set_string(device, "Path", info->path);
110 connman_device_set_data(device, info);
112 err = connman_device_register(device);
114 connman_error("Failed to register DUN device");
115 connman_device_unref(device);
119 info->device = device;
126 static void destroy_device(struct dundee_data *info)
128 connman_device_set_powered(info->device, FALSE);
130 if (info->call != NULL)
131 dbus_pending_call_cancel(info->call);
133 if (info->network != NULL) {
134 connman_device_remove_network(info->device, info->network);
135 connman_network_unref(info->network);
136 info->network = NULL;
139 connman_device_unregister(info->device);
140 connman_device_unref(info->device);
145 static void device_destroy(gpointer data)
147 struct dundee_data *info = data;
149 if (info->device != NULL)
150 destroy_device(info);
158 static int create_network(struct dundee_data *info)
160 struct connman_network *network;
164 DBG("%s", info->path);
166 network = connman_network_create(info->path,
167 CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
171 DBG("network %p", network);
173 connman_network_set_data(network, info);
175 connman_network_set_string(network, "Path",
178 connman_network_set_name(network, info->name);
180 group = get_ident(info->path);
181 connman_network_set_group(network, group);
183 connman_network_set_available(network, TRUE);
185 err = connman_device_add_network(info->device, network);
187 connman_network_unref(network);
191 info->network = network;
196 static void set_connected(struct dundee_data *info)
198 DBG("%s", info->path);
200 connman_inet_ifup(info->index);
202 connman_network_set_index(info->network, info->index);
203 connman_network_set_ipv4_method(info->network,
204 CONNMAN_IPCONFIG_METHOD_FIXED);
205 connman_network_set_ipaddress(info->network, info->address);
206 connman_network_set_nameservers(info->network, info->nameservers);
208 connman_network_set_connected(info->network, TRUE);
211 static void set_disconnected(struct dundee_data *info)
213 DBG("%s", info->path);
215 connman_network_set_connected(info->network, FALSE);
216 connman_inet_ifdown(info->index);
219 static void set_property_reply(DBusPendingCall *call, void *user_data)
221 struct dundee_data *info = user_data;
225 DBG("%s", info->path);
229 dbus_error_init(&error);
231 reply = dbus_pending_call_steal_reply(call);
233 if (dbus_set_error_from_message(&error, reply)) {
234 connman_error("Failed to change property: %s %s %s",
235 info->path, error.name, error.message);
236 dbus_error_free(&error);
238 connman_network_set_error(info->network,
239 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
242 dbus_message_unref(reply);
244 dbus_pending_call_unref(call);
247 static int set_property(struct dundee_data *info,
248 const char *property, int type, void *value)
250 DBusMessage *message;
251 DBusMessageIter iter;
253 DBG("%s %s", info->path, property);
255 message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
256 DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
260 dbus_message_iter_init_append(message, &iter);
261 connman_dbus_property_append_basic(&iter, property, type, value);
263 if (dbus_connection_send_with_reply(connection, message,
264 &info->call, TIMEOUT) == FALSE) {
265 connman_error("Failed to change property: %s %s",
266 info->path, property);
267 dbus_message_unref(message);
271 if (info->call == NULL) {
272 connman_error("D-Bus connection not available");
273 dbus_message_unref(message);
277 dbus_pending_call_set_notify(info->call, set_property_reply,
280 dbus_message_unref(message);
285 static int device_set_active(struct dundee_data *info)
287 dbus_bool_t active = TRUE;
289 DBG("%s", info->path);
291 return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
295 static int device_set_inactive(struct dundee_data *info)
297 dbus_bool_t active = FALSE;
300 DBG("%s", info->path);
302 err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
304 if (err == -EINPROGRESS)
310 static int network_probe(struct connman_network *network)
312 DBG("network %p", network);
317 static void network_remove(struct connman_network *network)
319 DBG("network %p", network);
322 static int network_connect(struct connman_network *network)
324 struct dundee_data *info = connman_network_get_data(network);
326 DBG("network %p", network);
328 return device_set_active(info);
331 static int network_disconnect(struct connman_network *network)
333 struct dundee_data *info = connman_network_get_data(network);
335 DBG("network %p", network);
337 return device_set_inactive(info);
340 static struct connman_network_driver network_driver = {
342 .type = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
343 .probe = network_probe,
344 .remove = network_remove,
345 .connect = network_connect,
346 .disconnect = network_disconnect,
349 static int dundee_probe(struct connman_device *device)
354 DBG("device %p", device);
356 if (dundee_devices == NULL)
359 g_hash_table_iter_init(&iter, dundee_devices);
361 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
362 struct dundee_data *info = value;
364 if (device == info->device)
371 static void dundee_remove(struct connman_device *device)
373 DBG("device %p", device);
376 static int dundee_enable(struct connman_device *device)
378 DBG("device %p", device);
383 static int dundee_disable(struct connman_device *device)
385 DBG("device %p", device);
390 static struct connman_device_driver dundee_driver = {
392 .type = CONNMAN_DEVICE_TYPE_BLUETOOTH,
393 .probe = dundee_probe,
394 .remove = dundee_remove,
395 .enable = dundee_enable,
396 .disable = dundee_disable,
399 static char *extract_nameservers(DBusMessageIter *array)
401 DBusMessageIter entry;
402 char *nameservers = NULL;
405 dbus_message_iter_recurse(array, &entry);
407 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
408 const char *nameserver;
410 dbus_message_iter_get_basic(&entry, &nameserver);
412 if (nameservers == NULL) {
413 nameservers = g_strdup(nameserver);
416 nameservers = g_strdup_printf("%s %s", tmp, nameserver);
420 dbus_message_iter_next(&entry);
426 static void extract_settings(DBusMessageIter *array,
427 struct dundee_data *info)
429 DBusMessageIter dict;
430 char *address = NULL, *gateway = NULL;
431 char *nameservers = NULL;
432 const char *interface = NULL;
435 if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
438 dbus_message_iter_recurse(array, &dict);
440 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
441 DBusMessageIter entry, value;
442 const char *key, *val;
444 dbus_message_iter_recurse(&dict, &entry);
445 dbus_message_iter_get_basic(&entry, &key);
447 dbus_message_iter_next(&entry);
448 dbus_message_iter_recurse(&entry, &value);
450 if (g_str_equal(key, "Interface") == TRUE) {
451 dbus_message_iter_get_basic(&value, &interface);
453 DBG("Interface %s", interface);
455 index = connman_inet_ifindex(interface);
457 DBG("index %d", index);
461 } else if (g_str_equal(key, "Address") == TRUE) {
462 dbus_message_iter_get_basic(&value, &val);
464 address = g_strdup(val);
466 DBG("Address %s", address);
467 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
468 nameservers = extract_nameservers(&value);
470 DBG("Nameservers %s", nameservers);
471 } else if (g_str_equal(key, "Gateway") == TRUE) {
472 dbus_message_iter_get_basic(&value, &val);
474 gateway = g_strdup(val);
476 DBG("Gateway %s", gateway);
479 dbus_message_iter_next(&dict);
485 info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
486 if (info->address == NULL)
490 connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
492 info->nameservers = nameservers;
495 if (info->nameservers != nameservers)
502 static gboolean device_changed(DBusConnection *conn,
503 DBusMessage *message,
506 const char *path = dbus_message_get_path(message);
507 struct dundee_data *info = NULL;
508 DBusMessageIter iter, value;
510 const char *signature = DBUS_TYPE_STRING_AS_STRING
511 DBUS_TYPE_VARIANT_AS_STRING;
513 if (dbus_message_has_signature(message, signature) == FALSE) {
514 connman_error("dundee signature does not match");
518 info = g_hash_table_lookup(dundee_devices, path);
522 if (dbus_message_iter_init(message, &iter) == FALSE)
525 dbus_message_iter_get_basic(&iter, &key);
527 dbus_message_iter_next(&iter);
528 dbus_message_iter_recurse(&iter, &value);
531 * Dundee guarantees the ordering of Settings and
532 * Active. Settings will always be send before Active = True.
533 * That means we don't have to order here.
535 if (g_str_equal(key, "Active") == TRUE) {
536 dbus_message_iter_get_basic(&value, &info->active);
538 DBG("%s Active %d", info->path, info->active);
540 if (info->active == TRUE)
543 set_disconnected(info);
544 } else if (g_str_equal(key, "Settings") == TRUE) {
545 DBG("%s Settings", info->path);
547 extract_settings(&value, info);
548 } else if (g_str_equal(key, "Name") == TRUE) {
551 dbus_message_iter_get_basic(&value, &name);
554 info->name = g_strdup(name);
556 DBG("%s Name %s", info->path, info->name);
558 connman_network_set_name(info->network, info->name);
559 connman_network_update(info->network);
565 static void add_device(const char *path, DBusMessageIter *properties)
567 struct dundee_data *info;
570 info = g_hash_table_lookup(dundee_devices, path);
574 info = g_try_new0(struct dundee_data, 1);
578 info->path = g_strdup(path);
580 while (dbus_message_iter_get_arg_type(properties) ==
581 DBUS_TYPE_DICT_ENTRY) {
582 DBusMessageIter entry, value;
585 dbus_message_iter_recurse(properties, &entry);
586 dbus_message_iter_get_basic(&entry, &key);
588 dbus_message_iter_next(&entry);
589 dbus_message_iter_recurse(&entry, &value);
591 if (g_str_equal(key, "Active") == TRUE) {
592 dbus_message_iter_get_basic(&value, &info->active);
594 DBG("%s Active %d", info->path, info->active);
595 } else if (g_str_equal(key, "Settings") == TRUE) {
596 DBG("%s Settings", info->path);
598 extract_settings(&value, info);
599 } else if (g_str_equal(key, "Name") == TRUE) {
602 dbus_message_iter_get_basic(&value, &name);
604 info->name = g_strdup(name);
606 DBG("%s Name %s", info->path, info->name);
609 dbus_message_iter_next(properties);
612 g_hash_table_insert(dundee_devices, g_strdup(path), info);
614 err = create_device(info);
618 err = create_network(info);
620 destroy_device(info);
624 if (info->active == TRUE)
628 g_hash_table_remove(dundee_devices, path);
635 static gboolean device_added(DBusConnection *conn, DBusMessage *message,
638 DBusMessageIter iter, properties;
640 const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
641 DBUS_TYPE_ARRAY_AS_STRING
642 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
643 DBUS_TYPE_STRING_AS_STRING
644 DBUS_TYPE_VARIANT_AS_STRING
645 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
647 if (dbus_message_has_signature(message, signature) == FALSE) {
648 connman_error("dundee signature does not match");
654 if (dbus_message_iter_init(message, &iter) == FALSE)
657 dbus_message_iter_get_basic(&iter, &path);
659 dbus_message_iter_next(&iter);
660 dbus_message_iter_recurse(&iter, &properties);
662 add_device(path, &properties);
667 static void remove_device(DBusConnection *conn, const char *path)
669 DBG("path %s", path);
671 g_hash_table_remove(dundee_devices, path);
674 static gboolean device_removed(DBusConnection *conn, DBusMessage *message,
678 const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
680 if (dbus_message_has_signature(message, signature) == FALSE) {
681 connman_error("dundee signature does not match");
685 dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
687 remove_device(conn, path);
691 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
695 DBusMessageIter array, dict;
696 const char *signature = DBUS_TYPE_ARRAY_AS_STRING
697 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
698 DBUS_TYPE_OBJECT_PATH_AS_STRING
699 DBUS_TYPE_ARRAY_AS_STRING
700 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
701 DBUS_TYPE_STRING_AS_STRING
702 DBUS_TYPE_VARIANT_AS_STRING
703 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
704 DBUS_STRUCT_END_CHAR_AS_STRING;
708 reply = dbus_pending_call_steal_reply(call);
710 if (dbus_message_has_signature(reply, signature) == FALSE) {
711 connman_error("dundee signature does not match");
715 dbus_error_init(&error);
717 if (dbus_set_error_from_message(&error, reply) == TRUE) {
718 connman_error("%s", error.message);
719 dbus_error_free(&error);
723 if (dbus_message_iter_init(reply, &array) == FALSE)
726 dbus_message_iter_recurse(&array, &dict);
728 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
729 DBusMessageIter value, properties;
732 dbus_message_iter_recurse(&dict, &value);
733 dbus_message_iter_get_basic(&value, &path);
735 dbus_message_iter_next(&value);
736 dbus_message_iter_recurse(&value, &properties);
738 add_device(path, &properties);
740 dbus_message_iter_next(&dict);
744 dbus_message_unref(reply);
746 dbus_pending_call_unref(call);
749 static int manager_get_devices(void)
751 DBusMessage *message;
752 DBusPendingCall *call;
756 message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
757 DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
761 if (dbus_connection_send_with_reply(connection, message,
762 &call, TIMEOUT) == FALSE) {
763 connman_error("Failed to call GetDevices()");
764 dbus_message_unref(message);
769 connman_error("D-Bus connection not available");
770 dbus_message_unref(message);
774 dbus_pending_call_set_notify(call, manager_get_devices_reply,
777 dbus_message_unref(message);
782 static void dundee_connect(DBusConnection *conn, void *user_data)
784 DBG("connection %p", conn);
786 dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
787 g_free, device_destroy);
789 manager_get_devices();
792 static void dundee_disconnect(DBusConnection *conn, void *user_data)
794 DBG("connection %p", conn);
796 g_hash_table_destroy(dundee_devices);
797 dundee_devices = NULL;
801 static guint added_watch;
802 static guint removed_watch;
803 static guint device_watch;
805 static int dundee_init(void)
809 connection = connman_dbus_get_connection();
810 if (connection == NULL)
813 watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
814 dundee_connect, dundee_disconnect, NULL, NULL);
816 added_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE, NULL,
817 DUNDEE_MANAGER_INTERFACE,
818 DEVICE_ADDED, device_added,
821 removed_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
822 NULL, DUNDEE_MANAGER_INTERFACE,
823 DEVICE_REMOVED, device_removed,
826 device_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
827 NULL, DUNDEE_DEVICE_INTERFACE,
833 if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
839 err = connman_network_driver_register(&network_driver);
843 err = connman_device_driver_register(&dundee_driver);
845 connman_network_driver_unregister(&network_driver);
852 g_dbus_remove_watch(connection, watch);
853 g_dbus_remove_watch(connection, added_watch);
854 g_dbus_remove_watch(connection, removed_watch);
855 g_dbus_remove_watch(connection, device_watch);
857 dbus_connection_unref(connection);
862 static void dundee_exit(void)
864 g_dbus_remove_watch(connection, watch);
865 g_dbus_remove_watch(connection, added_watch);
866 g_dbus_remove_watch(connection, removed_watch);
867 g_dbus_remove_watch(connection, device_watch);
869 connman_device_driver_unregister(&dundee_driver);
870 connman_network_driver_unregister(&network_driver);
872 dbus_connection_unref(connection);
875 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
876 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)