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 or handsfree_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") || pa_streq(s, "handsfree_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") || pa_streq(s, "handsfree_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") && !pa_streq(profile->name, "handsfree_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 headset profile */
194 if (!pa_streq(card->active_profile->name, "headset_head_unit") && !pa_streq(card->active_profile->name, "handsfree_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 headset profile */
206 if (pa_streq(card->active_profile->name, "headset_head_unit") || pa_streq(card->active_profile->name, "handsfree_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") ||
362 pa_streq(profile->name, "headset_head_unit") ||
363 pa_streq(profile->name, "handsfree_head_unit"))
364 return PA_HOOK_OK;
365
366 is_active_profile = card->active_profile == profile;
367
368 if (profile->available == PA_AVAILABLE_YES) {
369 if (is_active_profile)
370 return PA_HOOK_OK;
371
372 if (card->active_profile->available == PA_AVAILABLE_YES && card->active_profile->priority >= profile->priority)
373 return PA_HOOK_OK;
374
375 selected_profile = profile;
376 } else {
377 if (!is_active_profile)
378 return PA_HOOK_OK;
379
380 pa_assert_se((selected_profile = find_best_profile(card)));
381
382 if (selected_profile == card->active_profile)
383 return PA_HOOK_OK;
384 }
385
386 pa_log_debug("Setting card '%s' to profile '%s'", card->name, selected_profile->name);
387
388 if (pa_card_set_profile(card, selected_profile, false) != 0)
389 pa_log_warn("Could not set profile '%s'", selected_profile->name);
390
391 return PA_HOOK_OK;
392 }
393
handle_all_profiles(pa_core *core)394 static void handle_all_profiles(pa_core *core) {
395 pa_card *card;
396 uint32_t state;
397
398 PA_IDXSET_FOREACH(card, core->cards, state) {
399 pa_card_profile *profile;
400 void *state2;
401
402 PA_HASHMAP_FOREACH(profile, card->profiles, state2)
403 profile_available_hook_callback(core, profile, NULL);
404 }
405 }
406
pa__init(pa_module *m)407 int pa__init(pa_module *m) {
408 pa_modargs *ma;
409 struct userdata *u;
410
411 pa_assert(m);
412
413 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
414 pa_log_error("Failed to parse module arguments");
415 goto fail;
416 }
417
418 m->userdata = u = pa_xnew0(struct userdata, 1);
419
420 u->auto_switch = 1;
421
422 if (pa_modargs_get_value(ma, "auto_switch", NULL)) {
423 bool auto_switch_bool;
424
425 /* auto_switch originally took a boolean value, let's keep
426 * compatibility with configuration files that still pass a boolean. */
427 if (pa_modargs_get_value_boolean(ma, "auto_switch", &auto_switch_bool) >= 0) {
428 if (auto_switch_bool)
429 u->auto_switch = 1;
430 else
431 u->auto_switch = 0;
432
433 } else if (pa_modargs_get_value_u32(ma, "auto_switch", &u->auto_switch) < 0) {
434 pa_log("Failed to parse auto_switch argument.");
435 goto fail;
436 }
437 }
438
439 u->enable_a2dp_source = true;
440 if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) {
441 pa_log("Failed to parse a2dp_source argument.");
442 goto fail;
443 }
444
445 u->enable_ag = true;
446 if (pa_modargs_get_value_boolean(ma, "ag", &u->enable_ag) < 0) {
447 pa_log("Failed to parse ag argument.");
448 goto fail;
449 }
450
451 u->will_need_revert_card_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
452
453 u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL,
454 (pa_hook_cb_t) source_put_hook_callback, u);
455
456 u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
457 (pa_hook_cb_t) sink_put_hook_callback, u);
458
459 if (u->auto_switch) {
460 u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
461 (pa_hook_cb_t) source_output_put_hook_callback, u);
462
463 u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL,
464 (pa_hook_cb_t) source_output_unlink_hook_callback, u);
465
466 u->card_init_profile_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL,
467 (pa_hook_cb_t) card_init_profile_hook_callback, u);
468
469 u->card_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_UNLINK], PA_HOOK_NORMAL,
470 (pa_hook_cb_t) card_unlink_hook_callback, u);
471 }
472
473 u->profile_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED],
474 PA_HOOK_NORMAL, (pa_hook_cb_t) profile_available_hook_callback, u);
475
476 handle_all_profiles(m->core);
477
478 pa_modargs_free(ma);
479 return 0;
480
481 fail:
482 if (ma)
483 pa_modargs_free(ma);
484 return -1;
485 }
486
pa__done(pa_module *m)487 void pa__done(pa_module *m) {
488 struct userdata *u;
489
490 pa_assert(m);
491
492 if (!(u = m->userdata))
493 return;
494
495 if (u->source_put_slot)
496 pa_hook_slot_free(u->source_put_slot);
497
498 if (u->sink_put_slot)
499 pa_hook_slot_free(u->sink_put_slot);
500
501 if (u->source_output_put_slot)
502 pa_hook_slot_free(u->source_output_put_slot);
503
504 if (u->source_output_unlink_slot)
505 pa_hook_slot_free(u->source_output_unlink_slot);
506
507 if (u->card_init_profile_slot)
508 pa_hook_slot_free(u->card_init_profile_slot);
509
510 if (u->card_unlink_slot)
511 pa_hook_slot_free(u->card_unlink_slot);
512
513 if (u->profile_available_changed_slot)
514 pa_hook_slot_free(u->profile_available_changed_slot);
515
516 pa_hashmap_free(u->will_need_revert_card_map);
517
518 pa_xfree(u);
519 }
520