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