]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Expose the priority lists in the protocol extension.
[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 and prioritise by role");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
62 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
63 "on_hotplug=<When new device becomes available, recheck streams?> "
64 "on_rescue=<When device becomes unavailable, recheck streams?>");
65
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 #define DUMP_DATABASE
68
69 static const char* const valid_modargs[] = {
70 "do_routing",
71 "on_hotplug",
72 "on_rescue",
73 NULL
74 };
75
76 #define NUM_ROLES 9
77 enum {
78 ROLE_NONE,
79 ROLE_VIDEO,
80 ROLE_MUSIC,
81 ROLE_GAME,
82 ROLE_EVENT,
83 ROLE_PHONE,
84 ROLE_ANIMATION,
85 ROLE_PRODUCTION,
86 ROLE_A11Y,
87 };
88
89 typedef uint32_t role_indexes_t[NUM_ROLES];
90
91 static const char* role_names[NUM_ROLES] = {
92 "none",
93 "video",
94 "music",
95 "game",
96 "event",
97 "phone",
98 "animation",
99 "production",
100 "a11y",
101 };
102
103 struct userdata {
104 pa_core *core;
105 pa_module *module;
106 pa_subscription *subscription;
107 pa_hook_slot
108 *sink_new_hook_slot,
109 *source_new_hook_slot,
110 *sink_input_new_hook_slot,
111 *source_output_new_hook_slot,
112 *sink_put_hook_slot,
113 *source_put_hook_slot,
114 *sink_unlink_hook_slot,
115 *source_unlink_hook_slot,
116 *connection_unlink_hook_slot;
117 pa_time_event *save_time_event;
118 pa_database *database;
119
120 pa_native_protocol *protocol;
121 pa_idxset *subscribed;
122
123 pa_bool_t on_hotplug;
124 pa_bool_t on_rescue;
125 pa_bool_t do_routing;
126
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
129 };
130
131 #define ENTRY_VERSION 1
132
133 struct entry {
134 uint8_t version;
135 char description[PA_NAME_MAX];
136 role_indexes_t priority;
137 } PA_GCC_PACKED;
138
139 enum {
140 SUBCOMMAND_TEST,
141 SUBCOMMAND_READ,
142 SUBCOMMAND_RENAME,
143 SUBCOMMAND_DELETE,
144 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
145 SUBCOMMAND_PREFER_DEVICE,
146 SUBCOMMAND_DEFER_DEVICE,
147 SUBCOMMAND_SUBSCRIBE,
148 SUBCOMMAND_EVENT
149 };
150
151
152 static struct entry* read_entry(struct userdata *u, const char *name) {
153 pa_datum key, data;
154 struct entry *e;
155
156 pa_assert(u);
157 pa_assert(name);
158
159 key.data = (char*) name;
160 key.size = strlen(name);
161
162 pa_zero(data);
163
164 if (!pa_database_get(u->database, &key, &data))
165 goto fail;
166
167 if (data.size != sizeof(struct entry)) {
168 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));
169 goto fail;
170 }
171
172 e = (struct entry*) data.data;
173
174 if (e->version != ENTRY_VERSION) {
175 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
176 goto fail;
177 }
178
179 if (!memchr(e->description, 0, sizeof(e->description))) {
180 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
181 goto fail;
182 }
183
184 return e;
185
186 fail:
187
188 pa_datum_free(&data);
189 return NULL;
190 }
191
192 #ifdef DUMP_DATABASE
193 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
194 pa_assert(u);
195 pa_assert(human);
196
197 if (sink_mode) {
198 pa_sink *s;
199 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
200 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
201 else
202 pa_log_debug(" %s No sink specified", human);
203 } else {
204 pa_source *s;
205 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
206 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
207 else
208 pa_log_debug(" %s No source specified", human);
209 }
210 }
211
212 static void dump_database(struct userdata *u) {
213 pa_datum key;
214 pa_bool_t done;
215
216 pa_assert(u);
217
218 done = !pa_database_first(u->database, &key, NULL);
219
220 pa_log_debug("Dumping database");
221 while (!done) {
222 char *name;
223 struct entry *e;
224 pa_datum next_key;
225
226 done = !pa_database_next(u->database, &key, &next_key, NULL);
227
228 name = pa_xstrndup(key.data, key.size);
229
230 if ((e = read_entry(u, name))) {
231 pa_log_debug(" Got entry: %s", name);
232 pa_log_debug(" Description: %s", e->description);
233 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
234 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
235 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
236 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
237 pa_xfree(e);
238 }
239
240 pa_xfree(name);
241
242 pa_datum_free(&key);
243 key = next_key;
244 }
245
246 pa_log_debug(" Highest priority devices per-role:");
247
248 pa_log_debug(" Sinks:");
249 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
250 char name[13];
251 uint32_t len = PA_MAX(12u, strlen(role_names[role]));
252 for (int i = 0; i < 12; ++i) name[i] = ' ';
253 strncpy(name, role_names[role], len);
254 name[len] = ':';
255 name[0] -= 32;
256 name[12] = '\0';
257 dump_database_helper(u, role, name, TRUE);
258 }
259
260 pa_log_debug(" Sources:");
261 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
262 char name[13];
263 uint32_t len = PA_MAX(12u, strlen(role_names[role]));
264 for (int i = 0; i < 12; ++i) name[i] = ' ';
265 strncpy(name, role_names[role], len);
266 name[len] = ':';
267 name[0] -= 32;
268 name[12] = '\0';
269 dump_database_helper(u, role, name, FALSE);
270 }
271
272 pa_log_debug("Completed database dump");
273 }
274 #endif
275
276 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
277 struct userdata *u = userdata;
278
279 pa_assert(a);
280 pa_assert(e);
281 pa_assert(u);
282
283 pa_assert(e == u->save_time_event);
284 u->core->mainloop->time_free(u->save_time_event);
285 u->save_time_event = NULL;
286
287 pa_database_sync(u->database);
288 pa_log_info("Synced.");
289
290 #ifdef DUMP_DATABASE
291 dump_database(u);
292 #endif
293 }
294
295 static void trigger_save(struct userdata *u) {
296 pa_native_connection *c;
297 uint32_t idx;
298
299 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
300 pa_tagstruct *t;
301
302 t = pa_tagstruct_new(NULL, 0);
303 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
304 pa_tagstruct_putu32(t, 0);
305 pa_tagstruct_putu32(t, u->module->index);
306 pa_tagstruct_puts(t, u->module->name);
307 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
308
309 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
310 }
311
312 if (u->save_time_event)
313 return;
314
315 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
316 }
317
318 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
319 /** @todo: Compare the priority lists too */
320 if (strncmp(a->description, b->description, sizeof(a->description)))
321 return FALSE;
322
323 return TRUE;
324 }
325
326 static char *get_name(const char *key, const char *prefix) {
327 char *t;
328
329 if (strncmp(key, prefix, strlen(prefix)))
330 return NULL;
331
332 t = pa_xstrdup(key + strlen(prefix));
333 return t;
334 }
335
336 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
337 struct entry *old;
338
339 pa_assert(u);
340 pa_assert(entry);
341 pa_assert(name);
342 pa_assert(prefix);
343
344 if ((old = read_entry(u, name)))
345 *entry = *old;
346 else {
347 /* This is a new device, so make sure we write it's priority list correctly */
348 role_indexes_t max_priority;
349 pa_datum key;
350 pa_bool_t done;
351
352 pa_zero(max_priority);
353 done = !pa_database_first(u->database, &key, NULL);
354
355 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
356 while (!done) {
357 pa_datum next_key;
358
359 done = !pa_database_next(u->database, &key, &next_key, NULL);
360
361 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
362 char *name2;
363 struct entry *e;
364
365 name2 = pa_xstrndup(key.data, key.size);
366
367 if ((e = read_entry(u, name2))) {
368 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
369 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
370 }
371
372 pa_xfree(e);
373 }
374
375 pa_xfree(name2);
376 }
377 pa_datum_free(&key);
378 key = next_key;
379 }
380
381 /* Actually initialise our entry now we've calculated it */
382 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
383 entry->priority[i] = max_priority[i] + 1;
384 }
385 }
386
387 return old;
388 }
389
390 static uint32_t get_role_index(const char* role) {
391 pa_assert(role);
392
393 if (strcmp(role, "") == 0)
394 return ROLE_NONE;
395 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
396 if (strcmp(role, role_names[i]) == 0)
397 return i;
398
399 return PA_INVALID_INDEX;
400 }
401
402 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
403 role_indexes_t *indexes, highest_priority_available;
404 pa_datum key;
405 pa_bool_t done, sink_mode;
406
407 pa_assert(u);
408 pa_assert(prefix);
409
410 sink_mode = (strcmp(prefix, "sink:") == 0);
411
412 if (sink_mode)
413 indexes = &u->preferred_sinks;
414 else
415 indexes = &u->preferred_sources;
416
417 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
418 *indexes[i] = PA_INVALID_INDEX;
419 }
420 pa_zero(highest_priority_available);
421
422 done = !pa_database_first(u->database, &key, NULL);
423
424 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
425 while (!done) {
426 pa_datum next_key;
427
428 done = !pa_database_next(u->database, &key, &next_key, NULL);
429
430 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
431 char *name;
432 struct entry *e;
433
434 name = pa_xstrndup(key.data, key.size);
435
436 if ((e = read_entry(u, name))) {
437 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
438 if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
439 /* We've found a device with a higher priority than that we've currently got,
440 so see if it is currently available or not and update our list */
441 uint32_t idx;
442 pa_bool_t found = FALSE;
443 char *device_name = get_name(name, prefix);
444
445 if (sink_mode) {
446 pa_sink *sink;
447
448 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
449 if ((pa_sink*) ignore_device == sink)
450 continue;
451 if (strcmp(sink->name, device_name) == 0) {
452 found = TRUE;
453 idx = sink->index; /* Is this needed? */
454 break;
455 }
456 }
457 } else {
458 pa_source *source;
459
460 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
461 if ((pa_source*) ignore_device == source)
462 continue;
463 if (strcmp(source->name, device_name) == 0) {
464 found = TRUE;
465 idx = source->index; /* Is this needed? */
466 break;
467 }
468 }
469 }
470 if (found) {
471 highest_priority_available[i] = e->priority[i];
472 *indexes[i] = idx;
473 }
474
475 pa_xfree(device_name);
476 }
477 }
478
479 pa_xfree(e);
480 }
481
482 pa_xfree(name);
483 }
484
485 pa_datum_free(&key);
486 key = next_key;
487 }
488 }
489
490
491 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
492 const char *role;
493 uint32_t role_index, device_index;
494 pa_sink *sink;
495
496 pa_assert(u);
497 pa_assert(u->do_routing);
498
499 if (si->save_sink)
500 return;
501
502 /* Skip this if it is already in the process of being moved anyway */
503 if (!si->sink)
504 return;
505
506 /* It might happen that a stream and a sink are set up at the
507 same time, in which case we want to make sure we don't
508 interfere with that */
509 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
510 return;
511
512 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
513 role_index = get_role_index("");
514 else
515 role_index = get_role_index(role);
516
517 if (PA_INVALID_INDEX == role_index)
518 return;
519
520 device_index = u->preferred_sinks[role_index];
521 if (PA_INVALID_INDEX == device_index)
522 return;
523
524 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
525 return;
526
527 if (si->sink != sink)
528 pa_sink_input_move_to(si, sink, TRUE);
529 }
530
531 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
532 pa_sink_input *si;
533 uint32_t idx;
534
535 pa_assert(u);
536
537 if (!u->do_routing)
538 return PA_HOOK_OK;
539
540 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
541
542 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
543 route_sink_input(u, si);
544 }
545
546 return PA_HOOK_OK;
547 }
548
549 static void route_source_output(struct userdata *u, pa_source_output *so) {
550 const char *role;
551 uint32_t role_index, device_index;
552 pa_source *source;
553
554 pa_assert(u);
555 pa_assert(u->do_routing);
556
557 if (so->save_source)
558 return;
559
560 if (so->direct_on_input)
561 return;
562
563 /* Skip this if it is already in the process of being moved anyway */
564 if (!so->source)
565 return;
566
567 /* It might happen that a stream and a source are set up at the
568 same time, in which case we want to make sure we don't
569 interfere with that */
570 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
571 return;
572
573 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
574 role_index = get_role_index("");
575 else
576 role_index = get_role_index(role);
577
578 if (PA_INVALID_INDEX == role_index)
579 return;
580
581 device_index = u->preferred_sources[role_index];
582 if (PA_INVALID_INDEX == device_index)
583 return;
584
585 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
586 return;
587
588 if (so->source != source)
589 pa_source_output_move_to(so, source, TRUE);
590 }
591
592 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
593 pa_source_output *so;
594 uint32_t idx;
595
596 pa_assert(u);
597
598 if (!u->do_routing)
599 return PA_HOOK_OK;
600
601 update_highest_priority_device_indexes(u, "source:", ignore_source);
602
603 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
604 route_source_output(u, so);
605 }
606
607 return PA_HOOK_OK;
608 }
609
610 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
611 struct userdata *u = userdata;
612 struct entry entry, *old = NULL;
613 char *name = NULL;
614 pa_datum key, data;
615
616 pa_assert(c);
617 pa_assert(u);
618
619 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
620 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
621 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
622 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
623
624 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
625 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
626 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
627 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
628 return;
629
630 pa_zero(entry);
631 entry.version = ENTRY_VERSION;
632
633 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
634 pa_sink_input *si;
635
636 if (!u->do_routing)
637 return;
638 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
639 return;
640
641 /* The role may change mid-stream, so we reroute */
642 route_sink_input(u, si);
643
644 return;
645 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
646 pa_source_output *so;
647
648 if (!u->do_routing)
649 return;
650 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
651 return;
652
653 /* The role may change mid-stream, so we reroute */
654 route_source_output(u, so);
655
656 return;
657 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
658 pa_sink *sink;
659
660 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
661 return;
662
663 name = pa_sprintf_malloc("sink:%s", sink->name);
664
665 old = load_or_initialize_entry(u, &entry, name, "sink:");
666
667 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
668
669 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
670 pa_source *source;
671
672 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
673
674 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
675 return;
676
677 if (source->monitor_of)
678 return;
679
680 name = pa_sprintf_malloc("source:%s", source->name);
681
682 old = load_or_initialize_entry(u, &entry, name, "source:");
683
684 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
685 }
686
687 pa_assert(name);
688
689 if (old) {
690
691 if (entries_equal(old, &entry)) {
692 pa_xfree(old);
693 pa_xfree(name);
694 return;
695 }
696
697 pa_xfree(old);
698 }
699
700 key.data = name;
701 key.size = strlen(name);
702
703 data.data = &entry;
704 data.size = sizeof(entry);
705
706 pa_log_info("Storing device %s.", name);
707
708 pa_database_set(u->database, &key, &data, TRUE);
709
710 pa_xfree(name);
711
712 trigger_save(u);
713 }
714
715 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
716 char *name;
717 struct entry *e;
718
719 pa_assert(c);
720 pa_assert(new_data);
721 pa_assert(u);
722
723 name = pa_sprintf_malloc("sink:%s", new_data->name);
724
725 if ((e = read_entry(u, name))) {
726 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
727 pa_log_info("Restoring description for sink %s.", new_data->name);
728 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
729 }
730
731 pa_xfree(e);
732 }
733
734 pa_xfree(name);
735
736 return PA_HOOK_OK;
737 }
738
739 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
740 char *name;
741 struct entry *e;
742
743 pa_assert(c);
744 pa_assert(new_data);
745 pa_assert(u);
746
747 name = pa_sprintf_malloc("source:%s", new_data->name);
748
749 if ((e = read_entry(u, name))) {
750 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
751 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
752 pa_log_info("Restoring description for source %s.", new_data->name);
753 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
754 }
755
756 pa_xfree(e);
757 }
758
759 pa_xfree(name);
760
761 return PA_HOOK_OK;
762 }
763
764 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
765 pa_assert(c);
766 pa_assert(new_data);
767 pa_assert(u);
768
769 if (!u->do_routing)
770 return PA_HOOK_OK;
771
772 if (new_data->sink)
773 pa_log_debug("Not restoring device for stream, because already set.");
774 else {
775 const char *role;
776 uint32_t role_index;
777
778 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
779 role_index = get_role_index("");
780 else
781 role_index = get_role_index(role);
782
783 if (PA_INVALID_INDEX != role_index) {
784 uint32_t device_index;
785
786 device_index = u->preferred_sinks[role_index];
787 if (PA_INVALID_INDEX != device_index) {
788 pa_sink *sink;
789
790 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
791 new_data->sink = sink;
792 new_data->save_sink = TRUE;
793 }
794 }
795 }
796 }
797
798 return PA_HOOK_OK;
799 }
800
801 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
802 pa_assert(c);
803 pa_assert(new_data);
804 pa_assert(u);
805
806 if (!u->do_routing)
807 return PA_HOOK_OK;
808
809 if (new_data->direct_on_input)
810 return PA_HOOK_OK;
811
812 if (new_data->source)
813 pa_log_debug("Not restoring device for stream, because already set");
814 else {
815 const char *role;
816 uint32_t role_index;
817
818 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
819 role_index = get_role_index("");
820 else
821 role_index = get_role_index(role);
822
823 if (PA_INVALID_INDEX != role_index) {
824 uint32_t device_index;
825
826 device_index = u->preferred_sources[role_index];
827 if (PA_INVALID_INDEX != device_index) {
828 pa_source *source;
829
830 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
831 new_data->source = source;
832 new_data->save_source = TRUE;
833 }
834 }
835 }
836 }
837
838 return PA_HOOK_OK;
839 }
840
841
842 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
843 pa_assert(c);
844 pa_assert(u);
845 pa_assert(u->core == c);
846 pa_assert(u->on_hotplug);
847
848 return route_sink_inputs(u, NULL);
849 }
850
851 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
852 pa_assert(c);
853 pa_assert(u);
854 pa_assert(u->core == c);
855 pa_assert(u->on_hotplug);
856
857 return route_source_outputs(u, NULL);
858 }
859
860 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
861 pa_assert(c);
862 pa_assert(sink);
863 pa_assert(u);
864 pa_assert(u->core == c);
865 pa_assert(u->on_rescue);
866
867 /* There's no point in doing anything if the core is shut down anyway */
868 if (c->state == PA_CORE_SHUTDOWN)
869 return PA_HOOK_OK;
870
871 return route_sink_inputs(u, sink);
872 }
873
874 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
875 pa_assert(c);
876 pa_assert(source);
877 pa_assert(u);
878 pa_assert(u->core == c);
879 pa_assert(u->on_rescue);
880
881 /* There's no point in doing anything if the core is shut down anyway */
882 if (c->state == PA_CORE_SHUTDOWN)
883 return PA_HOOK_OK;
884
885 return route_source_outputs(u, source);
886 }
887
888
889 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
890 pa_sink *sink;
891 pa_source *source;
892 uint32_t idx;
893 char *n;
894
895 pa_assert(u);
896 pa_assert(name);
897 pa_assert(e);
898
899 if ((n = get_name(name, "sink:"))) {
900 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
901 if (!pa_streq(sink->name, n)) {
902 continue;
903 }
904
905 pa_log_info("Setting description for sink %s.", sink->name);
906 pa_sink_set_description(sink, e->description);
907 }
908 pa_xfree(n);
909 }
910 else if ((n = get_name(name, "source:"))) {
911 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
912 if (!pa_streq(source->name, n)) {
913 continue;
914 }
915
916 if (source->monitor_of) {
917 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
918 continue;
919 }
920
921 pa_log_info("Setting description for source %s.", source->name);
922 pa_source_set_description(source, e->description);
923 }
924 pa_xfree(n);
925 }
926 }
927
928
929 #define EXT_VERSION 1
930
931 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
932 struct userdata *u;
933 uint32_t command;
934 pa_tagstruct *reply = NULL;
935
936 pa_assert(p);
937 pa_assert(m);
938 pa_assert(c);
939 pa_assert(t);
940
941 u = m->userdata;
942
943 if (pa_tagstruct_getu32(t, &command) < 0)
944 goto fail;
945
946 reply = pa_tagstruct_new(NULL, 0);
947 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
948 pa_tagstruct_putu32(reply, tag);
949
950 switch (command) {
951 case SUBCOMMAND_TEST: {
952 if (!pa_tagstruct_eof(t))
953 goto fail;
954
955 pa_tagstruct_putu32(reply, EXT_VERSION);
956 break;
957 }
958
959 case SUBCOMMAND_READ: {
960 pa_datum key;
961 pa_bool_t done;
962
963 if (!pa_tagstruct_eof(t))
964 goto fail;
965
966 done = !pa_database_first(u->database, &key, NULL);
967
968 while (!done) {
969 pa_datum next_key;
970 struct entry *e;
971 char *name;
972
973 done = !pa_database_next(u->database, &key, &next_key, NULL);
974
975 name = pa_xstrndup(key.data, key.size);
976 pa_datum_free(&key);
977
978 if ((e = read_entry(u, name))) {
979 pa_tagstruct_puts(reply, name);
980 pa_tagstruct_puts(reply, e->description);
981 pa_tagstruct_puts(reply, "audio-card"); /** @todo store the icon */
982 pa_tagstruct_put_boolean(reply, TRUE); /** @todo show current available */
983 pa_tagstruct_putu32(reply, NUM_ROLES);
984
985 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
986 pa_tagstruct_puts(reply, role_names[i]);
987 pa_tagstruct_putu32(reply, e->priority[i]);
988 }
989
990 pa_xfree(e);
991 }
992
993 pa_xfree(name);
994
995 key = next_key;
996 }
997
998 break;
999 }
1000
1001 case SUBCOMMAND_RENAME: {
1002
1003 struct entry *e;
1004 const char *device, *description;
1005
1006 if (pa_tagstruct_gets(t, &device) < 0 ||
1007 pa_tagstruct_gets(t, &description) < 0)
1008 goto fail;
1009
1010 if (!device || !*device || !description || !*description)
1011 goto fail;
1012
1013 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1014 pa_datum key, data;
1015
1016 pa_strlcpy(e->description, description, sizeof(e->description));
1017
1018 key.data = (char *) device;
1019 key.size = strlen(device);
1020
1021 data.data = e;
1022 data.size = sizeof(*e);
1023
1024 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1025 apply_entry(u, device, e);
1026
1027 trigger_save(u);
1028 }
1029 else
1030 pa_log_warn("Could not save device");
1031
1032 pa_xfree(e);
1033 }
1034 else
1035 pa_log_warn("Could not rename device %s, no entry in database", device);
1036
1037 break;
1038 }
1039
1040 case SUBCOMMAND_DELETE:
1041
1042 while (!pa_tagstruct_eof(t)) {
1043 const char *name;
1044 pa_datum key;
1045
1046 if (pa_tagstruct_gets(t, &name) < 0)
1047 goto fail;
1048
1049 key.data = (char*) name;
1050 key.size = strlen(name);
1051
1052 /** @todo: Reindex the priorities */
1053 pa_database_unset(u->database, &key);
1054 }
1055
1056 trigger_save(u);
1057
1058 break;
1059
1060 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1061
1062 pa_bool_t enable;
1063
1064 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1065 goto fail;
1066
1067 if ((u->do_routing = enable)) {
1068 /* Update our caches */
1069 update_highest_priority_device_indexes(u, "sink:", NULL);
1070 update_highest_priority_device_indexes(u, "source:", NULL);
1071 }
1072
1073 break;
1074 }
1075
1076 case SUBCOMMAND_PREFER_DEVICE:
1077 case SUBCOMMAND_DEFER_DEVICE: {
1078
1079 const char *role, *device;
1080 struct entry *e;
1081 uint32_t role_index;
1082
1083 if (pa_tagstruct_gets(t, &role) < 0 ||
1084 pa_tagstruct_gets(t, &device) < 0)
1085 goto fail;
1086
1087 if (!role || !device || !*device)
1088 goto fail;
1089
1090 role_index = get_role_index(role);
1091 if (PA_INVALID_INDEX == role_index)
1092 goto fail;
1093
1094 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1095 pa_datum key, data;
1096 pa_bool_t done;
1097 char* prefix = NULL;
1098 uint32_t priority;
1099 pa_bool_t haschanged = FALSE;
1100
1101 if (strncmp(device, "sink:", 5) == 0)
1102 prefix = pa_xstrdup("sink:");
1103 else if (strncmp(device, "source:", 7) == 0)
1104 prefix = pa_xstrdup("source:");
1105
1106 if (!prefix)
1107 goto fail;
1108
1109 priority = e->priority[role_index];
1110
1111 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
1112
1113 done = !pa_database_first(u->database, &key, NULL);
1114
1115 while (!done && !haschanged) {
1116 pa_datum next_key;
1117
1118 done = !pa_database_next(u->database, &key, &next_key, NULL);
1119
1120 /* Only read devices with the right prefix */
1121 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
1122 char *name;
1123 struct entry *e2;
1124
1125 name = pa_xstrndup(key.data, key.size);
1126
1127 if ((e2 = read_entry(u, name))) {
1128 if (SUBCOMMAND_PREFER_DEVICE == command) {
1129 /* PREFER */
1130 if (e2->priority[role_index] == (priority - 1)) {
1131 e2->priority[role_index]++;
1132 haschanged = TRUE;
1133 }
1134 } else {
1135 /* DEFER */
1136 if (e2->priority[role_index] == (priority + 1)) {
1137 e2->priority[role_index]--;
1138 haschanged = TRUE;
1139 }
1140 }
1141
1142 if (haschanged) {
1143 data.data = e2;
1144 data.size = sizeof(*e2);
1145
1146 if (pa_database_set(u->database, &key, &data, TRUE))
1147 pa_log_warn("Could not save device");
1148 }
1149
1150 pa_xfree(e2);
1151 }
1152
1153 pa_xfree(name);
1154 }
1155
1156 pa_datum_free(&key);
1157 key = next_key;
1158 }
1159
1160 /* Now write out our actual entry */
1161 if (haschanged) {
1162 if (SUBCOMMAND_PREFER_DEVICE == command)
1163 e->priority[role_index]--;
1164 else
1165 e->priority[role_index]++;
1166
1167 key.data = (char *) device;
1168 key.size = strlen(device);
1169
1170 data.data = e;
1171 data.size = sizeof(*e);
1172
1173 if (pa_database_set(u->database, &key, &data, TRUE))
1174 pa_log_warn("Could not save device");
1175
1176 trigger_save(u);
1177 }
1178
1179 pa_xfree(e);
1180
1181 pa_xfree(prefix);
1182 }
1183 else
1184 pa_log_warn("Could not reorder device %s, no entry in database", device);
1185
1186 break;
1187 }
1188
1189 case SUBCOMMAND_SUBSCRIBE: {
1190
1191 pa_bool_t enabled;
1192
1193 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1194 !pa_tagstruct_eof(t))
1195 goto fail;
1196
1197 if (enabled)
1198 pa_idxset_put(u->subscribed, c, NULL);
1199 else
1200 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1201
1202 break;
1203 }
1204
1205 default:
1206 goto fail;
1207 }
1208
1209 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1210 return 0;
1211
1212 fail:
1213
1214 if (reply)
1215 pa_tagstruct_free(reply);
1216
1217 return -1;
1218 }
1219
1220 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1221 pa_assert(p);
1222 pa_assert(c);
1223 pa_assert(u);
1224
1225 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1226 return PA_HOOK_OK;
1227 }
1228
1229 int pa__init(pa_module*m) {
1230 pa_modargs *ma = NULL;
1231 struct userdata *u;
1232 char *fname;
1233 pa_sink *sink;
1234 pa_source *source;
1235 uint32_t idx;
1236 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1237
1238 pa_assert(m);
1239
1240 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1241 pa_log("Failed to parse module arguments");
1242 goto fail;
1243 }
1244
1245 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1246 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1247 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1248 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1249 goto fail;
1250 }
1251
1252 m->userdata = u = pa_xnew0(struct userdata, 1);
1253 u->core = m->core;
1254 u->module = m;
1255 u->do_routing = do_routing;
1256 u->on_hotplug = on_hotplug;
1257 u->on_rescue = on_rescue;
1258 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1259
1260 u->protocol = pa_native_protocol_get(m->core);
1261 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1262
1263 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);
1264
1265 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
1266
1267 /* Used to handle device description management */
1268 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);
1269 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);
1270
1271 /* The following slots are used to deal with routing */
1272 /* A little bit later than module-stream-restore, module-intended-roles */
1273 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);
1274 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);
1275
1276 if (on_hotplug) {
1277 /* A little bit later than module-stream-restore, module-intended-roles */
1278 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);
1279 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);
1280 }
1281
1282 if (on_rescue) {
1283 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1284 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);
1285 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);
1286 }
1287
1288 if (!(fname = pa_state_path("device-manager", TRUE)))
1289 goto fail;
1290
1291 if (!(u->database = pa_database_open(fname, TRUE))) {
1292 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1293 pa_xfree(fname);
1294 goto fail;
1295 }
1296
1297 pa_log_info("Sucessfully opened database file '%s'.", fname);
1298 pa_xfree(fname);
1299
1300 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1301 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1302 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1303
1304 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1305 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1306
1307 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1308 route_sink_inputs(u, NULL);
1309 route_source_outputs(u, NULL);
1310
1311 #ifdef DUMP_DATABASE
1312 dump_database(u);
1313 #endif
1314
1315 pa_modargs_free(ma);
1316 return 0;
1317
1318 fail:
1319 pa__done(m);
1320
1321 if (ma)
1322 pa_modargs_free(ma);
1323
1324 return -1;
1325 }
1326
1327 void pa__done(pa_module*m) {
1328 struct userdata* u;
1329
1330 pa_assert(m);
1331
1332 if (!(u = m->userdata))
1333 return;
1334
1335 if (u->subscription)
1336 pa_subscription_free(u->subscription);
1337
1338 if (u->sink_new_hook_slot)
1339 pa_hook_slot_free(u->sink_new_hook_slot);
1340 if (u->source_new_hook_slot)
1341 pa_hook_slot_free(u->source_new_hook_slot);
1342
1343 if (u->sink_input_new_hook_slot)
1344 pa_hook_slot_free(u->sink_input_new_hook_slot);
1345 if (u->source_output_new_hook_slot)
1346 pa_hook_slot_free(u->source_output_new_hook_slot);
1347
1348 if (u->sink_put_hook_slot)
1349 pa_hook_slot_free(u->sink_put_hook_slot);
1350 if (u->source_put_hook_slot)
1351 pa_hook_slot_free(u->source_put_hook_slot);
1352
1353 if (u->sink_unlink_hook_slot)
1354 pa_hook_slot_free(u->sink_unlink_hook_slot);
1355 if (u->source_unlink_hook_slot)
1356 pa_hook_slot_free(u->source_unlink_hook_slot);
1357
1358 if (u->save_time_event)
1359 u->core->mainloop->time_free(u->save_time_event);
1360
1361 if (u->database)
1362 pa_database_close(u->database);
1363
1364 if (u->protocol) {
1365 pa_native_protocol_remove_ext(u->protocol, m);
1366 pa_native_protocol_unref(u->protocol);
1367 }
1368
1369 if (u->subscribed)
1370 pa_idxset_free(u->subscribed, NULL, NULL);
1371
1372 pa_xfree(u);
1373 }