]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Rough framework (slots etc.) for handling routing.
[pulseaudio] / src / modules / module-device-manager.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006-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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
54
55 #include "module-device-manager-symdef.h"
56
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
62 "on_hotplug=<When new device becomes available, recheck streams?> "
63 "on_rescue=<When device becomes unavailable, recheck streams?>");
64
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
66
67 static const char* const valid_modargs[] = {
68 "on_hotplug",
69 "on_rescue",
70 NULL
71 };
72
73 struct userdata {
74 pa_core *core;
75 pa_module *module;
76 pa_subscription *subscription;
77 pa_hook_slot
78 *sink_new_hook_slot,
79 *source_new_hook_slot,
80 *sink_input_new_hook_slot,
81 *source_output_new_hook_slot,
82 *sink_put_hook_slot,
83 *source_put_hook_slot,
84 *sink_unlink_hook_slot,
85 *source_unlink_hook_slot,
86 *connection_unlink_hook_slot;
87 pa_time_event *save_time_event;
88 pa_database *database;
89
90 pa_native_protocol *protocol;
91 pa_idxset *subscribed;
92
93 pa_bool_t on_hotplug;
94 pa_bool_t on_rescue;
95 pa_bool_t role_device_priority_routing;
96 pa_bool_t stream_restore_used;
97 pa_bool_t checked_stream_restore;
98 };
99
100 #define ENTRY_VERSION 1
101
102 #define NUM_ROLES 9
103 enum {
104 ROLE_NONE,
105 ROLE_VIDEO,
106 ROLE_MUSIC,
107 ROLE_GAME,
108 ROLE_EVENT,
109 ROLE_PHONE,
110 ROLE_ANIMATION,
111 ROLE_PRODUCTION,
112 ROLE_A11Y,
113 };
114
115 typedef uint32_t role_indexes_t[NUM_ROLES];
116
117 struct entry {
118 uint8_t version;
119 char description[PA_NAME_MAX];
120 role_indexes_t priority;
121 } PA_GCC_PACKED;
122
123 enum {
124 SUBCOMMAND_TEST,
125 SUBCOMMAND_READ,
126 SUBCOMMAND_RENAME,
127 SUBCOMMAND_DELETE,
128 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
129 SUBCOMMAND_PREFER_DEVICE,
130 SUBCOMMAND_DEFER_DEVICE,
131 SUBCOMMAND_SUBSCRIBE,
132 SUBCOMMAND_EVENT
133 };
134
135 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
136 struct userdata *u = userdata;
137
138 pa_assert(a);
139 pa_assert(e);
140 pa_assert(u);
141
142 pa_assert(e == u->save_time_event);
143 u->core->mainloop->time_free(u->save_time_event);
144 u->save_time_event = NULL;
145
146 pa_database_sync(u->database);
147 pa_log_info("Synced.");
148 }
149
150 static struct entry* read_entry(struct userdata *u, const char *name) {
151 pa_datum key, data;
152 struct entry *e;
153
154 pa_assert(u);
155 pa_assert(name);
156
157 key.data = (char*) name;
158 key.size = strlen(name);
159
160 pa_zero(data);
161
162 if (!pa_database_get(u->database, &key, &data))
163 goto fail;
164
165 if (data.size != sizeof(struct entry)) {
166 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
167 goto fail;
168 }
169
170 e = (struct entry*) data.data;
171
172 if (e->version != ENTRY_VERSION) {
173 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
174 goto fail;
175 }
176
177 if (!memchr(e->description, 0, sizeof(e->description))) {
178 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
179 goto fail;
180 }
181
182 return e;
183
184 fail:
185
186 pa_datum_free(&data);
187 return NULL;
188 }
189
190 static void trigger_save(struct userdata *u) {
191 pa_native_connection *c;
192 uint32_t idx;
193
194 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
195 pa_tagstruct *t;
196
197 t = pa_tagstruct_new(NULL, 0);
198 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
199 pa_tagstruct_putu32(t, 0);
200 pa_tagstruct_putu32(t, u->module->index);
201 pa_tagstruct_puts(t, u->module->name);
202 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
203
204 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
205 }
206
207 if (u->save_time_event)
208 return;
209
210 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
211 }
212
213 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
214 if (strncmp(a->description, b->description, sizeof(a->description)))
215 return FALSE;
216
217 return TRUE;
218 }
219
220 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
221 struct entry *old;
222
223 pa_assert(u);
224 pa_assert(entry);
225 pa_assert(name);
226 pa_assert(prefix);
227
228 if ((old = read_entry(u, name)))
229 *entry = *old;
230 else {
231 /* This is a new device, so make sure we write it's priority list correctly */
232 role_indexes_t max_priority;
233 pa_datum key;
234 pa_bool_t done;
235
236 pa_zero(max_priority);
237 done = !pa_database_first(u->database, &key, NULL);
238
239 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
240 while (!done) {
241 pa_datum next_key;
242
243 done = !pa_database_next(u->database, &key, &next_key, NULL);
244
245 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
246 char *name2;
247 struct entry *e;
248
249 name2 = pa_xstrndup(key.data, key.size);
250
251 if ((e = read_entry(u, name2))) {
252 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
253 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
254 }
255
256 pa_xfree(e);
257 }
258
259 pa_xfree(name2);
260 }
261 pa_datum_free(&key);
262 key = next_key;
263 }
264
265 /* Actually initialise our entry now we've calculated it */
266 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
267 entry->priority[i] = max_priority[i] + 1;
268 }
269 }
270
271 return old;
272 }
273
274 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
275 struct userdata *u = userdata;
276 struct entry entry, *old = NULL;
277 char *name = NULL;
278 pa_datum key, data;
279
280 pa_assert(c);
281 pa_assert(u);
282
283 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
284 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
285 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
286 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
287 return;
288
289 pa_zero(entry);
290 entry.version = ENTRY_VERSION;
291
292 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
293 pa_sink *sink;
294
295 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
296 return;
297
298 name = pa_sprintf_malloc("sink:%s", sink->name);
299
300 old = load_or_initialize_entry(u, &entry, name, "sink:");
301
302 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
303
304 } else {
305 pa_source *source;
306
307 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
308
309 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
310 return;
311
312 if (source->monitor_of)
313 return;
314
315 name = pa_sprintf_malloc("source:%s", source->name);
316
317 old = load_or_initialize_entry(u, &entry, name, "source:");
318
319 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
320 }
321
322 if (old) {
323
324 if (entries_equal(old, &entry)) {
325 pa_xfree(old);
326 pa_xfree(name);
327 return;
328 }
329
330 pa_xfree(old);
331 }
332
333 key.data = name;
334 key.size = strlen(name);
335
336 data.data = &entry;
337 data.size = sizeof(entry);
338
339 pa_log_info("Storing device %s.", name);
340
341 pa_database_set(u->database, &key, &data, TRUE);
342
343 pa_xfree(name);
344
345 trigger_save(u);
346 }
347
348 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
349 char *name;
350 struct entry *e;
351
352 pa_assert(c);
353 pa_assert(new_data);
354 pa_assert(u);
355
356 name = pa_sprintf_malloc("sink:%s", new_data->name);
357
358 if ((e = read_entry(u, name))) {
359 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
360 pa_log_info("Restoring description for sink %s.", new_data->name);
361 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
362 }
363
364 pa_xfree(e);
365 }
366
367 pa_xfree(name);
368
369 return PA_HOOK_OK;
370 }
371
372 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
373 char *name;
374 struct entry *e;
375
376 pa_assert(c);
377 pa_assert(new_data);
378 pa_assert(u);
379
380 name = pa_sprintf_malloc("source:%s", new_data->name);
381
382 if ((e = read_entry(u, name))) {
383 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
384 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
385 pa_log_info("Restoring description for source %s.", new_data->name);
386 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
387 }
388
389 pa_xfree(e);
390 }
391
392 pa_xfree(name);
393
394 return PA_HOOK_OK;
395 }
396
397 static char *get_name(const char *key, const char *prefix) {
398 char *t;
399
400 if (strncmp(key, prefix, strlen(prefix)))
401 return NULL;
402
403 t = pa_xstrdup(key + strlen(prefix));
404 return t;
405 }
406
407 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
408 char *name;
409 struct entry *e;
410
411 pa_assert(c);
412 pa_assert(new_data);
413 pa_assert(u);
414
415 if (!u->role_device_priority_routing)
416 return PA_HOOK_OK;
417
418 /*if (!(name = get_name(new_data->proplist, "sink-input")))
419 return PA_HOOK_OK;
420
421 if (new_data->sink)
422 pa_log_debug("Not restoring device for stream %s, because already set.", name);
423 else if ((e = read_entry(u, name))) {
424
425 pa_xfree(e);
426 }
427
428 pa_xfree(name);*/
429
430 return PA_HOOK_OK;
431 }
432
433 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
434 char *name;
435 struct entry *e;
436
437 pa_assert(c);
438 pa_assert(new_data);
439 pa_assert(u);
440
441 if (!u->role_device_priority_routing)
442 return PA_HOOK_OK;
443
444 if (new_data->direct_on_input)
445 return PA_HOOK_OK;
446
447 /*if (!(name = get_name(new_data->proplist, "source-output")))
448 return PA_HOOK_OK;
449
450 if (new_data->source)
451 pa_log_debug("Not restoring device for stream %s, because already set", name);
452 else if ((e = read_entry(u, name))) {
453
454 pa_xfree(e);
455 }
456
457 pa_xfree(name);*/
458
459 return PA_HOOK_OK;
460 }
461
462 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
463 pa_sink_input *si;
464 uint32_t idx;
465
466 pa_assert(c);
467 pa_assert(sink);
468 pa_assert(u);
469 pa_assert(u->on_hotplug);
470
471 if (!u->role_device_priority_routing)
472 return PA_HOOK_OK;
473
474 /** @todo Ensure redo the routing based on the priorities */
475
476 return PA_HOOK_OK;
477 }
478
479 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
480 pa_source_output *so;
481 uint32_t idx;
482
483 pa_assert(c);
484 pa_assert(source);
485 pa_assert(u);
486 pa_assert(u->on_hotplug);
487
488 if (!u->role_device_priority_routing)
489 return PA_HOOK_OK;
490
491 /** @todo Ensure redo the routing based on the priorities */
492
493 return PA_HOOK_OK;
494 }
495
496 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
497 pa_sink_input *si;
498 uint32_t idx;
499
500 pa_assert(c);
501 pa_assert(sink);
502 pa_assert(u);
503 pa_assert(u->on_rescue);
504
505 /* There's no point in doing anything if the core is shut down anyway */
506 if (c->state == PA_CORE_SHUTDOWN)
507 return PA_HOOK_OK;
508
509 if (!u->role_device_priority_routing)
510 return PA_HOOK_OK;
511
512 /** @todo Ensure redo the routing based on the priorities */
513
514 return PA_HOOK_OK;
515 }
516
517 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
518 pa_source_output *so;
519 uint32_t idx;
520
521 pa_assert(c);
522 pa_assert(source);
523 pa_assert(u);
524 pa_assert(u->on_rescue);
525
526 /* There's no point in doing anything if the core is shut down anyway */
527 if (c->state == PA_CORE_SHUTDOWN)
528 return PA_HOOK_OK;
529
530 if (!u->role_device_priority_routing)
531 return PA_HOOK_OK;
532
533 /** @todo Ensure redo the routing based on the priorities */
534
535 return PA_HOOK_OK;
536 }
537
538
539 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
540 pa_sink *sink;
541 pa_source *source;
542 uint32_t idx;
543 char *n;
544
545 pa_assert(u);
546 pa_assert(name);
547 pa_assert(e);
548
549 if ((n = get_name(name, "sink:"))) {
550 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
551 if (!pa_streq(sink->name, n)) {
552 continue;
553 }
554
555 pa_log_info("Setting description for sink %s.", sink->name);
556 pa_sink_set_description(sink, e->description);
557 }
558 pa_xfree(n);
559 }
560 else if ((n = get_name(name, "source:"))) {
561 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
562 if (!pa_streq(source->name, n)) {
563 continue;
564 }
565
566 if (source->monitor_of) {
567 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
568 continue;
569 }
570
571 pa_log_info("Setting description for source %s.", source->name);
572 pa_source_set_description(source, e->description);
573 }
574 pa_xfree(n);
575 }
576 }
577
578
579 static uint32_t get_role_index(const char* role) {
580 pa_assert(role);
581
582 if (strcmp(role, "") == 0)
583 return ROLE_NONE;
584 if (strcmp(role, "video") == 0)
585 return ROLE_VIDEO;
586 if (strcmp(role, "music") == 0)
587 return ROLE_MUSIC;
588 if (strcmp(role, "game") == 0)
589 return ROLE_GAME;
590 if (strcmp(role, "event") == 0)
591 return ROLE_EVENT;
592 if (strcmp(role, "phone") == 0)
593 return ROLE_PHONE;
594 if (strcmp(role, "animation") == 0)
595 return ROLE_ANIMATION;
596 if (strcmp(role, "production") == 0)
597 return ROLE_PRODUCTION;
598 if (strcmp(role, "a11y") == 0)
599 return ROLE_A11Y;
600 return PA_INVALID_INDEX;
601 }
602
603 #define EXT_VERSION 1
604
605 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
606 struct userdata *u;
607 uint32_t command;
608 pa_tagstruct *reply = NULL;
609
610 pa_assert(p);
611 pa_assert(m);
612 pa_assert(c);
613 pa_assert(t);
614
615 u = m->userdata;
616
617 if (pa_tagstruct_getu32(t, &command) < 0)
618 goto fail;
619
620 reply = pa_tagstruct_new(NULL, 0);
621 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
622 pa_tagstruct_putu32(reply, tag);
623
624 switch (command) {
625 case SUBCOMMAND_TEST: {
626 if (!pa_tagstruct_eof(t))
627 goto fail;
628
629 pa_tagstruct_putu32(reply, EXT_VERSION);
630 break;
631 }
632
633 case SUBCOMMAND_READ: {
634 pa_datum key;
635 pa_bool_t done;
636
637 if (!pa_tagstruct_eof(t))
638 goto fail;
639
640 done = !pa_database_first(u->database, &key, NULL);
641
642 while (!done) {
643 pa_datum next_key;
644 struct entry *e;
645 char *name;
646
647 done = !pa_database_next(u->database, &key, &next_key, NULL);
648
649 name = pa_xstrndup(key.data, key.size);
650 pa_datum_free(&key);
651
652 if ((e = read_entry(u, name))) {
653 pa_tagstruct_puts(reply, name);
654 pa_tagstruct_puts(reply, e->description);
655
656 pa_xfree(e);
657 }
658
659 pa_xfree(name);
660
661 key = next_key;
662 }
663
664 break;
665 }
666
667 case SUBCOMMAND_RENAME: {
668
669 struct entry *e;
670 const char *device, *description;
671
672 if (pa_tagstruct_gets(t, &device) < 0 ||
673 pa_tagstruct_gets(t, &description) < 0)
674 goto fail;
675
676 if (!device || !*device || !description || !*description)
677 goto fail;
678
679 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
680 pa_datum key, data;
681
682 pa_strlcpy(e->description, description, sizeof(e->description));
683
684 key.data = (char *) device;
685 key.size = strlen(device);
686
687 data.data = e;
688 data.size = sizeof(*e);
689
690 if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
691 apply_entry(u, device, e);
692
693 trigger_save(u);
694 }
695 else
696 pa_log_warn("Could not save device");
697
698 pa_xfree(e);
699 }
700 else
701 pa_log_warn("Could not rename device %s, no entry in database", device);
702
703 break;
704 }
705
706 case SUBCOMMAND_DELETE:
707
708 while (!pa_tagstruct_eof(t)) {
709 const char *name;
710 pa_datum key;
711
712 if (pa_tagstruct_gets(t, &name) < 0)
713 goto fail;
714
715 key.data = (char*) name;
716 key.size = strlen(name);
717
718 /** @todo: Reindex the priorities */
719 pa_database_unset(u->database, &key);
720 }
721
722 trigger_save(u);
723
724 break;
725
726 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
727
728 pa_bool_t enable;
729 uint32_t sridx = PA_INVALID_INDEX;
730 uint32_t idx;
731 pa_module *module;
732
733 if (pa_tagstruct_get_boolean(t, &enable) < 0)
734 goto fail;
735
736 /* If this is the first run, check for stream restore module */
737 if (!u->checked_stream_restore) {
738 u->checked_stream_restore = TRUE;
739
740 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
741 if (strcmp(module->name, "module-stream-restore") == 0) {
742 pa_log_debug("Detected module-stream-restore is currently in use");
743 u->stream_restore_used = TRUE;
744 sridx = module->index;
745 }
746 }
747 }
748
749 u->role_device_priority_routing = enable;
750 if (enable) {
751 if (u->stream_restore_used) {
752 if (PA_INVALID_INDEX == sridx) {
753 /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
754 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
755 if (strcmp(module->name, "module-stream-restore") == 0) {
756 sridx = module->index;
757 }
758 }
759 }
760 if (PA_INVALID_INDEX != sridx) {
761 pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
762 pa_module_unload_request_by_index(u->core, sridx, TRUE);
763 }
764 }
765 } else if (u->stream_restore_used) {
766 /* We want to reload module-stream-restore */
767 if (!pa_module_load(u->core, "module-stream-restore", ""))
768 pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
769 }
770
771 break;
772 }
773
774 case SUBCOMMAND_PREFER_DEVICE:
775 case SUBCOMMAND_DEFER_DEVICE: {
776
777 const char *role, *device;
778 struct entry *e;
779 uint32_t role_index;
780
781 if (pa_tagstruct_gets(t, &role) < 0 ||
782 pa_tagstruct_gets(t, &device) < 0)
783 goto fail;
784
785 if (!role || !device || !*device)
786 goto fail;
787
788 role_index = get_role_index(role);
789 if (PA_INVALID_INDEX == role_index)
790 goto fail;
791
792 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
793 pa_datum key, data;
794 pa_bool_t done;
795 char* prefix;
796 uint32_t priority;
797 pa_bool_t haschanged = FALSE;
798
799 if (strncmp(device, "sink:", 5) == 0)
800 prefix = pa_xstrdup("sink:");
801 else
802 prefix = pa_xstrdup("source:");
803
804 priority = e->priority[role_index];
805
806 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
807
808 done = !pa_database_first(u->database, &key, NULL);
809
810 while (!done && !haschanged) {
811 pa_datum next_key;
812
813 done = !pa_database_next(u->database, &key, &next_key, NULL);
814
815 /* Only read devices with the right prefix */
816 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
817 char *name;
818 struct entry *e2;
819
820 name = pa_xstrndup(key.data, key.size);
821
822 if ((e2 = read_entry(u, name))) {
823 if (SUBCOMMAND_PREFER_DEVICE == command) {
824 /* PREFER */
825 if (e2->priority[role_index] == (priority - 1)) {
826 e2->priority[role_index]++;
827 haschanged = TRUE;
828 }
829 } else {
830 /* DEFER */
831 if (e2->priority[role_index] == (priority + 1)) {
832 e2->priority[role_index]--;
833 haschanged = TRUE;
834 }
835 }
836
837 if (haschanged) {
838 data.data = e2;
839 data.size = sizeof(*e2);
840
841 if (pa_database_set(u->database, &key, &data, FALSE))
842 pa_log_warn("Could not save device");
843 }
844
845 pa_xfree(e2);
846 }
847
848 pa_xfree(name);
849 }
850
851 pa_datum_free(&key);
852 key = next_key;
853 }
854
855 /* Now write out our actual entry */
856 if (haschanged) {
857 if (SUBCOMMAND_PREFER_DEVICE == command)
858 e->priority[role_index]--;
859 else
860 e->priority[role_index]++;
861
862 key.data = (char *) device;
863 key.size = strlen(device);
864
865 data.data = e;
866 data.size = sizeof(*e);
867
868 if (pa_database_set(u->database, &key, &data, FALSE))
869 pa_log_warn("Could not save device");
870
871 trigger_save(u);
872 }
873
874 pa_xfree(e);
875
876 pa_xfree(prefix);
877 }
878 else
879 pa_log_warn("Could not reorder device %s, no entry in database", device);
880
881 break;
882 }
883
884 case SUBCOMMAND_SUBSCRIBE: {
885
886 pa_bool_t enabled;
887
888 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
889 !pa_tagstruct_eof(t))
890 goto fail;
891
892 if (enabled)
893 pa_idxset_put(u->subscribed, c, NULL);
894 else
895 pa_idxset_remove_by_data(u->subscribed, c, NULL);
896
897 break;
898 }
899
900 default:
901 goto fail;
902 }
903
904 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
905 return 0;
906
907 fail:
908
909 if (reply)
910 pa_tagstruct_free(reply);
911
912 return -1;
913 }
914
915 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
916 pa_assert(p);
917 pa_assert(c);
918 pa_assert(u);
919
920 pa_idxset_remove_by_data(u->subscribed, c, NULL);
921 return PA_HOOK_OK;
922 }
923
924 int pa__init(pa_module*m) {
925 pa_modargs *ma = NULL;
926 struct userdata *u;
927 char *fname;
928 pa_sink *sink;
929 pa_source *source;
930 pa_sink_input *si;
931 pa_source_output *so;
932 uint32_t idx;
933 pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
934
935 pa_assert(m);
936
937 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
938 pa_log("Failed to parse module arguments");
939 goto fail;
940 }
941
942 if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
943 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
944 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
945 goto fail;
946 }
947
948 m->userdata = u = pa_xnew0(struct userdata, 1);
949 u->core = m->core;
950 u->module = m;
951 u->on_hotplug = on_hotplug;
952 u->on_rescue = on_rescue;
953 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
954
955 u->protocol = pa_native_protocol_get(m->core);
956 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
957
958 u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
959
960 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
961
962 /* Used to handle device description management */
963 u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
964 u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
965
966 /* The following slots are used to deal with routing */
967 /* A little bit later than module-stream-restore, module-intended-roles */
968 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
969 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
970
971 if (on_hotplug) {
972 /* A little bit later than module-stream-restore, module-intended-roles */
973 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
974 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
975 }
976
977 if (on_rescue) {
978 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
979 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
980 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
981 }
982
983 if (!(fname = pa_state_path("device-manager", TRUE)))
984 goto fail;
985
986 if (!(u->database = pa_database_open(fname, TRUE))) {
987 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
988 pa_xfree(fname);
989 goto fail;
990 }
991
992 pa_log_info("Sucessfully opened database file '%s'.", fname);
993 pa_xfree(fname);
994
995 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
996 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
997
998 PA_IDXSET_FOREACH(source, m->core->sources, idx)
999 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1000
1001 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
1002 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
1003
1004 PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
1005 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
1006
1007 pa_modargs_free(ma);
1008 return 0;
1009
1010 fail:
1011 pa__done(m);
1012
1013 if (ma)
1014 pa_modargs_free(ma);
1015
1016 return -1;
1017 }
1018
1019 void pa__done(pa_module*m) {
1020 struct userdata* u;
1021
1022 pa_assert(m);
1023
1024 if (!(u = m->userdata))
1025 return;
1026
1027 if (u->subscription)
1028 pa_subscription_free(u->subscription);
1029
1030 if (u->sink_new_hook_slot)
1031 pa_hook_slot_free(u->sink_new_hook_slot);
1032 if (u->source_new_hook_slot)
1033 pa_hook_slot_free(u->source_new_hook_slot);
1034
1035 if (u->sink_input_new_hook_slot)
1036 pa_hook_slot_free(u->sink_input_new_hook_slot);
1037 if (u->source_output_new_hook_slot)
1038 pa_hook_slot_free(u->source_output_new_hook_slot);
1039
1040 if (u->sink_put_hook_slot)
1041 pa_hook_slot_free(u->sink_put_hook_slot);
1042 if (u->source_put_hook_slot)
1043 pa_hook_slot_free(u->source_put_hook_slot);
1044
1045 if (u->sink_unlink_hook_slot)
1046 pa_hook_slot_free(u->sink_unlink_hook_slot);
1047 if (u->source_unlink_hook_slot)
1048 pa_hook_slot_free(u->source_unlink_hook_slot);
1049
1050 if (u->save_time_event)
1051 u->core->mainloop->time_free(u->save_time_event);
1052
1053 if (u->database)
1054 pa_database_close(u->database);
1055
1056 if (u->protocol) {
1057 pa_native_protocol_remove_ext(u->protocol, m);
1058 pa_native_protocol_unref(u->protocol);
1059 }
1060
1061 if (u->subscribed)
1062 pa_idxset_free(u->subscribed, NULL, NULL);
1063
1064 pa_xfree(u);
1065 }