]> code.delx.au - pulseaudio/blob - src/modules/bluetooth/module-bluez5-device.c
bluetooth: Create BlueZ 5 card
[pulseaudio] / src / modules / bluetooth / module-bluez5-device.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008-2013 João Paulo Rechi Vita
5 Copyright 2011-2013 BMW Car IT GmbH.
6
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.
11
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.
16
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
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pulsecore/core-util.h>
28 #include <pulsecore/i18n.h>
29 #include <pulsecore/module.h>
30 #include <pulsecore/modargs.h>
31
32 #include "bluez5-util.h"
33
34 #include "module-bluez5-device-symdef.h"
35
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>");
41
42 static const char* const valid_modargs[] = {
43 "path",
44 NULL
45 };
46
47 struct userdata {
48 pa_module *module;
49 pa_core *core;
50
51 pa_hook_slot *device_connection_changed_slot;
52
53 pa_bluetooth_discovery *discovery;
54 pa_bluetooth_device *device;
55
56 pa_card *card;
57 pa_bluetooth_profile_t profile;
58 };
59
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;
72
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;
77
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
87 };
88
89 /*
90 * See Bluetooth Assigned Numbers:
91 * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
92 */
93 major = (class_of_device >> 8) & 0x1F;
94 minor = (class_of_device >> 2) & 0x3F;
95
96 switch (major) {
97 case 2:
98 return PA_BLUETOOTH_FORM_FACTOR_PHONE;
99 case 4:
100 break;
101 default:
102 pa_log_debug("Unknown Bluetooth major device class %u", major);
103 return PA_BLUETOOTH_FORM_FACTOR_UNKNOWN;
104 }
105
106 r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BLUETOOTH_FORM_FACTOR_UNKNOWN;
107
108 if (!r)
109 pa_log_debug("Unknown Bluetooth minor device class %u", minor);
110
111 return r;
112 }
113
114 /* Run from main thread */
115 static const char *form_factor_to_string(pa_bluetooth_form_factor_t ff) {
116 switch (ff) {
117 case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN:
118 return "unknown";
119 case PA_BLUETOOTH_FORM_FACTOR_HEADSET:
120 return "headset";
121 case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE:
122 return "hands-free";
123 case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE:
124 return "microphone";
125 case PA_BLUETOOTH_FORM_FACTOR_SPEAKER:
126 return "speaker";
127 case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE:
128 return "headphone";
129 case PA_BLUETOOTH_FORM_FACTOR_PORTABLE:
130 return "portable";
131 case PA_BLUETOOTH_FORM_FACTOR_CAR:
132 return "car";
133 case PA_BLUETOOTH_FORM_FACTOR_HIFI:
134 return "hifi";
135 case PA_BLUETOOTH_FORM_FACTOR_PHONE:
136 return "phone";
137 }
138
139 pa_assert_not_reached();
140 }
141
142 /* Run from main thread */
143 static char *cleanup_name(const char *name) {
144 char *t, *s, *d;
145 bool space = false;
146
147 pa_assert(name);
148
149 while ((*name >= 1 && *name <= 32) || *name >= 127)
150 name++;
151
152 t = pa_xstrdup(name);
153
154 for (s = d = t; *s; s++) {
155
156 if (*s <= 32 || *s >= 127 || *s == '_') {
157 space = true;
158 continue;
159 }
160
161 if (space) {
162 *(d++) = ' ';
163 space = false;
164 }
165
166 *(d++) = *s;
167 }
168
169 *d = 0;
170
171 return t;
172 }
173
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;
178 char *alias;
179 pa_bluetooth_form_factor_t ff;
180 pa_card_profile *cp;
181 pa_bluetooth_profile_t *p;
182
183 pa_assert(u);
184 pa_assert(u->device);
185
186 d = u->device;
187
188 pa_card_new_data_init(&data);
189 data.driver = __FILE__;
190 data.module = u->module;
191
192 alias = cleanup_name(d->alias);
193 pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, alias);
194 pa_xfree(alias);
195
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");
200
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));
203
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;
209
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);
215
216 u->card = pa_card_new(u->core, &data);
217 pa_card_new_data_done(&data);
218 if (!u->card) {
219 pa_log("Failed to allocate card.");
220 return -1;
221 }
222
223 u->card->userdata = u;
224
225 p = PA_CARD_PROFILE_DATA(u->card->active_profile);
226 u->profile = *p;
227
228 return 0;
229 }
230
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) {
233 pa_assert(d);
234 pa_assert(u);
235
236 if (d != u->device || pa_bluetooth_device_any_transport_connected(d))
237 return PA_HOOK_OK;
238
239 pa_log_debug("Unloading module for device %s", d->path);
240 pa_module_unload(u->core, u->module, true);
241
242 return PA_HOOK_OK;
243 }
244
245 int pa__init(pa_module* m) {
246 struct userdata *u;
247 const char *path;
248 pa_modargs *ma;
249
250 pa_assert(m);
251
252 m->userdata = u = pa_xnew0(struct userdata, 1);
253 u->module = m;
254 u->core = m->core;
255
256 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
257 pa_log_error("Failed to parse module arguments");
258 goto fail;
259 }
260
261 if (!(path = pa_modargs_get_value(ma, "path", NULL))) {
262 pa_log_error("Failed to get device path from module arguments");
263 goto fail;
264 }
265
266 if (!(u->discovery = pa_bluetooth_discovery_get(m->core)))
267 goto fail;
268
269 if (!(u->device = pa_bluetooth_discovery_get_device_by_path(u->discovery, path))) {
270 pa_log_error("%s is unknown", path);
271 goto fail;
272 }
273
274 pa_modargs_free(ma);
275
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);
279
280 if (add_card(u) < 0)
281 goto fail;
282
283 return 0;
284
285 fail:
286
287 if (ma)
288 pa_modargs_free(ma);
289
290 pa__done(m);
291
292 return -1;
293 }
294
295 void pa__done(pa_module *m) {
296 struct userdata *u;
297
298 pa_assert(m);
299
300 if (!(u = m->userdata))
301 return;
302
303 if (u->device_connection_changed_slot)
304 pa_hook_slot_free(u->device_connection_changed_slot);
305
306 if (u->card)
307 pa_card_free(u->card);
308
309 if (u->discovery)
310 pa_bluetooth_discovery_unref(u->discovery);
311
312 pa_xfree(u);
313 }