diff options
Diffstat (limited to 'src/manager.c')
-rw-r--r-- | src/manager.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/src/manager.c b/src/manager.c new file mode 100644 index 0000000..e805e0c --- /dev/null +++ b/src/manager.c @@ -0,0 +1,431 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> + +#include <glib.h> + +#include <dbus/dbus.h> + +#include <gdbus.h> + +#include "hcid.h" +#include "dbus-common.h" +#include "log.h" +#include "adapter.h" +#include "error.h" +#include "manager.h" + +static char base_path[50] = "/org/bluez"; + +static DBusConnection *connection = NULL; +static int default_adapter_id = -1; +static GSList *adapters = NULL; + +const char *manager_get_base_path(void) +{ + return base_path; +} + +static DBusMessage *default_adapter(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + struct btd_adapter *adapter; + const gchar *path; + + adapter = manager_find_adapter_by_id(default_adapter_id); + if (!adapter) + return btd_error_no_such_adapter(msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + path = adapter_get_path(adapter); + + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + return reply; +} + +static DBusMessage *find_adapter(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + struct btd_adapter *adapter; + const char *pattern; + int dev_id; + const gchar *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern, + DBUS_TYPE_INVALID)) + return NULL; + + /* hci_devid() would make sense to use here, except it is + * restricted to devices which are up */ + if (!strcmp(pattern, "any") || !strcmp(pattern, "00:00:00:00:00:00")) { + path = adapter_any_get_path(); + if (path != NULL) + goto done; + return btd_error_no_such_adapter(msg); + } else if (!strncmp(pattern, "hci", 3) && strlen(pattern) >= 4) { + dev_id = atoi(pattern + 3); + adapter = manager_find_adapter_by_id(dev_id); + } else { + bdaddr_t bdaddr; + str2ba(pattern, &bdaddr); + adapter = manager_find_adapter(&bdaddr); + } + + if (!adapter) + return btd_error_no_such_adapter(msg); + + path = adapter_get_path(adapter); + +done: + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + return reply; +} + +static DBusMessage *list_adapters(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessage *reply; + GSList *l; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter); + + for (l = adapters; l; l = l->next) { + struct btd_adapter *adapter = l->data; + const gchar *path = adapter_get_path(adapter); + + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_OBJECT_PATH, &path); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return reply; +} + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter dict; + GSList *list; + char **array; + int i; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + array = g_new0(char *, g_slist_length(adapters) + 1); + for (i = 0, list = adapters; list; list = list->next) { + struct btd_adapter *adapter = list->data; + + array[i] = (char *) adapter_get_path(adapter); + i++; + } + dict_append_array(&dict, "Adapters", DBUS_TYPE_OBJECT_PATH, &array, i); + g_free(array); + + dbus_message_iter_close_container(&iter, &dict); + + return reply; +} + +static GDBusMethodTable manager_methods[] = { + { "GetProperties", "", "a{sv}",get_properties }, + { "DefaultAdapter", "", "o", default_adapter }, + { "FindAdapter", "s", "o", find_adapter }, + { "ListAdapters", "", "ao", list_adapters, + G_DBUS_METHOD_FLAG_DEPRECATED}, + { } +}; + +static GDBusSignalTable manager_signals[] = { + { "PropertyChanged", "sv" }, + { "AdapterAdded", "o" }, + { "AdapterRemoved", "o" }, + { "DefaultAdapterChanged", "o" }, + { } +}; + +dbus_bool_t manager_init(DBusConnection *conn, const char *path) +{ + connection = conn; + + snprintf(base_path, sizeof(base_path), "/org/bluez/%d", getpid()); + + return g_dbus_register_interface(conn, "/", MANAGER_INTERFACE, + manager_methods, manager_signals, + NULL, NULL, NULL); +} + +static void manager_update_adapters(void) +{ + GSList *list; + char **array; + int i; + + array = g_new0(char *, g_slist_length(adapters) + 1); + for (i = 0, list = adapters; list; list = list->next) { + struct btd_adapter *adapter = list->data; + + array[i] = (char *) adapter_get_path(adapter); + i++; + } + + emit_array_property_changed(connection, "/", + MANAGER_INTERFACE, "Adapters", + DBUS_TYPE_OBJECT_PATH, &array, i); + + g_free(array); +} + +static void manager_set_default_adapter(int id) +{ + struct btd_adapter *adapter; + const gchar *path; + + default_adapter_id = id; + + adapter = manager_find_adapter_by_id(id); + if (!adapter) + return; + + path = adapter_get_path(adapter); + + g_dbus_emit_signal(connection, "/", + MANAGER_INTERFACE, + "DefaultAdapterChanged", + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); +} + +static void manager_remove_adapter(struct btd_adapter *adapter) +{ + uint16_t dev_id = adapter_get_dev_id(adapter); + const gchar *path = adapter_get_path(adapter); + + adapters = g_slist_remove(adapters, adapter); + + manager_update_adapters(); + + if (default_adapter_id == dev_id || default_adapter_id < 0) { + int new_default = hci_get_route(NULL); + + manager_set_default_adapter(new_default); + } + + g_dbus_emit_signal(connection, "/", + MANAGER_INTERFACE, "AdapterRemoved", + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + adapter_remove(adapter); + + if (adapters == NULL) + btd_start_exit_timer(); +} + +void manager_cleanup(DBusConnection *conn, const char *path) +{ + g_slist_foreach(adapters, (GFunc) manager_remove_adapter, NULL); + g_slist_free(adapters); + + g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE); +} + +static gint adapter_id_cmp(gconstpointer a, gconstpointer b) +{ + struct btd_adapter *adapter = (struct btd_adapter *) a; + uint16_t id = GPOINTER_TO_UINT(b); + uint16_t dev_id = adapter_get_dev_id(adapter); + + return dev_id == id ? 0 : -1; +} + +static gint adapter_cmp(gconstpointer a, gconstpointer b) +{ + struct btd_adapter *adapter = (struct btd_adapter *) a; + const bdaddr_t *bdaddr = b; + bdaddr_t src; + + adapter_get_address(adapter, &src); + + return bacmp(&src, bdaddr); +} + +struct btd_adapter *manager_find_adapter(const bdaddr_t *sba) +{ + GSList *match; + + match = g_slist_find_custom(adapters, sba, adapter_cmp); + if (!match) + return NULL; + + return match->data; +} + +struct btd_adapter *manager_find_adapter_by_id(int id) +{ + GSList *match; + + match = g_slist_find_custom(adapters, GINT_TO_POINTER(id), + adapter_id_cmp); + if (!match) + return NULL; + + return match->data; +} + +void manager_foreach_adapter(adapter_cb func, gpointer user_data) +{ + g_slist_foreach(adapters, (GFunc) func, user_data); +} + +GSList *manager_get_adapters(void) +{ + return adapters; +} + +void manager_add_adapter(const char *path) +{ + g_dbus_emit_signal(connection, "/", + MANAGER_INTERFACE, "AdapterAdded", + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + manager_update_adapters(); + + btd_stop_exit_timer(); +} + +struct btd_adapter *btd_manager_register_adapter(int id) +{ + struct btd_adapter *adapter; + const char *path; + + adapter = manager_find_adapter_by_id(id); + if (adapter) { + error("Unable to register adapter: hci%d already exist", id); + return NULL; + } + + adapter = adapter_create(connection, id); + if (!adapter) + return NULL; + + adapters = g_slist_append(adapters, adapter); + + if (!adapter_init(adapter)) { + btd_adapter_unref(adapter); + return NULL; + } + + path = adapter_get_path(adapter); + g_dbus_emit_signal(connection, "/", + MANAGER_INTERFACE, "AdapterAdded", + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + manager_update_adapters(); + + btd_stop_exit_timer(); + + if (default_adapter_id < 0) + manager_set_default_adapter(id); + + DBG("Adapter %s registered", path); + + return btd_adapter_ref(adapter); +} + +int btd_manager_unregister_adapter(int id) +{ + struct btd_adapter *adapter; + const gchar *path; + + adapter = manager_find_adapter_by_id(id); + if (!adapter) + return -1; + + path = adapter_get_path(adapter); + + info("Unregister path: %s", path); + + manager_remove_adapter(adapter); + + return 0; +} + +void btd_manager_set_did(uint16_t vendor, uint16_t product, uint16_t version) +{ + GSList *l; + + for (l = adapters; l != NULL; l = g_slist_next(l)) { + struct btd_adapter *adapter = l->data; + + btd_adapter_set_did(adapter, vendor, product, version); + } +} |