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
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
manager_get_base_path(void)57 const char *manager_get_base_path(void)
58 {
59 return base_path;
60 }
61
default_adapter(DBusConnection * conn,DBusMessage * msg,void * data)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
find_adapter(DBusConnection * conn,DBusMessage * msg,void * data)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
list_adapters(DBusConnection * conn,DBusMessage * msg,void * data)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
get_properties(DBusConnection * conn,DBusMessage * msg,void * data)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
manager_init(DBusConnection * conn,const char * path)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
manager_update_adapters(void)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
manager_set_default_adapter(int id)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
manager_remove_adapter(struct btd_adapter * adapter)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
manager_cleanup(DBusConnection * conn,const char * path)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
adapter_id_cmp(gconstpointer a,gconstpointer b)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
adapter_cmp(gconstpointer a,gconstpointer b)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
manager_find_adapter(const bdaddr_t * sba)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
manager_find_adapter_by_id(int id)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
manager_foreach_adapter(adapter_cb func,gpointer user_data)342 void manager_foreach_adapter(adapter_cb func, gpointer user_data)
343 {
344 g_slist_foreach(adapters, (GFunc) func, user_data);
345 }
346
manager_get_adapters(void)347 GSList *manager_get_adapters(void)
348 {
349 return adapters;
350 }
351
manager_add_adapter(const char * path)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
btd_manager_register_adapter(int id)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
btd_manager_unregister_adapter(int id)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
btd_manager_set_did(uint16_t vendor,uint16_t product,uint16_t version)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