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