1/***
2  This file is part of PulseAudio.
3
4  Copyright 2008 Lennart Poettering
5  Copyright 2011 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/gccmacro.h>
27#include <pulse/xmalloc.h>
28#include <pulse/fork-detect.h>
29#include <pulse/operation.h>
30#include <pulse/format.h>
31
32#include <pulsecore/macro.h>
33#include <pulsecore/pstream-util.h>
34
35#include "internal.h"
36#include "ext-device-restore.h"
37
38/* Protocol extension commands */
39enum {
40    SUBCOMMAND_TEST,
41    SUBCOMMAND_SUBSCRIBE,
42    SUBCOMMAND_EVENT,
43    SUBCOMMAND_READ_FORMATS_ALL,
44    SUBCOMMAND_READ_FORMATS,
45    SUBCOMMAND_SAVE_FORMATS
46};
47
48static void ext_device_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
49    pa_operation *o = userdata;
50    uint32_t version = PA_INVALID_INDEX;
51
52    pa_assert(pd);
53    pa_assert(o);
54    pa_assert(PA_REFCNT_VALUE(o) >= 1);
55
56    if (!o->context)
57        goto finish;
58
59    if (command != PA_COMMAND_REPLY) {
60        if (pa_context_handle_error(o->context, command, t, false) < 0)
61            goto finish;
62
63    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
64               !pa_tagstruct_eof(t)) {
65
66        pa_context_fail(o->context, PA_ERR_PROTOCOL);
67        goto finish;
68    }
69
70    if (o->callback) {
71        pa_ext_device_restore_test_cb_t cb = (pa_ext_device_restore_test_cb_t) o->callback;
72        cb(o->context, version, o->userdata);
73    }
74
75finish:
76    pa_operation_done(o);
77    pa_operation_unref(o);
78}
79
80pa_operation *pa_ext_device_restore_test(
81        pa_context *c,
82        pa_ext_device_restore_test_cb_t cb,
83        void *userdata) {
84
85    uint32_t tag;
86    pa_operation *o;
87    pa_tagstruct *t;
88
89    pa_assert(c);
90    pa_assert(PA_REFCNT_VALUE(c) >= 1);
91
92    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
93    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
94    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
95
96    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
97
98    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
99    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
100    pa_tagstruct_puts(t, "module-device-restore");
101    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
102    pa_pstream_send_tagstruct(c->pstream, t);
103    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
104
105    return o;
106}
107
108pa_operation *pa_ext_device_restore_subscribe(
109        pa_context *c,
110        int enable,
111        pa_context_success_cb_t cb,
112        void *userdata) {
113
114    uint32_t tag;
115    pa_operation *o;
116    pa_tagstruct *t;
117
118    pa_assert(c);
119    pa_assert(PA_REFCNT_VALUE(c) >= 1);
120
121    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
122    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
123    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
124
125    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
126
127    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
128    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
129    pa_tagstruct_puts(t, "module-device-restore");
130    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
131    pa_tagstruct_put_boolean(t, enable);
132    pa_pstream_send_tagstruct(c->pstream, t);
133    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);
134
135    return o;
136}
137
138void pa_ext_device_restore_set_subscribe_cb(
139        pa_context *c,
140        pa_ext_device_restore_subscribe_cb_t cb,
141        void *userdata) {
142
143    pa_assert(c);
144    pa_assert(PA_REFCNT_VALUE(c) >= 1);
145
146    if (pa_detect_fork())
147        return;
148
149    c->ext_device_restore.callback = cb;
150    c->ext_device_restore.userdata = userdata;
151}
152
153static void ext_device_restore_read_device_formats_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
154    pa_operation *o = userdata;
155    int eol = 1;
156
157    pa_assert(pd);
158    pa_assert(o);
159    pa_assert(PA_REFCNT_VALUE(o) >= 1);
160
161    if (!o->context)
162        goto finish;
163
164    if (command != PA_COMMAND_REPLY) {
165        if (pa_context_handle_error(o->context, command, t, false) < 0)
166            goto finish;
167
168        eol = -1;
169    } else {
170        uint8_t j;
171
172        while (!pa_tagstruct_eof(t)) {
173            pa_ext_device_restore_info i;
174            pa_zero(i);
175
176            if (pa_tagstruct_getu32(t, &i.type) < 0 ||
177                pa_tagstruct_getu32(t, &i.index) < 0 ||
178                pa_tagstruct_getu8(t, &i.n_formats) < 0) {
179
180                pa_context_fail(o->context, PA_ERR_PROTOCOL);
181                goto finish;
182            }
183
184            if (PA_DEVICE_TYPE_SINK != i.type && PA_DEVICE_TYPE_SOURCE != i.type) {
185                pa_context_fail(o->context, PA_ERR_PROTOCOL);
186                goto finish;
187            }
188
189            if (i.index == PA_INVALID_INDEX) {
190                pa_context_fail(o->context, PA_ERR_PROTOCOL);
191                goto finish;
192            }
193
194            if (i.n_formats > 0) {
195                i.formats = pa_xnew0(pa_format_info*, i.n_formats);
196
197                for (j = 0; j < i.n_formats; j++) {
198
199                    pa_format_info *f = i.formats[j] = pa_format_info_new();
200                    if (pa_tagstruct_get_format_info(t, f) < 0) {
201                        uint8_t k;
202
203                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
204                        for (k = 0; k < j+1; k++)
205                            pa_format_info_free(i.formats[k]);
206                        pa_xfree(i.formats);
207                        goto finish;
208                    }
209                }
210            }
211
212            if (o->callback) {
213                pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
214                cb(o->context, &i, 0, o->userdata);
215            }
216
217            for (j = 0; j < i.n_formats; j++)
218                pa_format_info_free(i.formats[j]);
219            pa_xfree(i.formats);
220        }
221    }
222
223    if (o->callback) {
224        pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
225        cb(o->context, NULL, eol, o->userdata);
226    }
227
228finish:
229    pa_operation_done(o);
230    pa_operation_unref(o);
231}
232
233pa_operation *pa_ext_device_restore_read_formats_all(
234        pa_context *c,
235        pa_ext_device_restore_read_device_formats_cb_t cb,
236        void *userdata) {
237
238    uint32_t tag;
239    pa_operation *o;
240    pa_tagstruct *t;
241
242    pa_assert(c);
243    pa_assert(PA_REFCNT_VALUE(c) >= 1);
244
245    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
246    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
247    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
248
249    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
250
251    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
252    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
253    pa_tagstruct_puts(t, "module-device-restore");
254    pa_tagstruct_putu32(t, SUBCOMMAND_READ_FORMATS_ALL);
255    pa_pstream_send_tagstruct(c->pstream, t);
256    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
257
258    return o;
259}
260
261pa_operation *pa_ext_device_restore_read_formats(
262        pa_context *c,
263        pa_device_type_t type,
264        uint32_t idx,
265        pa_ext_device_restore_read_device_formats_cb_t cb,
266        void *userdata) {
267
268    uint32_t tag;
269    pa_operation *o;
270    pa_tagstruct *t;
271
272    pa_assert(c);
273    pa_assert(PA_REFCNT_VALUE(c) >= 1);
274    pa_assert(idx != PA_INVALID_INDEX);
275
276    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
277    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
278    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
279
280    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
281
282    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
283    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
284    pa_tagstruct_puts(t, "module-device-restore");
285    pa_tagstruct_putu32(t, SUBCOMMAND_READ_FORMATS);
286    pa_tagstruct_putu32(t, type);
287    pa_tagstruct_putu32(t, idx);
288    pa_pstream_send_tagstruct(c->pstream, t);
289    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
290
291    return o;
292}
293
294pa_operation *pa_ext_device_restore_save_formats(
295        pa_context *c,
296        pa_device_type_t type,
297        uint32_t idx,
298        uint8_t n_formats,
299        pa_format_info **formats,
300        pa_context_success_cb_t cb,
301        void *userdata) {
302
303    uint32_t tag;
304    pa_operation *o;
305    pa_tagstruct *t;
306    uint8_t j;
307
308    pa_assert(c);
309    pa_assert(PA_REFCNT_VALUE(c) >= 1);
310    pa_assert(idx != PA_INVALID_INDEX);
311    pa_assert(n_formats > 0);
312    pa_assert(formats && *formats);
313
314    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
315    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
316    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
317
318    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
319
320    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
321    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
322    pa_tagstruct_puts(t, "module-device-restore");
323    pa_tagstruct_putu32(t, SUBCOMMAND_SAVE_FORMATS);
324
325    pa_tagstruct_putu32(t, type);
326    pa_tagstruct_putu32(t, idx);
327    pa_tagstruct_putu8(t, n_formats);
328    for (j = 0; j < n_formats; j++)
329        pa_tagstruct_put_format_info(t, formats[j]);
330
331    pa_pstream_send_tagstruct(c->pstream, t);
332    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);
333
334    return o;
335}
336
337/* Command function defined in internal.h */
338void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
339    uint32_t subcommand;
340    pa_device_type_t type;
341    uint32_t idx;
342
343    pa_assert(c);
344    pa_assert(PA_REFCNT_VALUE(c) >= 1);
345    pa_assert(t);
346
347    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
348        pa_tagstruct_getu32(t, &type) < 0 ||
349        pa_tagstruct_getu32(t, &idx) < 0 ||
350        !pa_tagstruct_eof(t)) {
351
352        pa_context_fail(c, PA_ERR_PROTOCOL);
353        return;
354    }
355
356    if (subcommand != SUBCOMMAND_EVENT) {
357        pa_context_fail(c, PA_ERR_PROTOCOL);
358        return;
359    }
360
361    if (PA_DEVICE_TYPE_SINK != type && PA_DEVICE_TYPE_SOURCE != type) {
362        pa_context_fail(c, PA_ERR_PROTOCOL);
363        return;
364    }
365
366    if (idx == PA_INVALID_INDEX) {
367        pa_context_fail(c, PA_ERR_PROTOCOL);
368        return;
369    }
370
371    if (c->ext_device_restore.callback)
372        c->ext_device_restore.callback(c, type, idx, c->ext_device_restore.userdata);
373}
374