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 */
39 enum {
40 SUBCOMMAND_TEST,
41 SUBCOMMAND_SUBSCRIBE,
42 SUBCOMMAND_EVENT,
43 SUBCOMMAND_READ_FORMATS_ALL,
44 SUBCOMMAND_READ_FORMATS,
45 SUBCOMMAND_SAVE_FORMATS
46 };
47
ext_device_restore_test_cb(pa_pdispatch * pd,uint32_t command,uint32_t tag,pa_tagstruct * t,void * userdata)48 static 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
75 finish:
76 pa_operation_done(o);
77 pa_operation_unref(o);
78 }
79
pa_ext_device_restore_test(pa_context * c,pa_ext_device_restore_test_cb_t cb,void * userdata)80 pa_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
pa_ext_device_restore_subscribe(pa_context * c,int enable,pa_context_success_cb_t cb,void * userdata)108 pa_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
pa_ext_device_restore_set_subscribe_cb(pa_context * c,pa_ext_device_restore_subscribe_cb_t cb,void * userdata)138 void 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
ext_device_restore_read_device_formats_cb(pa_pdispatch * pd,uint32_t command,uint32_t tag,pa_tagstruct * t,void * userdata)153 static 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
228 finish:
229 pa_operation_done(o);
230 pa_operation_unref(o);
231 }
232
pa_ext_device_restore_read_formats_all(pa_context * c,pa_ext_device_restore_read_device_formats_cb_t cb,void * userdata)233 pa_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
pa_ext_device_restore_read_formats(pa_context * c,pa_device_type_t type,uint32_t idx,pa_ext_device_restore_read_device_formats_cb_t cb,void * userdata)261 pa_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
pa_ext_device_restore_save_formats(pa_context * c,pa_device_type_t type,uint32_t idx,uint8_t n_formats,pa_format_info ** formats,pa_context_success_cb_t cb,void * userdata)294 pa_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 */
pa_ext_device_restore_command(pa_context * c,uint32_t tag,pa_tagstruct * t)338 void 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