Home | History | Annotate | Download | only in src
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2006-2010  Nokia Corporation
      6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      7  *
      8  *
      9  *  This program is free software; you can redistribute it and/or modify
     10  *  it under the terms of the GNU General Public License as published by
     11  *  the Free Software Foundation; either version 2 of the License, or
     12  *  (at your option) any later version.
     13  *
     14  *  This program is distributed in the hope that it will be useful,
     15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  *  GNU General Public License for more details.
     18  *
     19  *  You should have received a copy of the GNU General Public License
     20  *  along with this program; if not, write to the Free Software
     21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     22  *
     23  */
     24 
     25 #ifdef HAVE_CONFIG_H
     26 #include <config.h>
     27 #endif
     28 
     29 #include <stdlib.h>
     30 #include <stdio.h>
     31 #include <errno.h>
     32 #include <unistd.h>
     33 #include <sys/ioctl.h>
     34 #include <sys/socket.h>
     35 
     36 #include <bluetooth/bluetooth.h>
     37 
     38 #include <glib.h>
     39 
     40 #include <dbus/dbus.h>
     41 
     42 #include <gdbus.h>
     43 
     44 #include "hcid.h"
     45 #include "dbus-common.h"
     46 #include "log.h"
     47 #include "adapter.h"
     48 #include "error.h"
     49 #include "manager.h"
     50 
     51 static char base_path[50] = "/org/bluez";
     52 
     53 static DBusConnection *connection = NULL;
     54 static int default_adapter_id = -1;
     55 static GSList *adapters = NULL;
     56 
     57 const char *manager_get_base_path(void)
     58 {
     59 	return base_path;
     60 }
     61 
     62 static DBusMessage *default_adapter(DBusConnection *conn,
     63 					DBusMessage *msg, void *data)
     64 {
     65 	DBusMessage *reply;
     66 	struct btd_adapter *adapter;
     67 	const gchar *path;
     68 
     69 	adapter = manager_find_adapter_by_id(default_adapter_id);
     70 	if (!adapter)
     71 		return btd_error_no_such_adapter(msg);
     72 
     73 	reply = dbus_message_new_method_return(msg);
     74 	if (!reply)
     75 		return NULL;
     76 
     77 	path = adapter_get_path(adapter);
     78 
     79 	dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
     80 				DBUS_TYPE_INVALID);
     81 
     82 	return reply;
     83 }
     84 
     85 static DBusMessage *find_adapter(DBusConnection *conn,
     86 					DBusMessage *msg, void *data)
     87 {
     88 	DBusMessage *reply;
     89 	struct btd_adapter *adapter;
     90 	const char *pattern;
     91 	int dev_id;
     92 	const gchar *path;
     93 
     94 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
     95 							DBUS_TYPE_INVALID))
     96 		return NULL;
     97 
     98 	/* hci_devid() would make sense to use here, except it is
     99 	 * restricted to devices which are up */
    100 	if (!strcmp(pattern, "any") || !strcmp(pattern, "00:00:00:00:00:00")) {
    101 		path = adapter_any_get_path();
    102 		if (path != NULL)
    103 			goto done;
    104 		return btd_error_no_such_adapter(msg);
    105 	} else if (!strncmp(pattern, "hci", 3) && strlen(pattern) >= 4) {
    106 		dev_id = atoi(pattern + 3);
    107 		adapter = manager_find_adapter_by_id(dev_id);
    108 	} else {
    109 		bdaddr_t bdaddr;
    110 		str2ba(pattern, &bdaddr);
    111 		adapter = manager_find_adapter(&bdaddr);
    112 	}
    113 
    114 	if (!adapter)
    115 		return btd_error_no_such_adapter(msg);
    116 
    117 	path = adapter_get_path(adapter);
    118 
    119 done:
    120 	reply = dbus_message_new_method_return(msg);
    121 	if (!reply)
    122 		return NULL;
    123 
    124 	dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
    125 							DBUS_TYPE_INVALID);
    126 
    127 	return reply;
    128 }
    129 
    130 static DBusMessage *list_adapters(DBusConnection *conn,
    131 					DBusMessage *msg, void *data)
    132 {
    133 	DBusMessageIter iter;
    134 	DBusMessageIter array_iter;
    135 	DBusMessage *reply;
    136 	GSList *l;
    137 
    138 	reply = dbus_message_new_method_return(msg);
    139 	if (!reply)
    140 		return NULL;
    141 
    142 	dbus_message_iter_init_append(reply, &iter);
    143 
    144 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
    145 				DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
    146 
    147 	for (l = adapters; l; l = l->next) {
    148 		struct btd_adapter *adapter = l->data;
    149 		const gchar *path = adapter_get_path(adapter);
    150 
    151 		dbus_message_iter_append_basic(&array_iter,
    152 					DBUS_TYPE_OBJECT_PATH, &path);
    153 	}
    154 
    155 	dbus_message_iter_close_container(&iter, &array_iter);
    156 
    157 	return reply;
    158 }
    159 
    160 static DBusMessage *get_properties(DBusConnection *conn,
    161 					DBusMessage *msg, void *data)
    162 {
    163 	DBusMessage *reply;
    164 	DBusMessageIter iter;
    165 	DBusMessageIter dict;
    166 	GSList *list;
    167 	char **array;
    168 	int i;
    169 
    170 	reply = dbus_message_new_method_return(msg);
    171 	if (!reply)
    172 		return NULL;
    173 
    174 	dbus_message_iter_init_append(reply, &iter);
    175 
    176 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
    177 			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
    178 			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
    179 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
    180 
    181 	array = g_new0(char *, g_slist_length(adapters) + 1);
    182 	for (i = 0, list = adapters; list; list = list->next) {
    183 		struct btd_adapter *adapter = list->data;
    184 
    185 		array[i] = (char *) adapter_get_path(adapter);
    186 		i++;
    187 	}
    188 	dict_append_array(&dict, "Adapters", DBUS_TYPE_OBJECT_PATH, &array, i);
    189 	g_free(array);
    190 
    191 	dbus_message_iter_close_container(&iter, &dict);
    192 
    193 	return reply;
    194 }
    195 
    196 static GDBusMethodTable manager_methods[] = {
    197 	{ "GetProperties",	"",	"a{sv}",get_properties	},
    198 	{ "DefaultAdapter",	"",	"o",	default_adapter	},
    199 	{ "FindAdapter",	"s",	"o",	find_adapter	},
    200 	{ "ListAdapters",	"",	"ao",	list_adapters,
    201 						G_DBUS_METHOD_FLAG_DEPRECATED},
    202 	{ }
    203 };
    204 
    205 static GDBusSignalTable manager_signals[] = {
    206 	{ "PropertyChanged",		"sv"	},
    207 	{ "AdapterAdded",		"o"	},
    208 	{ "AdapterRemoved",		"o"	},
    209 	{ "DefaultAdapterChanged",	"o"	},
    210 	{ }
    211 };
    212 
    213 dbus_bool_t manager_init(DBusConnection *conn, const char *path)
    214 {
    215 	connection = conn;
    216 
    217 	snprintf(base_path, sizeof(base_path), "/org/bluez/%d", getpid());
    218 
    219 	return g_dbus_register_interface(conn, "/", MANAGER_INTERFACE,
    220 					manager_methods, manager_signals,
    221 					NULL, NULL, NULL);
    222 }
    223 
    224 static void manager_update_adapters(void)
    225 {
    226 	GSList *list;
    227 	char **array;
    228 	int i;
    229 
    230 	array = g_new0(char *, g_slist_length(adapters) + 1);
    231 	for (i = 0, list = adapters; list; list = list->next) {
    232 		struct btd_adapter *adapter = list->data;
    233 
    234 		array[i] = (char *) adapter_get_path(adapter);
    235 		i++;
    236 	}
    237 
    238 	emit_array_property_changed(connection, "/",
    239 					MANAGER_INTERFACE, "Adapters",
    240 					DBUS_TYPE_OBJECT_PATH, &array, i);
    241 
    242 	g_free(array);
    243 }
    244 
    245 static void manager_set_default_adapter(int id)
    246 {
    247 	struct btd_adapter *adapter;
    248 	const gchar *path;
    249 
    250 	default_adapter_id = id;
    251 
    252 	adapter = manager_find_adapter_by_id(id);
    253 	if (!adapter)
    254 		return;
    255 
    256 	path = adapter_get_path(adapter);
    257 
    258 	g_dbus_emit_signal(connection, "/",
    259 			MANAGER_INTERFACE,
    260 			"DefaultAdapterChanged",
    261 			DBUS_TYPE_OBJECT_PATH, &path,
    262 			DBUS_TYPE_INVALID);
    263 }
    264 
    265 static void manager_remove_adapter(struct btd_adapter *adapter)
    266 {
    267 	uint16_t dev_id = adapter_get_dev_id(adapter);
    268 	const gchar *path = adapter_get_path(adapter);
    269 
    270 	adapters = g_slist_remove(adapters, adapter);
    271 
    272 	manager_update_adapters();
    273 
    274 	if (default_adapter_id == dev_id || default_adapter_id < 0) {
    275 		int new_default = hci_get_route(NULL);
    276 
    277 		manager_set_default_adapter(new_default);
    278 	}
    279 
    280 	g_dbus_emit_signal(connection, "/",
    281 			MANAGER_INTERFACE, "AdapterRemoved",
    282 			DBUS_TYPE_OBJECT_PATH, &path,
    283 			DBUS_TYPE_INVALID);
    284 
    285 	adapter_remove(adapter);
    286 
    287 	if (adapters == NULL)
    288 		btd_start_exit_timer();
    289 }
    290 
    291 void manager_cleanup(DBusConnection *conn, const char *path)
    292 {
    293 	g_slist_foreach(adapters, (GFunc) manager_remove_adapter, NULL);
    294 	g_slist_free(adapters);
    295 
    296 	g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
    297 }
    298 
    299 static gint adapter_id_cmp(gconstpointer a, gconstpointer b)
    300 {
    301 	struct btd_adapter *adapter = (struct btd_adapter *) a;
    302 	uint16_t id = GPOINTER_TO_UINT(b);
    303 	uint16_t dev_id = adapter_get_dev_id(adapter);
    304 
    305 	return dev_id == id ? 0 : -1;
    306 }
    307 
    308 static gint adapter_cmp(gconstpointer a, gconstpointer b)
    309 {
    310 	struct btd_adapter *adapter = (struct btd_adapter *) a;
    311 	const bdaddr_t *bdaddr = b;
    312 	bdaddr_t src;
    313 
    314 	adapter_get_address(adapter, &src);
    315 
    316 	return bacmp(&src, bdaddr);
    317 }
    318 
    319 struct btd_adapter *manager_find_adapter(const bdaddr_t *sba)
    320 {
    321 	GSList *match;
    322 
    323 	match = g_slist_find_custom(adapters, sba, adapter_cmp);
    324 	if (!match)
    325 		return NULL;
    326 
    327 	return match->data;
    328 }
    329 
    330 struct btd_adapter *manager_find_adapter_by_id(int id)
    331 {
    332 	GSList *match;
    333 
    334 	match = g_slist_find_custom(adapters, GINT_TO_POINTER(id),
    335 							adapter_id_cmp);
    336 	if (!match)
    337 		return NULL;
    338 
    339 	return match->data;
    340 }
    341 
    342 void manager_foreach_adapter(adapter_cb func, gpointer user_data)
    343 {
    344 	g_slist_foreach(adapters, (GFunc) func, user_data);
    345 }
    346 
    347 GSList *manager_get_adapters(void)
    348 {
    349 	return adapters;
    350 }
    351 
    352 void manager_add_adapter(const char *path)
    353 {
    354 	g_dbus_emit_signal(connection, "/",
    355 				MANAGER_INTERFACE, "AdapterAdded",
    356 				DBUS_TYPE_OBJECT_PATH, &path,
    357 				DBUS_TYPE_INVALID);
    358 
    359 	manager_update_adapters();
    360 
    361 	btd_stop_exit_timer();
    362 }
    363 
    364 struct btd_adapter *btd_manager_register_adapter(int id)
    365 {
    366 	struct btd_adapter *adapter;
    367 	const char *path;
    368 
    369 	adapter = manager_find_adapter_by_id(id);
    370 	if (adapter) {
    371 		error("Unable to register adapter: hci%d already exist", id);
    372 		return NULL;
    373 	}
    374 
    375 	adapter = adapter_create(connection, id);
    376 	if (!adapter)
    377 		return NULL;
    378 
    379 	adapters = g_slist_append(adapters, adapter);
    380 
    381 	if (!adapter_init(adapter)) {
    382 		btd_adapter_unref(adapter);
    383 		return NULL;
    384 	}
    385 
    386 	path = adapter_get_path(adapter);
    387 	g_dbus_emit_signal(connection, "/",
    388 				MANAGER_INTERFACE, "AdapterAdded",
    389 				DBUS_TYPE_OBJECT_PATH, &path,
    390 				DBUS_TYPE_INVALID);
    391 
    392 	manager_update_adapters();
    393 
    394 	btd_stop_exit_timer();
    395 
    396 	if (default_adapter_id < 0)
    397 		manager_set_default_adapter(id);
    398 
    399 	DBG("Adapter %s registered", path);
    400 
    401 	return btd_adapter_ref(adapter);
    402 }
    403 
    404 int btd_manager_unregister_adapter(int id)
    405 {
    406 	struct btd_adapter *adapter;
    407 	const gchar *path;
    408 
    409 	adapter = manager_find_adapter_by_id(id);
    410 	if (!adapter)
    411 		return -1;
    412 
    413 	path = adapter_get_path(adapter);
    414 
    415 	info("Unregister path: %s", path);
    416 
    417 	manager_remove_adapter(adapter);
    418 
    419 	return 0;
    420 }
    421 
    422 void btd_manager_set_did(uint16_t vendor, uint16_t product, uint16_t version)
    423 {
    424 	GSList *l;
    425 
    426 	for (l = adapters; l != NULL; l = g_slist_next(l)) {
    427 		struct btd_adapter *adapter = l->data;
    428 
    429 		btd_adapter_set_did(adapter, vendor, product, version);
    430 	}
    431 }
    432