1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008-2013 João 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, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <pulsecore/core.h>
25 #include <pulsecore/core-util.h>
26 #include <pulsecore/macro.h>
27 #include <pulsecore/module.h>
28 #include <pulsecore/modargs.h>
29 #include <pulsecore/shared.h>
30
31 #include "bluez5-util.h"
32
33 PA_MODULE_AUTHOR("João Paulo Rechi Vita");
34 PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers");
35 PA_MODULE_VERSION(PACKAGE_VERSION);
36 PA_MODULE_LOAD_ONCE(true);
37 PA_MODULE_USAGE(
38 "headset=ofono|native|auto"
39 "autodetect_mtu=<boolean>"
40 "enable_msbc=<boolean, enable mSBC support in native and oFono backends, default is true>"
41 "output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>"
42 "enable_native_hsp_hs=<boolean, enable HSP support in native backend>"
43 "enable_native_hfp_hf=<boolean, enable HFP support in native backend>"
44 "avrcp_absolute_volume=<synchronize volume with peer, true by default>"
45 );
46
47 static const char* const valid_modargs[] = {
48 "headset",
49 "autodetect_mtu",
50 "enable_msbc",
51 "output_rate_refresh_interval_ms",
52 "enable_native_hsp_hs",
53 "enable_native_hfp_hf",
54 "avrcp_absolute_volume",
55 NULL
56 };
57
58 struct userdata {
59 pa_module *module;
60 pa_core *core;
61 pa_hashmap *loaded_device_paths;
62 pa_hook_slot *device_connection_changed_slot;
63 pa_bluetooth_discovery *discovery;
64 bool autodetect_mtu;
65 bool avrcp_absolute_volume;
66 uint32_t output_rate_refresh_interval_ms;
67 };
68
device_connection_changed_cb(pa_bluetooth_discovery * y,const pa_bluetooth_device * d,struct userdata * u)69 static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
70 bool module_loaded;
71
72 pa_assert(d);
73 pa_assert(u);
74
75 module_loaded = pa_hashmap_get(u->loaded_device_paths, d->path) ? true : false;
76
77 /* When changing A2DP codec there is no transport connected, ensure that no module is unloaded */
78 if (module_loaded && !pa_bluetooth_device_any_transport_connected(d) &&
79 !d->codec_switching_in_progress) {
80 /* disconnection, the module unloads itself */
81 pa_log_debug("Unregistering module for %s", d->path);
82 pa_hashmap_remove(u->loaded_device_paths, d->path);
83 return PA_HOOK_OK;
84 }
85
86 if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
87 /* a new device has been connected */
88 pa_module *m;
89 char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i output_rate_refresh_interval_ms=%u"
90 " avrcp_absolute_volume=%i",
91 d->path,
92 (int)u->autodetect_mtu,
93 u->output_rate_refresh_interval_ms,
94 (int)u->avrcp_absolute_volume);
95
96 pa_log_debug("Loading module-bluez5-device %s", args);
97 pa_module_load(&m, u->module->core, "module-bluez5-device", args);
98 pa_xfree(args);
99
100 if (m)
101 /* No need to duplicate the path here since the device object will
102 * exist for the whole hashmap entry lifespan */
103 pa_hashmap_put(u->loaded_device_paths, d->path, d->path);
104 else
105 pa_log_warn("Failed to load module for device %s", d->path);
106
107 return PA_HOOK_OK;
108 }
109
110 return PA_HOOK_OK;
111 }
112
113 #ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
114 const char *default_headset_backend = "native";
115 #else
116 const char *default_headset_backend = "ofono";
117 #endif
118
pa__init(pa_module * m)119 int pa__init(pa_module *m) {
120 struct userdata *u;
121 pa_modargs *ma;
122 const char *headset_str;
123 int headset_backend;
124 bool autodetect_mtu;
125 bool enable_msbc;
126 bool avrcp_absolute_volume;
127 uint32_t output_rate_refresh_interval_ms;
128 bool enable_native_hsp_hs;
129 bool enable_native_hfp_hf;
130
131 pa_assert(m);
132
133 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
134 pa_log("failed to parse module arguments.");
135 goto fail;
136 }
137
138 pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend));
139 if (pa_streq(headset_str, "ofono"))
140 headset_backend = HEADSET_BACKEND_OFONO;
141 else if (pa_streq(headset_str, "native"))
142 headset_backend = HEADSET_BACKEND_NATIVE;
143 else if (pa_streq(headset_str, "auto"))
144 headset_backend = HEADSET_BACKEND_AUTO;
145 else {
146 pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str);
147 goto fail;
148 }
149
150 /* default value if no module parameter */
151 enable_native_hfp_hf = (headset_backend == HEADSET_BACKEND_NATIVE);
152
153 autodetect_mtu = false;
154 if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) {
155 pa_log("Invalid boolean value for autodetect_mtu parameter");
156 }
157 enable_msbc = true;
158 if (pa_modargs_get_value_boolean(ma, "enable_msbc", &enable_msbc) < 0) {
159 pa_log("Invalid boolean value for enable_msbc parameter");
160 }
161 enable_native_hfp_hf = true;
162 if (pa_modargs_get_value_boolean(ma, "enable_native_hfp_hf", &enable_native_hfp_hf) < 0) {
163 pa_log("enable_native_hfp_hf must be true or false");
164 goto fail;
165 }
166 enable_native_hsp_hs = !enable_native_hfp_hf;
167 if (pa_modargs_get_value_boolean(ma, "enable_native_hsp_hs", &enable_native_hsp_hs) < 0) {
168 pa_log("enable_native_hsp_hs must be true or false");
169 goto fail;
170 }
171
172 avrcp_absolute_volume = true;
173 if (pa_modargs_get_value_boolean(ma, "avrcp_absolute_volume", &avrcp_absolute_volume) < 0) {
174 pa_log("avrcp_absolute_volume must be true or false");
175 goto fail;
176 }
177
178 output_rate_refresh_interval_ms = DEFAULT_OUTPUT_RATE_REFRESH_INTERVAL_MS;
179 if (pa_modargs_get_value_u32(ma, "output_rate_refresh_interval_ms", &output_rate_refresh_interval_ms) < 0) {
180 pa_log("Invalid value for output_rate_refresh_interval parameter.");
181 goto fail;
182 }
183
184 m->userdata = u = pa_xnew0(struct userdata, 1);
185 u->module = m;
186 u->core = m->core;
187 u->autodetect_mtu = autodetect_mtu;
188 u->avrcp_absolute_volume = avrcp_absolute_volume;
189 u->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms;
190 u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
191
192 if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend, enable_native_hsp_hs, enable_native_hfp_hf, enable_msbc)))
193 goto fail;
194
195 u->device_connection_changed_slot =
196 pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
197 PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u);
198
199 pa_modargs_free(ma);
200 return 0;
201
202 fail:
203 if (ma)
204 pa_modargs_free(ma);
205 pa__done(m);
206 return -1;
207 }
208
pa__done(pa_module * m)209 void pa__done(pa_module *m) {
210 struct userdata *u;
211
212 pa_assert(m);
213
214 if (!(u = m->userdata))
215 return;
216
217 if (u->device_connection_changed_slot)
218 pa_hook_slot_free(u->device_connection_changed_slot);
219
220 if (u->loaded_device_paths)
221 pa_hashmap_free(u->loaded_device_paths);
222
223 if (u->discovery)
224 pa_bluetooth_discovery_unref(u->discovery);
225
226 pa_xfree(u);
227 }
228