Home | History | Annotate | Download | only in src
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2006-2007  Nokia Corporation
      6  *  Copyright (C) 2004-2009  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 #include <bluetooth/hci.h>
     38 #include <bluetooth/hci_lib.h>
     39 
     40 #include <glib.h>
     41 
     42 #include <dbus/dbus.h>
     43 
     44 #include <gdbus.h>
     45 
     46 #include "hcid.h"
     47 #include "dbus-common.h"
     48 #include "logging.h"
     49 #include "adapter.h"
     50 #include "error.h"
     51 #include "manager.h"
     52 
     53 static char base_path[50] = "/org/bluez";
     54 
     55 static DBusConnection *connection = NULL;
     56 static int default_adapter_id = -1;
     57 static GSList *adapters = NULL;
     58 
     59 const char *manager_get_base_path(void)
     60 {
     61 	return base_path;
     62 }
     63 
     64 void manager_update_svc(const bdaddr_t *bdaddr, uint8_t svc)
     65 {
     66 	GSList *l;
     67 	bdaddr_t src;
     68 
     69 	for (l = adapters; l != NULL; l = l->next) {
     70 		struct btd_adapter *adapter = l->data;
     71 
     72 		adapter_get_address(adapter, &src);
     73 
     74 		if (bacmp(bdaddr, BDADDR_ANY) != 0 && bacmp(bdaddr, &src) != 0)
     75 			continue;
     76 
     77 		adapter_update(adapter, svc);
     78 	}
     79 }
     80 
     81 int manager_get_adapter_class(uint16_t dev_id, uint8_t *cls)
     82 {
     83 	struct btd_adapter *adapter;
     84 
     85 	adapter = manager_find_adapter_by_id(dev_id);
     86 	if (!adapter)
     87 		return -EINVAL;
     88 
     89 	return adapter_get_class(adapter, cls);
     90 }
     91 
     92 static inline DBusMessage *invalid_args(DBusMessage *msg)
     93 {
     94 	return g_dbus_create_error(msg,
     95 			ERROR_INTERFACE ".InvalidArguments",
     96 			"Invalid arguments in method call");
     97 }
     98 
     99 static inline DBusMessage *no_such_adapter(DBusMessage *msg)
    100 {
    101 	return g_dbus_create_error(msg,
    102 			ERROR_INTERFACE ".NoSuchAdapter",
    103 			"No such adapter");
    104 }
    105 
    106 static DBusMessage *default_adapter(DBusConnection *conn,
    107 					DBusMessage *msg, void *data)
    108 {
    109 	DBusMessage *reply;
    110 	struct btd_adapter *adapter;
    111 	const gchar *path;
    112 
    113 	adapter = manager_find_adapter_by_id(default_adapter_id);
    114 	if (!adapter)
    115 		return no_such_adapter(msg);
    116 
    117 	reply = dbus_message_new_method_return(msg);
    118 	if (!reply)
    119 		return NULL;
    120 
    121 	path = adapter_get_path(adapter);
    122 
    123 	dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
    124 				DBUS_TYPE_INVALID);
    125 
    126 	return reply;
    127 }
    128 
    129 static DBusMessage *find_adapter(DBusConnection *conn,
    130 					DBusMessage *msg, void *data)
    131 {
    132 	DBusMessage *reply;
    133 	struct btd_adapter *adapter;
    134 	const char *pattern;
    135 	int dev_id;
    136 	const gchar *path;
    137 
    138 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
    139 							DBUS_TYPE_INVALID))
    140 		return NULL;
    141 
    142 	/* hci_devid() would make sense to use here, except it
    143 	   is restricted to devices which are up */
    144 	if (!strcmp(pattern, "any") || !strcmp(pattern, "00:00:00:00:00:00")) {
    145 		path = adapter_any_get_path();
    146 		if (path != NULL)
    147 			goto done;
    148 		return no_such_adapter(msg);
    149 	} else if (!strncmp(pattern, "hci", 3) && strlen(pattern) >= 4) {
    150 		dev_id = atoi(pattern + 3);
    151 		adapter = manager_find_adapter_by_id(dev_id);
    152 	} else
    153 		adapter = manager_find_adapter_by_address(pattern);
    154 
    155 	if (!adapter)
    156 		return no_such_adapter(msg);
    157 
    158 	path = adapter_get_path(adapter);
    159 
    160 done:
    161 	reply = dbus_message_new_method_return(msg);
    162 	if (!reply)
    163 		return NULL;
    164 
    165 	dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
    166 							DBUS_TYPE_INVALID);
    167 
    168 	return reply;
    169 }
    170 
    171 static DBusMessage *list_adapters(DBusConnection *conn,
    172 					DBusMessage *msg, void *data)
    173 {
    174 	DBusMessageIter iter;
    175 	DBusMessageIter array_iter;
    176 	DBusMessage *reply;
    177 	GSList *l;
    178 
    179 	reply = dbus_message_new_method_return(msg);
    180 	if (!reply)
    181 		return NULL;
    182 
    183 	dbus_message_iter_init_append(reply, &iter);
    184 
    185 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
    186 				DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
    187 
    188 	for (l = adapters; l; l = l->next) {
    189 		struct btd_adapter *adapter = l->data;
    190 		const gchar *path = adapter_get_path(adapter);
    191 
    192 		dbus_message_iter_append_basic(&array_iter,
    193 					DBUS_TYPE_OBJECT_PATH, &path);
    194 	}
    195 
    196 	dbus_message_iter_close_container(&iter, &array_iter);
    197 
    198 	return reply;
    199 }
    200 
    201 static DBusMessage *get_properties(DBusConnection *conn,
    202 					DBusMessage *msg, void *data)
    203 {
    204 	DBusMessage *reply;
    205 	DBusMessageIter iter;
    206 	DBusMessageIter dict;
    207 	GSList *list;
    208 	char **array;
    209 	int i;
    210 
    211 	reply = dbus_message_new_method_return(msg);
    212 	if (!reply)
    213 		return NULL;
    214 
    215 	dbus_message_iter_init_append(reply, &iter);
    216 
    217 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
    218 			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
    219 			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
    220 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
    221 
    222 	array = g_new0(char *, g_slist_length(adapters) + 1);
    223 	for (i = 0, list = adapters; list; list = list->next, i++) {
    224 		struct btd_adapter *adapter = list->data;
    225 
    226 		if (!adapter_is_ready(adapter))
    227 			continue;
    228 
    229 		array[i] = (char *) adapter_get_path(adapter);
    230 	}
    231 	dict_append_array(&dict, "Adapters", DBUS_TYPE_OBJECT_PATH, &array, i);
    232 	g_free(array);
    233 
    234 	dbus_message_iter_close_container(&iter, &dict);
    235 
    236 	return reply;
    237 }
    238 
    239 static GDBusMethodTable manager_methods[] = {
    240 	{ "GetProperties",	"",	"a{sv}",get_properties	},
    241 	{ "DefaultAdapter",	"",	"o",	default_adapter	},
    242 	{ "FindAdapter",	"s",	"o",	find_adapter	},
    243 	{ "ListAdapters",	"",	"ao",	list_adapters	},
    244 	{ }
    245 };
    246 
    247 static GDBusSignalTable manager_signals[] = {
    248 	{ "PropertyChanged",		"sv"	},
    249 	{ "AdapterAdded",		"o"	},
    250 	{ "AdapterRemoved",		"o"	},
    251 	{ "DefaultAdapterChanged",	"o"	},
    252 	{ }
    253 };
    254 
    255 dbus_bool_t manager_init(DBusConnection *conn, const char *path)
    256 {
    257 	connection = conn;
    258 
    259 	snprintf(base_path, sizeof(base_path), "/org/bluez/%d", getpid());
    260 
    261 	return g_dbus_register_interface(conn, "/", MANAGER_INTERFACE,
    262 			manager_methods, manager_signals,
    263 			NULL, NULL, NULL);
    264 }
    265 
    266 static void manager_update_adapters(void)
    267 {
    268 	GSList *list;
    269 	char **array;
    270 	int i;
    271 
    272 	array = g_new0(char *, g_slist_length(adapters) + 1);
    273 	for (i = 0, list = adapters; list; list = list->next, i++) {
    274 		struct btd_adapter *adapter = list->data;
    275 
    276 		if (!adapter_is_ready(adapter))
    277 			continue;
    278 
    279 		array[i] = (char *) adapter_get_path(adapter);
    280 	}
    281 
    282 	emit_array_property_changed(connection, "/",
    283 					MANAGER_INTERFACE, "Adapters",
    284 					DBUS_TYPE_OBJECT_PATH, &array);
    285 
    286 	g_free(array);
    287 }
    288 
    289 static void manager_remove_adapter(struct btd_adapter *adapter)
    290 {
    291 	uint16_t dev_id = adapter_get_dev_id(adapter);
    292 	const gchar *path = adapter_get_path(adapter);
    293 
    294 	adapters = g_slist_remove(adapters, adapter);
    295 
    296 	manager_update_adapters();
    297 
    298 	if (default_adapter_id == dev_id || default_adapter_id < 0) {
    299 		int new_default = hci_get_route(NULL);
    300 
    301 		manager_set_default_adapter(new_default);
    302 	}
    303 
    304 	g_dbus_emit_signal(connection, "/",
    305 			MANAGER_INTERFACE, "AdapterRemoved",
    306 			DBUS_TYPE_OBJECT_PATH, &path,
    307 			DBUS_TYPE_INVALID);
    308 
    309 	adapter_remove(adapter);
    310 
    311 	if (adapters == NULL)
    312 		btd_start_exit_timer();
    313 }
    314 
    315 void manager_cleanup(DBusConnection *conn, const char *path)
    316 {
    317 	g_slist_foreach(adapters, (GFunc) manager_remove_adapter, NULL);
    318 	g_slist_free(adapters);
    319 
    320 	g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
    321 }
    322 
    323 static gint adapter_id_cmp(gconstpointer a, gconstpointer b)
    324 {
    325 	struct btd_adapter *adapter = (struct btd_adapter *) a;
    326 	uint16_t id = GPOINTER_TO_UINT(b);
    327 	uint16_t dev_id = adapter_get_dev_id(adapter);
    328 
    329 	return dev_id == id ? 0 : -1;
    330 }
    331 
    332 static gint adapter_path_cmp(gconstpointer a, gconstpointer b)
    333 {
    334 	struct btd_adapter *adapter = (struct btd_adapter *) a;
    335 	const char *path = b;
    336 	const gchar *adapter_path = adapter_get_path(adapter);
    337 
    338 	return strcmp(adapter_path, path);
    339 }
    340 
    341 static gint adapter_cmp(gconstpointer a, gconstpointer b)
    342 {
    343 	struct btd_adapter *adapter = (struct btd_adapter *) a;
    344 	const bdaddr_t *bdaddr = b;
    345 	bdaddr_t src;
    346 
    347 	adapter_get_address(adapter, &src);
    348 
    349 	return bacmp(&src, bdaddr);
    350 }
    351 
    352 static gint adapter_address_cmp(gconstpointer a, gconstpointer b)
    353 {
    354 	struct btd_adapter *adapter = (struct btd_adapter *) a;
    355 	const char *address = b;
    356 	bdaddr_t bdaddr;
    357 	char addr[18];
    358 
    359 	adapter_get_address(adapter, &bdaddr);
    360 	ba2str(&bdaddr, addr);
    361 
    362 	return strcmp(addr, address);
    363 }
    364 
    365 struct btd_adapter *manager_find_adapter(const bdaddr_t *sba)
    366 {
    367 	GSList *match;
    368 
    369 	match = g_slist_find_custom(adapters, sba, adapter_cmp);
    370 	if (!match)
    371 		return NULL;
    372 
    373 	return match->data;
    374 }
    375 
    376 struct btd_adapter *manager_find_adapter_by_address(const char *address)
    377 {
    378 	GSList *match;
    379 
    380 	match = g_slist_find_custom(adapters, address, adapter_address_cmp);
    381 	if (!match)
    382 		return NULL;
    383 
    384 	return match->data;
    385 }
    386 
    387 struct btd_adapter *manager_find_adapter_by_path(const char *path)
    388 {
    389 	GSList *match;
    390 
    391 	match = g_slist_find_custom(adapters, path, adapter_path_cmp);
    392 	if (!match)
    393 		return NULL;
    394 
    395 	return match->data;
    396 }
    397 
    398 struct btd_adapter *manager_find_adapter_by_id(int id)
    399 {
    400 	GSList *match;
    401 
    402 	match = g_slist_find_custom(adapters, GINT_TO_POINTER(id), adapter_id_cmp);
    403 	if (!match)
    404 		return NULL;
    405 
    406 	return match->data;
    407 }
    408 
    409 GSList *manager_get_adapters(void)
    410 {
    411 	return adapters;
    412 }
    413 
    414 void manager_add_adapter(const char *path)
    415 {
    416 	g_dbus_emit_signal(connection, "/",
    417 			MANAGER_INTERFACE, "AdapterAdded",
    418 			DBUS_TYPE_OBJECT_PATH, &path,
    419 			DBUS_TYPE_INVALID);
    420 
    421 	manager_update_adapters();
    422 
    423 	btd_stop_exit_timer();
    424 }
    425 
    426 int manager_register_adapter(int id, gboolean devup)
    427 {
    428 	struct btd_adapter *adapter;
    429 
    430 	adapter = manager_find_adapter_by_id(id);
    431 	if (adapter) {
    432 		error("Unable to register adapter: hci%d already exist", id);
    433 		return -1;
    434 	}
    435 
    436 	adapter = adapter_create(connection, id, devup);
    437 	if (!adapter)
    438 		return -1;
    439 
    440 	adapters = g_slist_append(adapters, adapter);
    441 
    442 	return 0;
    443 }
    444 
    445 int manager_unregister_adapter(int id)
    446 {
    447 	struct btd_adapter *adapter;
    448 	const gchar *path;
    449 
    450 	adapter = manager_find_adapter_by_id(id);
    451 	if (!adapter)
    452 		return -1;
    453 
    454 	path = adapter_get_path(adapter);
    455 
    456 	info("Unregister path: %s", path);
    457 
    458 	manager_remove_adapter(adapter);
    459 
    460 	return 0;
    461 }
    462 
    463 int manager_start_adapter(int id)
    464 {
    465 	struct btd_adapter* adapter;
    466 	int ret;
    467 
    468 	adapter = manager_find_adapter_by_id(id);
    469 	if (!adapter) {
    470 		error("Getting device data failed: hci%d", id);
    471 		return -EINVAL;
    472 	}
    473 
    474 	ret = adapter_start(adapter);
    475 	if (ret < 0)
    476 		return ret;
    477 
    478 	if (default_adapter_id < 0)
    479 		manager_set_default_adapter(id);
    480 
    481 	return ret;
    482 }
    483 
    484 int manager_stop_adapter(int id)
    485 {
    486 	struct btd_adapter *adapter;
    487 
    488 	adapter = manager_find_adapter_by_id(id);
    489 	if (!adapter) {
    490 		error("Getting device data failed: hci%d", id);
    491 		return -EINVAL;
    492 	}
    493 
    494 	return adapter_stop(adapter);
    495 }
    496 
    497 int manager_get_default_adapter()
    498 {
    499 	return default_adapter_id;
    500 }
    501 
    502 void manager_set_default_adapter(int id)
    503 {
    504 	struct btd_adapter *adapter;
    505 	const gchar *path;
    506 
    507 	default_adapter_id = id;
    508 
    509 	adapter = manager_find_adapter_by_id(id);
    510 	if (!adapter)
    511 		return;
    512 
    513 	path = adapter_get_path(adapter);
    514 
    515 	g_dbus_emit_signal(connection, "/",
    516 			MANAGER_INTERFACE,
    517 			"DefaultAdapterChanged",
    518 			DBUS_TYPE_OBJECT_PATH, &path,
    519 			DBUS_TYPE_INVALID);
    520 }
    521 
    522 void btd_manager_set_offline(gboolean offline)
    523 {
    524 	GSList *l;
    525 
    526 	for (l = adapters; l != NULL; l = g_slist_next(l)) {
    527 		struct btd_adapter *adapter = l->data;
    528 
    529 		if (offline)
    530 			btd_adapter_switch_offline(adapter);
    531 		else
    532 			btd_adapter_restore_powered(adapter);
    533 	}
    534 }
    535