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