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