• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2008 Lennart Poettering
5   Copyright 2009 Colin Guthrie
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 published
9   by the Free Software Foundation; either version 2.1 of the License,
10   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 License
18   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <pulse/context.h>
26 #include <pulse/xmalloc.h>
27 #include <pulse/fork-detect.h>
28 #include <pulse/operation.h>
29 
30 #include <pulsecore/macro.h>
31 #include <pulsecore/pstream-util.h>
32 
33 #include "internal.h"
34 #include "ext-device-manager.h"
35 
36 enum {
37     SUBCOMMAND_TEST,
38     SUBCOMMAND_READ,
39     SUBCOMMAND_RENAME,
40     SUBCOMMAND_DELETE,
41     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
42     SUBCOMMAND_REORDER,
43     SUBCOMMAND_SUBSCRIBE,
44     SUBCOMMAND_EVENT
45 };
46 
ext_device_manager_test_cb(pa_pdispatch * pd,uint32_t command,uint32_t tag,pa_tagstruct * t,void * userdata)47 static void ext_device_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
48     pa_operation *o = userdata;
49     uint32_t version = PA_INVALID_INDEX;
50 
51     pa_assert(pd);
52     pa_assert(o);
53     pa_assert(PA_REFCNT_VALUE(o) >= 1);
54 
55     if (!o->context)
56         goto finish;
57 
58     if (command != PA_COMMAND_REPLY) {
59         if (pa_context_handle_error(o->context, command, t, false) < 0)
60             goto finish;
61 
62     } else if (pa_tagstruct_getu32(t, &version) < 0 ||
63                !pa_tagstruct_eof(t)) {
64 
65         pa_context_fail(o->context, PA_ERR_PROTOCOL);
66         goto finish;
67     }
68 
69     if (o->callback) {
70         pa_ext_device_manager_test_cb_t cb = (pa_ext_device_manager_test_cb_t) o->callback;
71         cb(o->context, version, o->userdata);
72     }
73 
74 finish:
75     pa_operation_done(o);
76     pa_operation_unref(o);
77 }
78 
pa_ext_device_manager_test(pa_context * c,pa_ext_device_manager_test_cb_t cb,void * userdata)79 pa_operation *pa_ext_device_manager_test(
80         pa_context *c,
81         pa_ext_device_manager_test_cb_t cb,
82         void *userdata) {
83 
84     uint32_t tag;
85     pa_operation *o;
86     pa_tagstruct *t;
87 
88     pa_assert(c);
89     pa_assert(PA_REFCNT_VALUE(c) >= 1);
90 
91     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
92     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
93     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
94 
95     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
96 
97     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
98     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
99     pa_tagstruct_puts(t, "module-device-manager");
100     pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
101     pa_pstream_send_tagstruct(c->pstream, t);
102     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
103 
104     return o;
105 }
106 
ext_device_manager_read_cb(pa_pdispatch * pd,uint32_t command,uint32_t tag,pa_tagstruct * t,void * userdata)107 static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
108     pa_operation *o = userdata;
109     int eol = 1;
110 
111     pa_assert(pd);
112     pa_assert(o);
113     pa_assert(PA_REFCNT_VALUE(o) >= 1);
114 
115     if (!o->context)
116         goto finish;
117 
118     if (command != PA_COMMAND_REPLY) {
119         if (pa_context_handle_error(o->context, command, t, false) < 0)
120             goto finish;
121 
122         eol = -1;
123     } else {
124 
125         while (!pa_tagstruct_eof(t)) {
126             pa_ext_device_manager_info i;
127 
128             memset(&i, 0, sizeof(i));
129 
130             if (pa_tagstruct_gets(t, &i.name) < 0 ||
131                 pa_tagstruct_gets(t, &i.description) < 0 ||
132                 pa_tagstruct_gets(t, &i.icon) < 0 ||
133                 pa_tagstruct_getu32(t, &i.index) < 0 ||
134                 pa_tagstruct_getu32(t, &i.n_role_priorities) < 0) {
135 
136                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
137                 goto finish;
138             }
139 
140             if (i.n_role_priorities > 0) {
141                 uint32_t j;
142                 i.role_priorities = pa_xnew0(pa_ext_device_manager_role_priority_info, i.n_role_priorities+1);
143 
144                 for (j = 0; j < i.n_role_priorities; j++) {
145 
146                     if (pa_tagstruct_gets(t, &i.role_priorities[j].role) < 0 ||
147                         pa_tagstruct_getu32(t, &i.role_priorities[j].priority) < 0) {
148 
149                         pa_context_fail(o->context, PA_ERR_PROTOCOL);
150                         pa_xfree(i.role_priorities);
151                         goto finish;
152                     }
153                 }
154 
155                 /* Terminate with an extra NULL entry, just to make sure */
156                 i.role_priorities[j].role = NULL;
157                 i.role_priorities[j].priority = 0;
158             }
159 
160             if (o->callback) {
161                 pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
162                 cb(o->context, &i, 0, o->userdata);
163             }
164 
165             pa_xfree(i.role_priorities);
166         }
167     }
168 
169     if (o->callback) {
170         pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
171         cb(o->context, NULL, eol, o->userdata);
172     }
173 
174 finish:
175     pa_operation_done(o);
176     pa_operation_unref(o);
177 }
178 
pa_ext_device_manager_read(pa_context * c,pa_ext_device_manager_read_cb_t cb,void * userdata)179 pa_operation *pa_ext_device_manager_read(
180         pa_context *c,
181         pa_ext_device_manager_read_cb_t cb,
182         void *userdata) {
183 
184     uint32_t tag;
185     pa_operation *o;
186     pa_tagstruct *t;
187 
188     pa_assert(c);
189     pa_assert(PA_REFCNT_VALUE(c) >= 1);
190 
191     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
192     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
193     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
194 
195     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
196 
197     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
198     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
199     pa_tagstruct_puts(t, "module-device-manager");
200     pa_tagstruct_putu32(t, SUBCOMMAND_READ);
201     pa_pstream_send_tagstruct(c->pstream, t);
202     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
203 
204     return o;
205 }
206 
pa_ext_device_manager_set_device_description(pa_context * c,const char * device,const char * description,pa_context_success_cb_t cb,void * userdata)207 pa_operation *pa_ext_device_manager_set_device_description(
208         pa_context *c,
209         const char* device,
210         const char* description,
211         pa_context_success_cb_t cb,
212         void *userdata) {
213 
214     uint32_t tag;
215     pa_operation *o = NULL;
216     pa_tagstruct *t = NULL;
217 
218     pa_assert(c);
219     pa_assert(PA_REFCNT_VALUE(c) >= 1);
220     pa_assert(device);
221     pa_assert(description);
222 
223     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
224     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
225     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
226     PA_CHECK_VALIDITY_RETURN_NULL(c, *description, PA_ERR_INVALID);
227 
228     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
229 
230     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
231     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
232     pa_tagstruct_puts(t, "module-device-manager");
233     pa_tagstruct_putu32(t, SUBCOMMAND_RENAME);
234 
235     pa_tagstruct_puts(t, device);
236     pa_tagstruct_puts(t, description);
237 
238     pa_pstream_send_tagstruct(c->pstream, t);
239     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
240 
241     return o;
242 }
243 
pa_ext_device_manager_delete(pa_context * c,const char * const s[],pa_context_success_cb_t cb,void * userdata)244 pa_operation *pa_ext_device_manager_delete(
245         pa_context *c,
246         const char *const s[],
247         pa_context_success_cb_t cb,
248         void *userdata) {
249 
250     uint32_t tag;
251     pa_operation *o = NULL;
252     pa_tagstruct *t = NULL;
253     const char *const *k;
254 
255     pa_assert(c);
256     pa_assert(PA_REFCNT_VALUE(c) >= 1);
257     pa_assert(s);
258 
259     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
260     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
261     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
262 
263     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
264 
265     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
266     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
267     pa_tagstruct_puts(t, "module-device-manager");
268     pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
269 
270     for (k = s; *k; k++) {
271         if (!*k || !**k)
272             goto fail;
273 
274         pa_tagstruct_puts(t, *k);
275     }
276 
277     pa_pstream_send_tagstruct(c->pstream, t);
278     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
279 
280     return o;
281 
282 fail:
283     if (o) {
284         pa_operation_cancel(o);
285         pa_operation_unref(o);
286     }
287 
288     if (t)
289         pa_tagstruct_free(t);
290 
291     pa_context_set_error(c, PA_ERR_INVALID);
292     return NULL;
293 }
294 
pa_ext_device_manager_enable_role_device_priority_routing(pa_context * c,int enable,pa_context_success_cb_t cb,void * userdata)295 pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
296         pa_context *c,
297         int enable,
298         pa_context_success_cb_t cb,
299         void *userdata) {
300 
301     uint32_t tag;
302     pa_operation *o = NULL;
303     pa_tagstruct *t = NULL;
304 
305     pa_assert(c);
306     pa_assert(PA_REFCNT_VALUE(c) >= 1);
307 
308     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
309     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
310     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
311 
312     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
313 
314     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
315     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
316     pa_tagstruct_puts(t, "module-device-manager");
317     pa_tagstruct_putu32(t, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING);
318     pa_tagstruct_put_boolean(t, !!enable);
319 
320     pa_pstream_send_tagstruct(c->pstream, t);
321     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
322 
323     return o;
324 }
325 
pa_ext_device_manager_reorder_devices_for_role(pa_context * c,const char * role,const char ** devices,pa_context_success_cb_t cb,void * userdata)326 pa_operation *pa_ext_device_manager_reorder_devices_for_role(
327         pa_context *c,
328         const char* role,
329         const char** devices,
330         pa_context_success_cb_t cb,
331         void *userdata) {
332 
333     uint32_t tag, i;
334     pa_operation *o = NULL;
335     pa_tagstruct *t = NULL;
336 
337     pa_assert(c);
338     pa_assert(PA_REFCNT_VALUE(c) >= 1);
339 
340     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
341     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
342     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
343 
344     pa_assert(role);
345     pa_assert(devices);
346 
347     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
348 
349     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
350     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
351     pa_tagstruct_puts(t, "module-device-manager");
352     pa_tagstruct_putu32(t, SUBCOMMAND_REORDER);
353     pa_tagstruct_puts(t, role);
354 
355     i = 0; while (devices[i]) i++;
356     pa_tagstruct_putu32(t, i);
357 
358     i = 0;
359     while (devices[i])
360         pa_tagstruct_puts(t, devices[i++]);
361 
362     pa_pstream_send_tagstruct(c->pstream, t);
363     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
364 
365     return o;
366 }
367 
pa_ext_device_manager_subscribe(pa_context * c,int enable,pa_context_success_cb_t cb,void * userdata)368 pa_operation *pa_ext_device_manager_subscribe(
369         pa_context *c,
370         int enable,
371         pa_context_success_cb_t cb,
372         void *userdata) {
373 
374     uint32_t tag;
375     pa_operation *o;
376     pa_tagstruct *t;
377 
378     pa_assert(c);
379     pa_assert(PA_REFCNT_VALUE(c) >= 1);
380 
381     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
382     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
383     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
384 
385     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
386 
387     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
388     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
389     pa_tagstruct_puts(t, "module-device-manager");
390     pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
391     pa_tagstruct_put_boolean(t, enable);
392     pa_pstream_send_tagstruct(c->pstream, t);
393     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
394 
395     return o;
396 }
397 
pa_ext_device_manager_set_subscribe_cb(pa_context * c,pa_ext_device_manager_subscribe_cb_t cb,void * userdata)398 void pa_ext_device_manager_set_subscribe_cb(
399         pa_context *c,
400         pa_ext_device_manager_subscribe_cb_t cb,
401         void *userdata) {
402 
403     pa_assert(c);
404     pa_assert(PA_REFCNT_VALUE(c) >= 1);
405 
406     if (pa_detect_fork())
407         return;
408 
409     c->ext_device_manager.callback = cb;
410     c->ext_device_manager.userdata = userdata;
411 }
412 
pa_ext_device_manager_command(pa_context * c,uint32_t tag,pa_tagstruct * t)413 void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
414     uint32_t subcommand;
415 
416     pa_assert(c);
417     pa_assert(PA_REFCNT_VALUE(c) >= 1);
418     pa_assert(t);
419 
420     if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
421         !pa_tagstruct_eof(t)) {
422 
423         pa_context_fail(c, PA_ERR_PROTOCOL);
424         return;
425     }
426 
427     if (subcommand != SUBCOMMAND_EVENT) {
428         pa_context_fail(c, PA_ERR_PROTOCOL);
429         return;
430     }
431 
432     if (c->ext_device_manager.callback)
433         c->ext_device_manager.callback(c, c->ext_device_manager.userdata);
434 }
435