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