]> code.delx.au - pulseaudio/blob - src/modules/bluetooth/module-bluetooth-policy.c
bluetooth: Support HFGW in module-bluetooth-policy
[pulseaudio] / src / modules / bluetooth / module-bluetooth-policy.c
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, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <pulse/xmalloc.h>
29
30 #include <pulsecore/core.h>
31 #include <pulsecore/modargs.h>
32 #include <pulsecore/source-output.h>
33 #include <pulsecore/source.h>
34 #include <pulsecore/core-util.h>
35
36 #include "module-bluetooth-policy-symdef.h"
37
38 PA_MODULE_AUTHOR("Frédéric Dalleau");
39 PA_MODULE_DESCRIPTION("When a bluetooth sink or source is added, load module-loopback");
40 PA_MODULE_VERSION(PACKAGE_VERSION);
41 PA_MODULE_LOAD_ONCE(TRUE);
42 PA_MODULE_USAGE(
43 "a2dp_source=<Handle a2dp_source card profile (sink role)?> "
44 "hfgw=<Handle hfgw card profile (headset role)?>");
45
46 static const char* const valid_modargs[] = {
47 "a2dp_source",
48 "hfgw",
49 NULL
50 };
51
52 struct userdata {
53 bool enable_a2dp_source;
54 bool enable_hfgw;
55 pa_hook_slot *source_put_slot;
56 pa_hook_slot *sink_put_slot;
57 };
58
59 /* When a source is created, loopback it to default sink */
60 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void *userdata) {
61 struct userdata *u = userdata;
62 const char *s;
63 const char *role;
64 char *args;
65
66 pa_assert(c);
67 pa_assert(source);
68
69 /* Only consider bluetooth sinks and sources */
70 s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS);
71 if (!s)
72 return PA_HOOK_OK;
73
74 if (!pa_streq(s, "bluetooth"))
75 return PA_HOOK_OK;
76
77 s = pa_proplist_gets(source->proplist, "bluetooth.protocol");
78 if (!s)
79 return PA_HOOK_OK;
80
81 if (u->enable_a2dp_source && pa_streq(s, "a2dp_source")) /* A2DP profile (we're doing sink role) */
82 role = "music";
83 else if (u->enable_hfgw && pa_streq(s, "hfgw")) /* HFP profile (we're doing headset role) */
84 role = "phone";
85 else {
86 pa_log_debug("Profile %s cannot be selected for loopback", s);
87 return PA_HOOK_OK;
88 }
89
90 /* Load module-loopback */
91 args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name, role);
92 (void) pa_module_load(c, "module-loopback", args);
93 pa_xfree(args);
94
95 return PA_HOOK_OK;
96 }
97
98 /* When a sink is created, loopback it to default source */
99 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void *userdata) {
100 struct userdata *u = userdata;
101 const char *s;
102 const char *role;
103 char *args;
104
105 pa_assert(c);
106 pa_assert(sink);
107
108 /* Only consider bluetooth sinks and sources */
109 s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS);
110 if (!s)
111 return PA_HOOK_OK;
112
113 if (!pa_streq(s, "bluetooth"))
114 return PA_HOOK_OK;
115
116 s = pa_proplist_gets(sink->proplist, "bluetooth.protocol");
117 if (!s)
118 return PA_HOOK_OK;
119
120 if (u->enable_hfgw && pa_streq(s, "hfgw")) /* HFP profile (we're doing headset role) */
121 role = "phone";
122 else {
123 pa_log_debug("Profile %s cannot be selected for loopback", s);
124 return PA_HOOK_OK;
125 }
126
127 /* Load module-loopback */
128 args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name, role);
129 (void) pa_module_load(c, "module-loopback", args);
130 pa_xfree(args);
131
132 return PA_HOOK_OK;
133 }
134
135 int pa__init(pa_module *m) {
136 pa_modargs *ma;
137 struct userdata *u;
138
139 pa_assert(m);
140
141 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
142 pa_log_error("Failed to parse module arguments");
143 return -1;
144 }
145
146 m->userdata = u = pa_xnew(struct userdata, 1);
147
148 u->enable_a2dp_source = TRUE;
149 if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) {
150 pa_log("Failed to parse a2dp_source argument.");
151 goto fail;
152 }
153
154 u->enable_hfgw = TRUE;
155 if (pa_modargs_get_value_boolean(ma, "hfgw", &u->enable_hfgw) < 0) {
156 pa_log("Failed to parse hfgw argument.");
157 goto fail;
158 }
159
160 u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) source_put_hook_callback, u);
161
162 u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_put_hook_callback, u);
163
164 pa_modargs_free(ma);
165 return 0;
166
167 fail:
168 pa_modargs_free(ma);
169 return -1;
170 }
171
172 void pa__done(pa_module *m) {
173 struct userdata *u;
174
175 pa_assert(m);
176
177 if (!(u = m->userdata))
178 return;
179
180 if (u->source_put_slot)
181 pa_hook_slot_free(u->source_put_slot);
182
183 if (u->sink_put_slot)
184 pa_hook_slot_free(u->sink_put_slot);
185
186 pa_xfree(u);
187 }