• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
manager_get_base_path(void)59 const char *manager_get_base_path(void)
60 {
61 	return base_path;
62 }
63 
manager_update_svc(struct btd_adapter * adapter,uint8_t svc)64 void manager_update_svc(struct btd_adapter* adapter, uint8_t svc)
65 {
66 	adapter_update(adapter, svc);
67 }
68 
invalid_args(DBusMessage * msg)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 
no_such_adapter(DBusMessage * msg)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 
default_adapter(DBusConnection * conn,DBusMessage * msg,void * data)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 
find_adapter(DBusConnection * conn,DBusMessage * msg,void * data)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 
list_adapters(DBusConnection * conn,DBusMessage * msg,void * data)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 
get_properties(DBusConnection * conn,DBusMessage * msg,void * data)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 
manager_init(DBusConnection * conn,const char * path)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 
manager_update_adapters(void)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 
manager_remove_adapter(struct btd_adapter * adapter)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 
manager_cleanup(DBusConnection * conn,const char * path)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 
adapter_id_cmp(gconstpointer a,gconstpointer b)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 
adapter_path_cmp(gconstpointer a,gconstpointer b)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 
adapter_cmp(gconstpointer a,gconstpointer b)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 
adapter_address_cmp(gconstpointer a,gconstpointer b)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 
manager_find_adapter(const bdaddr_t * sba)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 
manager_find_adapter_by_address(const char * address)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 
manager_find_adapter_by_path(const char * path)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 
manager_find_adapter_by_id(int id)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 
manager_get_adapters(void)388 GSList *manager_get_adapters(void)
389 {
390 	return adapters;
391 }
392 
manager_add_adapter(const char * path)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 
manager_register_adapter(int id,gboolean devup)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 
manager_unregister_adapter(int id)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 
manager_start_adapter(int id)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 
manager_stop_adapter(int id)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 
manager_get_default_adapter()476 int manager_get_default_adapter()
477 {
478 	return default_adapter_id;
479 }
480 
manager_set_default_adapter(int id)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 
btd_manager_set_offline(gboolean offline)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