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
36enum {
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
47static 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
74finish:
75    pa_operation_done(o);
76    pa_operation_unref(o);
77}
78
79pa_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
107static 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
174finish:
175    pa_operation_done(o);
176    pa_operation_unref(o);
177}
178
179pa_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
207pa_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
244pa_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
282fail:
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
295pa_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
326pa_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
368pa_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
398void 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
413void 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