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