1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2009 Canonical Ltd
6 Copyright (C) 2012 Intel Corporation
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulse/xmalloc.h>
27
28 #include <pulsecore/core.h>
29 #include <pulsecore/modargs.h>
30 #include <pulsecore/source-output.h>
31 #include <pulsecore/source.h>
32 #include <pulsecore/core-util.h>
33
34 PA_MODULE_AUTHOR("Frédéric Dalleau, Pali Rohár");
35 PA_MODULE_DESCRIPTION("Policy module to make using bluetooth devices out-of-the-box easier");
36 PA_MODULE_VERSION(PACKAGE_VERSION);
37 PA_MODULE_LOAD_ONCE(true);
38 PA_MODULE_USAGE(
39 "auto_switch=<Switch between hsp and a2dp profile? (0 - never, 1 - media.role=phone, 2 - heuristic> "
40 "a2dp_source=<Handle a2dp_source card profile (sink role)?> "
41 "ag=<Handle headset_audio_gateway card profile (headset role)?> ");
42
43 static const char* const valid_modargs[] = {
44 "auto_switch",
45 "a2dp_source",
46 "ag",
47 NULL
48 };
49
50 struct userdata {
51 uint32_t auto_switch;
52 bool enable_a2dp_source;
53 bool enable_ag;
54 pa_hook_slot *source_put_slot;
55 pa_hook_slot *sink_put_slot;
56 pa_hook_slot *source_output_put_slot;
57 pa_hook_slot *source_output_unlink_slot;
58 pa_hook_slot *card_init_profile_slot;
59 pa_hook_slot *card_unlink_slot;
60 pa_hook_slot *profile_available_changed_slot;
61 pa_hashmap *will_need_revert_card_map;
62 };
63
64 /* When a source is created, loopback it to default sink */
source_put_hook_callback(pa_core * c,pa_source * source,void * userdata)65 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void *userdata) {
66 struct userdata *u = userdata;
67 const char *s;
68 const char *role;
69 char *args;
70 pa_module *m = NULL;
71
72 pa_assert(c);
73 pa_assert(source);
74
75 /* Only consider bluetooth sinks and sources */
76 s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS);
77 if (!s)
78 return PA_HOOK_OK;
79
80 if (!pa_streq(s, "bluetooth"))
81 return PA_HOOK_OK;
82
83 s = pa_proplist_gets(source->proplist, "bluetooth.protocol");
84 if (!s)
85 return PA_HOOK_OK;
86
87 if (u->enable_a2dp_source && pa_streq(s, "a2dp_source"))
88 role = "music";
89 else if (u->enable_ag && pa_streq(s, "headset_audio_gateway"))
90 role = "phone";
91 else {
92 pa_log_debug("Profile %s cannot be selected for loopback", s);
93 return PA_HOOK_OK;
94 }
95
96 /* Load module-loopback */
97 args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name,
98 role);
99 (void) pa_module_load(&m, c, "module-loopback", args);
100 pa_xfree(args);
101
102 return PA_HOOK_OK;
103 }
104
105 /* When a sink is created, loopback it to default source */
sink_put_hook_callback(pa_core * c,pa_sink * sink,void * userdata)106 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void *userdata) {
107 struct userdata *u = userdata;
108 const char *s;
109 const char *role;
110 char *args;
111 pa_module *m = NULL;
112
113 pa_assert(c);
114 pa_assert(sink);
115
116 /* Only consider bluetooth sinks and sources */
117 s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS);
118 if (!s)
119 return PA_HOOK_OK;
120
121 if (!pa_streq(s, "bluetooth"))
122 return PA_HOOK_OK;
123
124 s = pa_proplist_gets(sink->proplist, "bluetooth.protocol");
125 if (!s)
126 return PA_HOOK_OK;
127
128 if (u->enable_ag && pa_streq(s, "headset_audio_gateway"))
129 role = "phone";
130 else {
131 pa_log_debug("Profile %s cannot be selected for loopback", s);
132 return PA_HOOK_OK;
133 }
134
135 /* Load module-loopback */
136 args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name,
137 role);
138 (void) pa_module_load(&m, c, "module-loopback", args);
139 pa_xfree(args);
140
141 return PA_HOOK_OK;
142 }
143
card_set_profile(struct userdata * u,pa_card * card,bool revert_to_a2dp)144 static void card_set_profile(struct userdata *u, pa_card *card, bool revert_to_a2dp)
145 {
146 pa_card_profile *profile;
147 void *state;
148
149 /* Find available profile and activate it */
150 PA_HASHMAP_FOREACH(profile, card->profiles, state) {
151 if (profile->available == PA_AVAILABLE_NO)
152 continue;
153
154 /* Check for correct profile based on revert_to_a2dp */
155 if (revert_to_a2dp) {
156 if (!pa_streq(profile->name, "a2dp_sink"))
157 continue;
158 } else {
159 if (!pa_streq(profile->name, "headset_head_unit"))
160 continue;
161 }
162
163 pa_log_debug("Setting card '%s' to profile '%s'", card->name, profile->name);
164
165 if (pa_card_set_profile(card, profile, false) != 0) {
166 pa_log_warn("Could not set profile '%s'", profile->name);
167 continue;
168 }
169
170 /* When we are not in revert_to_a2dp phase flag this card for will_need_revert */
171 if (!revert_to_a2dp)
172 pa_hashmap_put(u->will_need_revert_card_map, card, PA_INT_TO_PTR(1));
173
174 break;
175 }
176 }
177
178 /* Switch profile for one card */
switch_profile(pa_card * card,bool revert_to_a2dp,void * userdata)179 static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
180 struct userdata *u = userdata;
181 const char *s;
182
183 /* Only consider bluetooth cards */
184 s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
185 if (!s || !pa_streq(s, "bluetooth"))
186 return;
187
188 if (revert_to_a2dp) {
189 /* In revert_to_a2dp phase only consider cards with will_need_revert flag and remove it */
190 if (!pa_hashmap_remove(u->will_need_revert_card_map, card))
191 return;
192
193 /* Skip card if does not have active hsp profile */
194 if (!pa_streq(card->active_profile->name, "headset_head_unit"))
195 return;
196
197 /* Skip card if already has active a2dp profile */
198 if (pa_streq(card->active_profile->name, "a2dp_sink"))
199 return;
200 } else {
201 /* Skip card if does not have active a2dp profile */
202 if (!pa_streq(card->active_profile->name, "a2dp_sink"))
203 return;
204
205 /* Skip card if already has active hsp profile */
206 if (pa_streq(card->active_profile->name, "headset_head_unit"))
207 return;
208 }
209
210 card_set_profile(u, card, revert_to_a2dp);
211 }
212
213 /* Return true if we should ignore this source output */
ignore_output(pa_source_output * source_output,void * userdata)214 static bool ignore_output(pa_source_output *source_output, void *userdata) {
215 struct userdata *u = userdata;
216 const char *s;
217
218 /* New applications could set media.role for identifying streams */
219 /* We are interested only in media.role=phone */
220 s = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
221 if (s)
222 return !pa_streq(s, "phone");
223
224 /* If media.role is not set use some heuristic (if enabled) */
225 if (u->auto_switch != 2)
226 return true;
227
228 /* Ignore if resample method is peaks (used by desktop volume programs) */
229 if (pa_source_output_get_resample_method(source_output) == PA_RESAMPLER_PEAKS)
230 return true;
231
232 /* Ignore if there is no client/application assigned (used by virtual stream) */
233 if (!source_output->client)
234 return true;
235
236 /* Ignore if recording from monitor of sink */
237 if (source_output->direct_on_input)
238 return true;
239
240 return false;
241 }
242
source_output_count(pa_core * c,void * userdata)243 static unsigned source_output_count(pa_core *c, void *userdata) {
244 pa_source_output *source_output;
245 uint32_t idx;
246 unsigned count = 0;
247
248 PA_IDXSET_FOREACH(source_output, c->source_outputs, idx)
249 if (!ignore_output(source_output, userdata))
250 ++count;
251
252 return count;
253 }
254
255 /* Switch profile for all cards */
switch_profile_all(pa_idxset * cards,bool revert_to_a2dp,void * userdata)256 static void switch_profile_all(pa_idxset *cards, bool revert_to_a2dp, void *userdata) {
257 pa_card *card;
258 uint32_t idx;
259
260 PA_IDXSET_FOREACH(card, cards, idx)
261 switch_profile(card, revert_to_a2dp, userdata);
262 }
263
264 /* When a source output is created, switch profile a2dp to profile hsp */
source_output_put_hook_callback(pa_core * c,pa_source_output * source_output,void * userdata)265 static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_output *source_output, void *userdata) {
266 pa_assert(c);
267 pa_assert(source_output);
268
269 if (ignore_output(source_output, userdata))
270 return PA_HOOK_OK;
271
272 switch_profile_all(c->cards, false, userdata);
273 return PA_HOOK_OK;
274 }
275
276 /* When all source outputs are unlinked, switch profile hsp back back to profile a2dp */
source_output_unlink_hook_callback(pa_core * c,pa_source_output * source_output,void * userdata)277 static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source_output *source_output, void *userdata) {
278 pa_assert(c);
279 pa_assert(source_output);
280
281 if (ignore_output(source_output, userdata))
282 return PA_HOOK_OK;
283
284 /* If there are still some source outputs do nothing. */
285 if (source_output_count(c, userdata) > 0)
286 return PA_HOOK_OK;
287
288 switch_profile_all(c->cards, true, userdata);
289 return PA_HOOK_OK;
290 }
291
card_init_profile_hook_callback(pa_core * c,pa_card * card,void * userdata)292 static pa_hook_result_t card_init_profile_hook_callback(pa_core *c, pa_card *card, void *userdata) {
293 struct userdata *u = userdata;
294 const char *s;
295
296 pa_assert(c);
297 pa_assert(card);
298
299 if (source_output_count(c, userdata) == 0)
300 return PA_HOOK_OK;
301
302 /* Only consider bluetooth cards */
303 s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
304 if (!s || !pa_streq(s, "bluetooth"))
305 return PA_HOOK_OK;
306
307 /* Ignore card if has already set other initial profile than a2dp */
308 if (card->active_profile &&
309 !pa_streq(card->active_profile->name, "a2dp_sink"))
310 return PA_HOOK_OK;
311
312 /* Set initial profile to hsp */
313 card_set_profile(u, card, false);
314
315 /* Flag this card for will_need_revert */
316 pa_hashmap_put(u->will_need_revert_card_map, card, PA_INT_TO_PTR(1));
317 return PA_HOOK_OK;
318 }
319
card_unlink_hook_callback(pa_core * c,pa_card * card,void * userdata)320 static pa_hook_result_t card_unlink_hook_callback(pa_core *c, pa_card *card, void *userdata) {
321 pa_assert(c);
322 pa_assert(card);
323 switch_profile(card, true, userdata);
324 return PA_HOOK_OK;
325 }
326
find_best_profile(pa_card * card)327 static pa_card_profile *find_best_profile(pa_card *card) {
328 void *state;
329 pa_card_profile *profile;
330 pa_card_profile *result = card->active_profile;
331
332 PA_HASHMAP_FOREACH(profile, card->profiles, state) {
333 if (profile->available == PA_AVAILABLE_NO)
334 continue;
335
336 if (result == NULL ||
337 (profile->available == PA_AVAILABLE_YES && result->available == PA_AVAILABLE_UNKNOWN) ||
338 (profile->available == result->available && profile->priority > result->priority))
339 result = profile;
340 }
341
342 return result;
343 }
344
profile_available_hook_callback(pa_core * c,pa_card_profile * profile,void * userdata)345 static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_profile *profile, void *userdata) {
346 pa_card *card;
347 const char *s;
348 bool is_active_profile;
349 pa_card_profile *selected_profile;
350
351 pa_assert(c);
352 pa_assert(profile);
353 pa_assert_se((card = profile->card));
354
355 /* Only consider bluetooth cards */
356 s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
357 if (!s || !pa_streq(s, "bluetooth"))
358 return PA_HOOK_OK;
359
360 /* Do not automatically switch profiles for headsets, just in case */
361 if (pa_streq(profile->name, "a2dp_sink") || pa_streq(profile->name, "headset_head_unit"))
362 return PA_HOOK_OK;
363
364 is_active_profile = card->active_profile == profile;
365
366 if (profile->available == PA_AVAILABLE_YES) {
367 if (is_active_profile)
368 return PA_HOOK_OK;
369
370 if (card->active_profile->available == PA_AVAILABLE_YES && card->active_profile->priority >= profile->priority)
371 return PA_HOOK_OK;
372
373 selected_profile = profile;
374 } else {
375 if (!is_active_profile)
376 return PA_HOOK_OK;
377
378 pa_assert_se((selected_profile = find_best_profile(card)));
379
380 if (selected_profile == card->active_profile)
381 return PA_HOOK_OK;
382 }
383
384 pa_log_debug("Setting card '%s' to profile '%s'", card->name, selected_profile->name);
385
386 if (pa_card_set_profile(card, selected_profile, false) != 0)
387 pa_log_warn("Could not set profile '%s'", selected_profile->name);
388
389 return PA_HOOK_OK;
390 }
391
handle_all_profiles(pa_core * core)392 static void handle_all_profiles(pa_core *core) {
393 pa_card *card;
394 uint32_t state;
395
396 PA_IDXSET_FOREACH(card, core->cards, state) {
397 pa_card_profile *profile;
398 void *state2;
399
400 PA_HASHMAP_FOREACH(profile, card->profiles, state2)
401 profile_available_hook_callback(core, profile, NULL);
402 }
403 }
404
pa__init(pa_module * m)405 int pa__init(pa_module *m) {
406 pa_modargs *ma;
407 struct userdata *u;
408
409 pa_assert(m);
410
411 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
412 pa_log_error("Failed to parse module arguments");
413 goto fail;
414 }
415
416 m->userdata = u = pa_xnew0(struct userdata, 1);
417
418 u->auto_switch = 1;
419
420 if (pa_modargs_get_value(ma, "auto_switch", NULL)) {
421 bool auto_switch_bool;
422
423 /* auto_switch originally took a boolean value, let's keep
424 * compatibility with configuration files that still pass a boolean. */
425 if (pa_modargs_get_value_boolean(ma, "auto_switch", &auto_switch_bool) >= 0) {
426 if (auto_switch_bool)
427 u->auto_switch = 1;
428 else
429 u->auto_switch = 0;
430
431 } else if (pa_modargs_get_value_u32(ma, "auto_switch", &u->auto_switch) < 0) {
432 pa_log("Failed to parse auto_switch argument.");
433 goto fail;
434 }
435 }
436
437 u->enable_a2dp_source = true;
438 if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) {
439 pa_log("Failed to parse a2dp_source argument.");
440 goto fail;
441 }
442
443 u->enable_ag = true;
444 if (pa_modargs_get_value_boolean(ma, "ag", &u->enable_ag) < 0) {
445 pa_log("Failed to parse ag argument.");
446 goto fail;
447 }
448
449 u->will_need_revert_card_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
450
451 u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL,
452 (pa_hook_cb_t) source_put_hook_callback, u);
453
454 u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
455 (pa_hook_cb_t) sink_put_hook_callback, u);
456
457 if (u->auto_switch) {
458 u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
459 (pa_hook_cb_t) source_output_put_hook_callback, u);
460
461 u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL,
462 (pa_hook_cb_t) source_output_unlink_hook_callback, u);
463
464 u->card_init_profile_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL,
465 (pa_hook_cb_t) card_init_profile_hook_callback, u);
466
467 u->card_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_UNLINK], PA_HOOK_NORMAL,
468 (pa_hook_cb_t) card_unlink_hook_callback, u);
469 }
470
471 u->profile_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED],
472 PA_HOOK_NORMAL, (pa_hook_cb_t) profile_available_hook_callback, u);
473
474 handle_all_profiles(m->core);
475
476 pa_modargs_free(ma);
477 return 0;
478
479 fail:
480 if (ma)
481 pa_modargs_free(ma);
482 return -1;
483 }
484
pa__done(pa_module * m)485 void pa__done(pa_module *m) {
486 struct userdata *u;
487
488 pa_assert(m);
489
490 if (!(u = m->userdata))
491 return;
492
493 if (u->source_put_slot)
494 pa_hook_slot_free(u->source_put_slot);
495
496 if (u->sink_put_slot)
497 pa_hook_slot_free(u->sink_put_slot);
498
499 if (u->source_output_put_slot)
500 pa_hook_slot_free(u->source_output_put_slot);
501
502 if (u->source_output_unlink_slot)
503 pa_hook_slot_free(u->source_output_unlink_slot);
504
505 if (u->card_init_profile_slot)
506 pa_hook_slot_free(u->card_init_profile_slot);
507
508 if (u->card_unlink_slot)
509 pa_hook_slot_free(u->card_unlink_slot);
510
511 if (u->profile_available_changed_slot)
512 pa_hook_slot_free(u->profile_available_changed_slot);
513
514 pa_hashmap_free(u->will_need_revert_card_map);
515
516 pa_xfree(u);
517 }
518