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 33PA_MODULE_AUTHOR("João Paulo Rechi Vita"); 34PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers"); 35PA_MODULE_VERSION(PACKAGE_VERSION); 36PA_MODULE_LOAD_ONCE(true); 37PA_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 47static 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 58struct 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 69static 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 114const char *default_headset_backend = "native"; 115#else 116const char *default_headset_backend = "ofono"; 117#endif 118 119int 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 202fail: 203 if (ma) 204 pa_modargs_free(ma); 205 pa__done(m); 206 return -1; 207} 208 209void 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