2 This file is part of PulseAudio.
4 Copyright 2008-2013 João Paulo Rechi Vita
5 Copyright 2011-2013 BMW Car IT GmbH.
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <pulsecore/core-util.h>
28 #include <pulsecore/i18n.h>
29 #include <pulsecore/module.h>
30 #include <pulsecore/modargs.h>
32 #include "bluez5-util.h"
34 #include "module-bluez5-device-symdef.h"
36 PA_MODULE_AUTHOR("João Paulo Rechi Vita");
37 PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source");
38 PA_MODULE_VERSION(PACKAGE_VERSION
);
39 PA_MODULE_LOAD_ONCE(false);
40 PA_MODULE_USAGE("path=<device object path>");
42 static const char* const valid_modargs
[] = {
51 pa_hook_slot
*device_connection_changed_slot
;
53 pa_bluetooth_discovery
*discovery
;
54 pa_bluetooth_device
*device
;
57 pa_bluetooth_profile_t profile
;
60 typedef enum pa_bluetooth_form_factor
{
61 PA_BLUETOOTH_FORM_FACTOR_UNKNOWN
,
62 PA_BLUETOOTH_FORM_FACTOR_HEADSET
,
63 PA_BLUETOOTH_FORM_FACTOR_HANDSFREE
,
64 PA_BLUETOOTH_FORM_FACTOR_MICROPHONE
,
65 PA_BLUETOOTH_FORM_FACTOR_SPEAKER
,
66 PA_BLUETOOTH_FORM_FACTOR_HEADPHONE
,
67 PA_BLUETOOTH_FORM_FACTOR_PORTABLE
,
68 PA_BLUETOOTH_FORM_FACTOR_CAR
,
69 PA_BLUETOOTH_FORM_FACTOR_HIFI
,
70 PA_BLUETOOTH_FORM_FACTOR_PHONE
,
71 } pa_bluetooth_form_factor_t
;
73 /* Run from main thread */
74 static pa_bluetooth_form_factor_t
form_factor_from_class(uint32_t class_of_device
) {
75 unsigned major
, minor
;
76 pa_bluetooth_form_factor_t r
;
78 static const pa_bluetooth_form_factor_t table
[] = {
79 [1] = PA_BLUETOOTH_FORM_FACTOR_HEADSET
,
80 [2] = PA_BLUETOOTH_FORM_FACTOR_HANDSFREE
,
81 [4] = PA_BLUETOOTH_FORM_FACTOR_MICROPHONE
,
82 [5] = PA_BLUETOOTH_FORM_FACTOR_SPEAKER
,
83 [6] = PA_BLUETOOTH_FORM_FACTOR_HEADPHONE
,
84 [7] = PA_BLUETOOTH_FORM_FACTOR_PORTABLE
,
85 [8] = PA_BLUETOOTH_FORM_FACTOR_CAR
,
86 [10] = PA_BLUETOOTH_FORM_FACTOR_HIFI
90 * See Bluetooth Assigned Numbers:
91 * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
93 major
= (class_of_device
>> 8) & 0x1F;
94 minor
= (class_of_device
>> 2) & 0x3F;
98 return PA_BLUETOOTH_FORM_FACTOR_PHONE
;
102 pa_log_debug("Unknown Bluetooth major device class %u", major
);
103 return PA_BLUETOOTH_FORM_FACTOR_UNKNOWN
;
106 r
= minor
< PA_ELEMENTSOF(table
) ? table
[minor
] : PA_BLUETOOTH_FORM_FACTOR_UNKNOWN
;
109 pa_log_debug("Unknown Bluetooth minor device class %u", minor
);
114 /* Run from main thread */
115 static const char *form_factor_to_string(pa_bluetooth_form_factor_t ff
) {
117 case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN
:
119 case PA_BLUETOOTH_FORM_FACTOR_HEADSET
:
121 case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE
:
123 case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE
:
125 case PA_BLUETOOTH_FORM_FACTOR_SPEAKER
:
127 case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE
:
129 case PA_BLUETOOTH_FORM_FACTOR_PORTABLE
:
131 case PA_BLUETOOTH_FORM_FACTOR_CAR
:
133 case PA_BLUETOOTH_FORM_FACTOR_HIFI
:
135 case PA_BLUETOOTH_FORM_FACTOR_PHONE
:
139 pa_assert_not_reached();
142 /* Run from main thread */
143 static char *cleanup_name(const char *name
) {
149 while ((*name
>= 1 && *name
<= 32) || *name
>= 127)
152 t
= pa_xstrdup(name
);
154 for (s
= d
= t
; *s
; s
++) {
156 if (*s
<= 32 || *s
>= 127 || *s
== '_') {
174 /* Run from main thread */
175 static int add_card(struct userdata
*u
) {
176 const pa_bluetooth_device
*d
;
177 pa_card_new_data data
;
179 pa_bluetooth_form_factor_t ff
;
181 pa_bluetooth_profile_t
*p
;
184 pa_assert(u
->device
);
188 pa_card_new_data_init(&data
);
189 data
.driver
= __FILE__
;
190 data
.module
= u
->module
;
192 alias
= cleanup_name(d
->alias
);
193 pa_proplist_sets(data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, alias
);
196 pa_proplist_sets(data
.proplist
, PA_PROP_DEVICE_STRING
, d
->address
);
197 pa_proplist_sets(data
.proplist
, PA_PROP_DEVICE_API
, "bluez");
198 pa_proplist_sets(data
.proplist
, PA_PROP_DEVICE_CLASS
, "sound");
199 pa_proplist_sets(data
.proplist
, PA_PROP_DEVICE_BUS
, "bluetooth");
201 if ((ff
= form_factor_from_class(d
->class_of_device
)) != PA_BLUETOOTH_FORM_FACTOR_UNKNOWN
)
202 pa_proplist_sets(data
.proplist
, PA_PROP_DEVICE_FORM_FACTOR
, form_factor_to_string(ff
));
204 pa_proplist_sets(data
.proplist
, "bluez.path", d
->path
);
205 pa_proplist_setf(data
.proplist
, "bluez.class", "0x%06x", d
->class_of_device
);
206 pa_proplist_sets(data
.proplist
, "bluez.alias", d
->alias
);
207 data
.name
= pa_sprintf_malloc("bluez_card.%s", d
->address
);
208 data
.namereg_fail
= false;
210 cp
= pa_card_profile_new("off", _("Off"), sizeof(pa_bluetooth_profile_t
));
211 cp
->available
= PA_AVAILABLE_YES
;
212 p
= PA_CARD_PROFILE_DATA(cp
);
213 *p
= PA_BLUETOOTH_PROFILE_OFF
;
214 pa_hashmap_put(data
.profiles
, cp
->name
, cp
);
216 u
->card
= pa_card_new(u
->core
, &data
);
217 pa_card_new_data_done(&data
);
219 pa_log("Failed to allocate card.");
223 u
->card
->userdata
= u
;
225 p
= PA_CARD_PROFILE_DATA(u
->card
->active_profile
);
231 /* Run from main thread */
232 static pa_hook_result_t
device_connection_changed_cb(pa_bluetooth_discovery
*y
, const pa_bluetooth_device
*d
, struct userdata
*u
) {
236 if (d
!= u
->device
|| pa_bluetooth_device_any_transport_connected(d
))
239 pa_log_debug("Unloading module for device %s", d
->path
);
240 pa_module_unload(u
->core
, u
->module
, true);
245 int pa__init(pa_module
* m
) {
252 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
256 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
257 pa_log_error("Failed to parse module arguments");
261 if (!(path
= pa_modargs_get_value(ma
, "path", NULL
))) {
262 pa_log_error("Failed to get device path from module arguments");
266 if (!(u
->discovery
= pa_bluetooth_discovery_get(m
->core
)))
269 if (!(u
->device
= pa_bluetooth_discovery_get_device_by_path(u
->discovery
, path
))) {
270 pa_log_error("%s is unknown", path
);
276 u
->device_connection_changed_slot
=
277 pa_hook_connect(pa_bluetooth_discovery_hook(u
->discovery
, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED
),
278 PA_HOOK_NORMAL
, (pa_hook_cb_t
) device_connection_changed_cb
, u
);
295 void pa__done(pa_module
*m
) {
300 if (!(u
= m
->userdata
))
303 if (u
->device_connection_changed_slot
)
304 pa_hook_slot_free(u
->device_connection_changed_slot
);
307 pa_card_free(u
->card
);
310 pa_bluetooth_discovery_unref(u
->discovery
);