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