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 );
41
42 static const char* const valid_modargs[] = {
43 "headset",
44 "autodetect_mtu",
45 NULL
46 };
47
48 struct userdata {
49 pa_module *module;
50 pa_core *core;
51 pa_hashmap *loaded_device_paths;
52 pa_hook_slot *device_connection_changed_slot;
53 pa_bluetooth_discovery *discovery;
54 bool autodetect_mtu;
55 };
56
device_connection_changed_cb(pa_bluetooth_discovery * y,const pa_bluetooth_device * d,struct userdata * u)57 static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
58 bool module_loaded;
59
60 pa_assert(d);
61 pa_assert(u);
62
63 module_loaded = pa_hashmap_get(u->loaded_device_paths, d->path) ? true : false;
64
65 if (module_loaded && !pa_bluetooth_device_any_transport_connected(d)) {
66 /* disconnection, the module unloads itself */
67 pa_log_debug("Unregistering module for %s", d->path);
68 pa_hashmap_remove(u->loaded_device_paths, d->path);
69 return PA_HOOK_OK;
70 }
71
72 if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
73 /* a new device has been connected */
74 pa_module *m;
75 char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu);
76
77 pa_log_debug("Loading module-bluez5-device %s", args);
78 pa_module_load(&m, u->module->core, "module-bluez5-device", args);
79 pa_xfree(args);
80
81 if (m)
82 /* No need to duplicate the path here since the device object will
83 * exist for the whole hashmap entry lifespan */
84 pa_hashmap_put(u->loaded_device_paths, d->path, d->path);
85 else
86 pa_log_warn("Failed to load module for device %s", d->path);
87
88 return PA_HOOK_OK;
89 }
90
91 return PA_HOOK_OK;
92 }
93
94 #ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
95 const char *default_headset_backend = "auto";
96 #else
97 const char *default_headset_backend = "ofono";
98 #endif
99
pa__init(pa_module * m)100 int pa__init(pa_module *m) {
101 struct userdata *u;
102 pa_modargs *ma;
103 const char *headset_str;
104 int headset_backend;
105 bool autodetect_mtu;
106
107 pa_assert(m);
108
109 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
110 pa_log("failed to parse module arguments.");
111 goto fail;
112 }
113
114 pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend));
115 if (pa_streq(headset_str, "ofono"))
116 headset_backend = HEADSET_BACKEND_OFONO;
117 else if (pa_streq(headset_str, "native"))
118 headset_backend = HEADSET_BACKEND_NATIVE;
119 else if (pa_streq(headset_str, "auto"))
120 headset_backend = HEADSET_BACKEND_AUTO;
121 else {
122 pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str);
123 goto fail;
124 }
125
126 autodetect_mtu = false;
127 if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) {
128 pa_log("Invalid boolean value for autodetect_mtu parameter");
129 goto fail;
130 }
131
132 m->userdata = u = pa_xnew0(struct userdata, 1);
133 u->module = m;
134 u->core = m->core;
135 u->autodetect_mtu = autodetect_mtu;
136 u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
137
138 if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend)))
139 goto fail;
140
141 u->device_connection_changed_slot =
142 pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
143 PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u);
144
145 pa_modargs_free(ma);
146 return 0;
147
148 fail:
149 if (ma)
150 pa_modargs_free(ma);
151 pa__done(m);
152 return -1;
153 }
154
pa__done(pa_module * m)155 void pa__done(pa_module *m) {
156 struct userdata *u;
157
158 pa_assert(m);
159
160 if (!(u = m->userdata))
161 return;
162
163 if (u->device_connection_changed_slot)
164 pa_hook_slot_free(u->device_connection_changed_slot);
165
166 if (u->loaded_device_paths)
167 pa_hashmap_free(u->loaded_device_paths);
168
169 if (u->discovery)
170 pa_bluetooth_discovery_unref(u->discovery);
171
172 pa_xfree(u);
173 }
174