]> code.delx.au - pulseaudio/blob - src/modules/bluetooth/bluetooth-util.c
3dfc65e7b97de877d3a934c74c2197af93a49755
[pulseaudio] / src / modules / bluetooth / bluetooth-util.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008 Joao Paulo Rechi Vita
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/shared.h>
28 #include <modules/dbus-util.h>
29
30 #include "bluetooth-util.h"
31
32 struct pa_bluetooth_discovery {
33 PA_REFCNT_DECLARE;
34
35 pa_core *core;
36 pa_dbus_connection *connection;
37 PA_LLIST_HEAD(pa_dbus_pending, pending);
38 pa_hashmap *devices;
39 pa_hook hook;
40 };
41
42 static pa_bluetooth_uuid *uuid_new(const char *uuid) {
43 pa_bluetooth_uuid *u;
44
45 u = pa_xnew(pa_bluetooth_uuid, 1);
46 u->uuid = pa_xstrdup(uuid);
47 PA_LLIST_INIT(pa_bluetooth_uuid, u);
48
49 return u;
50 }
51
52 static void uuid_free(pa_bluetooth_uuid *u) {
53 pa_assert(u);
54
55 pa_xfree(u->uuid);
56 pa_xfree(u);
57 }
58
59 static pa_bluetooth_device* device_new(const char *path) {
60 pa_bluetooth_device *d;
61
62 d = pa_xnew(pa_bluetooth_device, 1);
63
64 d->dead = FALSE;
65
66 d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0;
67
68 d->name = NULL;
69 d->path = pa_xstrdup(path);
70 d->paired = -1;
71 d->alias = NULL;
72 d->device_connected = -1;
73 PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
74 d->address = NULL;
75 d->class = -1;
76 d->trusted = -1;
77
78 d->audio_sink_connected = -1;
79
80 d->headset_connected = -1;
81
82 return d;
83 }
84
85 static void device_free(pa_bluetooth_device *d) {
86 pa_bluetooth_uuid *u;
87
88 pa_assert(d);
89
90 while ((u = d->uuids)) {
91 PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
92 uuid_free(u);
93 }
94
95 pa_xfree(d->name);
96 pa_xfree(d->path);
97 pa_xfree(d->alias);
98 pa_xfree(d->address);
99 pa_xfree(d);
100 }
101
102 static pa_bool_t device_is_loaded(pa_bluetooth_device *d) {
103 pa_assert(d);
104
105 return
106 d->device_info_valid &&
107 d->audio_sink_info_valid &&
108 d->headset_info_valid;
109 }
110
111 static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
112 pa_assert(d);
113
114 pa_assert(d->device_info_valid);
115 pa_assert(d->audio_sink_info_valid);
116 pa_assert(d->headset_info_valid);
117
118 return
119 d->device_info_valid > 0 &&
120 (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0);
121 }
122
123 static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
124 const char *key;
125 DBusMessageIter variant_i;
126
127 pa_assert(y);
128 pa_assert(d);
129 pa_assert(i);
130
131 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
132 pa_log("Property name not a string.");
133 return -1;
134 }
135
136 dbus_message_iter_get_basic(i, &key);
137
138 if (!dbus_message_iter_next(i)) {
139 pa_log("Property value missing");
140 return -1;
141 }
142
143 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
144 pa_log("Property value not a variant.");
145 return -1;
146 }
147
148 dbus_message_iter_recurse(i, &variant_i);
149
150 /* pa_log_debug("Parsing property org.bluez.Device.%s", key); */
151
152 switch (dbus_message_iter_get_arg_type(&variant_i)) {
153
154 case DBUS_TYPE_STRING: {
155
156 const char *value;
157 dbus_message_iter_get_basic(&variant_i, &value);
158
159 if (pa_streq(key, "Name")) {
160 pa_xfree(d->name);
161 d->name = pa_xstrdup(value);
162 } else if (pa_streq(key, "Alias")) {
163 pa_xfree(d->alias);
164 d->alias = pa_xstrdup(value);
165 } else if (pa_streq(key, "Address")) {
166 pa_xfree(d->address);
167 d->address = pa_xstrdup(value);
168 }
169
170 /* pa_log_debug("Value %s", value); */
171
172 break;
173 }
174
175 case DBUS_TYPE_BOOLEAN: {
176
177 dbus_bool_t value;
178 dbus_message_iter_get_basic(&variant_i, &value);
179
180 if (pa_streq(key, "Paired"))
181 d->paired = !!value;
182 else if (pa_streq(key, "Connected"))
183 d->device_connected = !!value;
184 else if (pa_streq(key, "Trusted"))
185 d->trusted = !!value;
186
187 /* pa_log_debug("Value %s", pa_yes_no(value)); */
188
189 break;
190 }
191
192 case DBUS_TYPE_UINT32: {
193
194 uint32_t value;
195 dbus_message_iter_get_basic(&variant_i, &value);
196
197 if (pa_streq(key, "Class"))
198 d->class = (int) value;
199
200 /* pa_log_debug("Value %u", (unsigned) value); */
201
202 break;
203 }
204
205 case DBUS_TYPE_ARRAY: {
206
207 DBusMessageIter ai;
208 dbus_message_iter_recurse(&variant_i, &ai);
209
210 if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
211 pa_streq(key, "UUIDs")) {
212
213 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
214 pa_bluetooth_uuid *node;
215 const char *value;
216
217 dbus_message_iter_get_basic(&ai, &value);
218 node = uuid_new(value);
219 PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
220
221 if (!dbus_message_iter_next(&ai))
222 break;
223 }
224 }
225
226 break;
227 }
228 }
229
230 return 0;
231 }
232
233 static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) {
234 const char *key;
235 DBusMessageIter variant_i;
236
237 pa_assert(u);
238 pa_assert(connected);
239 pa_assert(i);
240
241 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
242 pa_log("Property name not a string.");
243 return -1;
244 }
245
246 dbus_message_iter_get_basic(i, &key);
247
248 if (!dbus_message_iter_next(i)) {
249 pa_log("Property value missing");
250 return -1;
251 }
252
253 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
254 pa_log("Property value not a variant.");
255 return -1;
256 }
257
258 dbus_message_iter_recurse(i, &variant_i);
259
260 /* pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); */
261
262 switch (dbus_message_iter_get_arg_type(&variant_i)) {
263
264 case DBUS_TYPE_BOOLEAN: {
265
266 dbus_bool_t value;
267 dbus_message_iter_get_basic(&variant_i, &value);
268
269 if (pa_streq(key, "Connected"))
270 *connected = !!value;
271
272 /* pa_log_debug("Value %s", pa_yes_no(value)); */
273
274 break;
275 }
276 }
277
278 return 0;
279 }
280
281 static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t dead) {
282 pa_assert(y);
283 pa_assert(d);
284
285 if (!device_is_loaded(d))
286 return;
287
288 if (!device_is_audio(d))
289 return;
290
291 d->dead = dead;
292 pa_hook_fire(&y->hook, d);
293 }
294
295 static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
296 DBusMessage *r;
297 DBusMessageIter arg_i, element_i;
298 pa_dbus_pending *p;
299 pa_bluetooth_device *d;
300 pa_bluetooth_discovery *y;
301 int valid;
302
303 pa_assert_se(p = userdata);
304 pa_assert_se(y = p->context_data);
305 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
306
307 /* pa_log_debug("Got %s.GetProperties response for %s", */
308 /* dbus_message_get_interface(p->message), */
309 /* dbus_message_get_path(p->message)); */
310
311 d = p->call_data;
312
313 valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
314
315 if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
316 d->device_info_valid = valid;
317 else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties"))
318 d->headset_info_valid = valid;
319 else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties"))
320 d->audio_sink_info_valid = valid;
321
322 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
323
324 if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
325 pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
326
327 goto finish;
328 }
329
330 if (!dbus_message_iter_init(r, &arg_i)) {
331 pa_log("GetProperties reply has no arguments.");
332 goto finish;
333 }
334
335 if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
336 pa_log("GetProperties argument is not an array.");
337 goto finish;
338 }
339
340 dbus_message_iter_recurse(&arg_i, &element_i);
341 while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
342
343 if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
344 DBusMessageIter dict_i;
345
346 dbus_message_iter_recurse(&element_i, &dict_i);
347
348 if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
349 if (parse_device_property(y, d, &dict_i) < 0)
350 goto finish;
351
352 } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
353 if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0)
354 goto finish;
355
356 } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
357 if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0)
358 goto finish;
359 }
360 }
361
362 if (!dbus_message_iter_next(&element_i))
363 break;
364 }
365
366 finish:
367 run_callback(y, d, FALSE);
368
369 dbus_message_unref(r);
370
371 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
372 pa_dbus_pending_free(p);
373 }
374
375 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func) {
376 pa_dbus_pending *p;
377 DBusPendingCall *call;
378
379 pa_assert(y);
380 pa_assert(m);
381
382 pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
383
384 p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, d);
385 PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
386 dbus_pending_call_set_notify(call, func, p, NULL);
387
388 return p;
389 }
390
391 static void found_device(pa_bluetooth_discovery *y, const char* path) {
392 DBusMessage *m;
393 pa_bluetooth_device *d;
394
395 pa_assert(y);
396 pa_assert(path);
397
398 d = device_new(path);
399
400 pa_hashmap_put(y->devices, d->path, d);
401
402 pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
403 send_and_add_to_pending(y, d, m, get_properties_reply);
404
405 pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Headset", "GetProperties"));
406 send_and_add_to_pending(y, d, m, get_properties_reply);
407
408 pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.AudioSink", "GetProperties"));
409 send_and_add_to_pending(y, d, m, get_properties_reply);
410 }
411
412 static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
413 DBusError e;
414 DBusMessage *r;
415 char **paths = NULL;
416 int num = -1;
417 pa_dbus_pending *p;
418 pa_bluetooth_discovery *y;
419
420 pa_assert(pending);
421
422 dbus_error_init(&e);
423
424 pa_assert_se(p = userdata);
425 pa_assert_se(y = p->context_data);
426 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
427
428 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
429 pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
430 goto end;
431 }
432
433 if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
434 pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
435 dbus_error_free(&e);
436 } else {
437 int i;
438
439 for (i = 0; i < num; ++i)
440 found_device(y, paths[i]);
441 }
442
443 end:
444 if (paths)
445 dbus_free_string_array (paths);
446
447 dbus_message_unref(r);
448
449 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
450 pa_dbus_pending_free(p);
451 }
452
453 static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
454 DBusMessage *m;
455
456 pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices"));
457 send_and_add_to_pending(y, NULL, m, list_devices_reply);
458 }
459
460 static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
461 DBusError e;
462 DBusMessage *r;
463 char **paths = NULL;
464 int num = -1;
465 pa_dbus_pending *p;
466 pa_bluetooth_discovery *y;
467
468 pa_assert(pending);
469
470 dbus_error_init(&e);
471
472 pa_assert_se(p = userdata);
473 pa_assert_se(y = p->context_data);
474 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
475
476 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
477 pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
478 goto end;
479 }
480
481 if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
482 pa_log("org.bluez.Manager.ListAdapters returned an error: %s", e.message);
483 dbus_error_free(&e);
484 } else {
485 int i;
486
487 for (i = 0; i < num; ++i)
488 found_adapter(y, paths[i]);
489 }
490
491 end:
492 if (paths)
493 dbus_free_string_array (paths);
494
495 dbus_message_unref(r);
496
497 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
498 pa_dbus_pending_free(p);
499 }
500
501 static void list_adapters(pa_bluetooth_discovery *y) {
502 DBusMessage *m;
503 pa_assert(y);
504
505 pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
506 send_and_add_to_pending(y, NULL, m, list_adapters_reply);
507 }
508
509 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
510 DBusError err;
511 pa_bluetooth_discovery *y;
512
513 pa_assert(bus);
514 pa_assert(m);
515
516 pa_assert_se(y = userdata);
517
518 dbus_error_init(&err);
519
520 pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
521 dbus_message_get_interface(m),
522 dbus_message_get_path(m),
523 dbus_message_get_member(m));
524
525 if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
526 const char *path;
527 pa_bluetooth_device *d;
528
529 if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
530 pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
531 goto fail;
532 }
533
534 pa_log_debug("Device %s removed", path);
535
536 if ((d = pa_hashmap_remove(y->devices, path))) {
537 run_callback(y, d, TRUE);
538 device_free(d);
539 }
540
541 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
542
543 } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
544 const char *path;
545
546 if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
547 pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
548 goto fail;
549 }
550
551 pa_log_debug("Device %s created", path);
552
553 found_device(y, path);
554 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
555
556 } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
557 const char *path;
558
559 if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
560 pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
561 goto fail;
562 }
563
564 pa_log_debug("Adapter %s created", path);
565
566 found_adapter(y, path);
567 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
568
569 } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
570 dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
571 dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
572
573 pa_bluetooth_device *d;
574
575 if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
576 DBusMessageIter arg_i;
577
578 if (!dbus_message_iter_init(m, &arg_i)) {
579 pa_log("Failed to parse PropertyChanged: %s", err.message);
580 goto fail;
581 }
582
583 if (dbus_message_has_interface(m, "org.bluez.Device")) {
584 if (parse_device_property(y, d, &arg_i) < 0)
585 goto fail;
586
587 } else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
588 if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0)
589 goto fail;
590
591 } else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
592 if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0)
593 goto fail;
594 }
595
596 run_callback(y, d, FALSE);
597 }
598
599 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
600 }
601
602 fail:
603 dbus_error_free(&err);
604
605 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
606 }
607
608 const pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *y, const char* address) {
609 pa_bluetooth_device *d;
610 void *state = NULL;
611
612 pa_assert(y);
613 pa_assert(PA_REFCNT_VALUE(y) > 0);
614 pa_assert(address);
615
616 if (!pa_hook_is_firing(&y->hook))
617 pa_bluetooth_discovery_sync(y);
618
619 while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
620 if (pa_streq(d->address, address))
621 return d;
622
623 return NULL;
624 }
625
626 const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *y, const char* path) {
627 pa_assert(y);
628 pa_assert(PA_REFCNT_VALUE(y) > 0);
629 pa_assert(path);
630
631 if (!pa_hook_is_firing(&y->hook))
632 pa_bluetooth_discovery_sync(y);
633
634 return pa_hashmap_get(y->devices, path);
635 }
636
637 static int setup_dbus(pa_bluetooth_discovery *y) {
638 DBusError err;
639
640 dbus_error_init(&err);
641
642 y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err);
643
644 if (dbus_error_is_set(&err) || !y->connection) {
645 pa_log("Failed to get D-Bus connection: %s", err.message);
646 dbus_error_free(&err);
647 return -1;
648 }
649
650 return 0;
651 }
652
653 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
654 DBusError err;
655 pa_bluetooth_discovery *y;
656
657 pa_assert(c);
658
659 dbus_error_init(&err);
660
661 if ((y = pa_shared_get(c, "bluetooth-discovery")))
662 return pa_bluetooth_discovery_ref(y);
663
664 y = pa_xnew0(pa_bluetooth_discovery, 1);
665 PA_REFCNT_INIT(y);
666 y->core = c;
667 y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
668 PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
669 pa_hook_init(&y->hook, y);
670 pa_shared_set(c, "bluetooth-discovery", y);
671
672 if (setup_dbus(y) < 0)
673 goto fail;
674
675 /* dynamic detection of bluetooth audio devices */
676 if (!dbus_connection_add_filter(pa_dbus_connection_get(y->connection), filter_cb, y, NULL)) {
677 pa_log_error("Failed to add filter function");
678 goto fail;
679 }
680
681 if (pa_dbus_add_matches(
682 pa_dbus_connection_get(y->connection), &err,
683 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
684 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
685 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
686 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
687 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
688 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) {
689 pa_log("Failed to add D-Bus matches: %s", err.message);
690 goto fail;
691 }
692
693 list_adapters(y);
694
695 return y;
696
697 fail:
698
699 if (y)
700 pa_bluetooth_discovery_unref(y);
701
702 dbus_error_free(&err);
703
704 return NULL;
705 }
706
707 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
708 pa_assert(y);
709 pa_assert(PA_REFCNT_VALUE(y) > 0);
710
711 PA_REFCNT_INC(y);
712
713 return y;
714 }
715
716 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
717 pa_bluetooth_device *d;
718
719 pa_assert(y);
720 pa_assert(PA_REFCNT_VALUE(y) > 0);
721
722 if (PA_REFCNT_DEC(y) > 0)
723 return;
724
725 pa_dbus_free_pending_list(&y->pending);
726
727 if (y->devices) {
728 while ((d = pa_hashmap_steal_first(y->devices))) {
729 run_callback(y, d, TRUE);
730 device_free(d);
731 }
732
733 pa_hashmap_free(y->devices, NULL, NULL);
734 }
735
736 if (y->connection) {
737 pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
738 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
739 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
740 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
741 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
742 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
743 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
744 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL);
745
746 dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
747
748 pa_dbus_connection_unref(y->connection);
749 }
750
751 pa_hook_done(&y->hook);
752
753 if (y->core)
754 pa_shared_remove(y->core, "bluetooth-discovery");
755 }
756
757 void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
758 pa_assert(y);
759 pa_assert(PA_REFCNT_VALUE(y) > 0);
760
761 pa_dbus_sync_pending_list(&y->pending);
762 }
763
764 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y) {
765 pa_assert(y);
766 pa_assert(PA_REFCNT_VALUE(y) > 0);
767
768 return &y->hook;
769 }
770
771 const char*pa_bluetooth_get_form_factor(uint32_t class) {
772 unsigned i;
773 const char *r;
774
775 static const char * const table[] = {
776 [1] = "headset",
777 [2] = "hands-free",
778 [4] = "microphone",
779 [5] = "speaker",
780 [6] = "headphone",
781 [7] = "portable",
782 [8] = "car",
783 [10] = "hifi"
784 };
785
786 if (((class >> 8) & 31) != 4)
787 return NULL;
788
789 if ((i = (class >> 2) & 63) > PA_ELEMENTSOF(table))
790 r = NULL;
791 else
792 r = table[i];
793
794 if (!r)
795 pa_log_debug("Unknown Bluetooth minor device class %u", i);
796
797 return r;
798 }
799
800 char *pa_bluetooth_cleanup_name(const char *name) {
801 char *t, *s, *d;
802 pa_bool_t space = FALSE;
803
804 pa_assert(name);
805
806 while ((*name >= 1 && *name <= 32) || *name >= 127)
807 name++;
808
809 t = pa_xstrdup(name);
810
811 for (s = d = t; *s; s++) {
812
813 if (*s <= 32 || *s >= 127 || *s == '_') {
814 space = TRUE;
815 continue;
816 }
817
818 if (space) {
819 *(d++) = ' ';
820 space = FALSE;
821 }
822
823 *(d++) = *s;
824 }
825
826 *d = 0;
827
828 return t;
829 }