bluetooth: Create BlueZ 5 card
[platform/upstream/pulseaudio.git] / 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 }