2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
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.
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.
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
30 #include <sys/types.h>
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>
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>
55 #include "module-device-manager-symdef.h"
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);
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?>");
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
69 static const char* const valid_modargs[] = {
89 typedef uint32_t role_indexes_t[NUM_ROLES];
91 static const char* role_names[NUM_ROLES] = {
106 pa_subscription *subscription;
109 *source_new_hook_slot,
110 *sink_input_new_hook_slot,
111 *source_output_new_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;
120 pa_native_protocol *protocol;
121 pa_idxset *subscribed;
123 pa_bool_t on_hotplug;
125 pa_bool_t do_routing;
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
131 #define ENTRY_VERSION 1
135 char description[PA_NAME_MAX];
136 role_indexes_t priority;
144 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
145 SUBCOMMAND_PREFER_DEVICE,
146 SUBCOMMAND_DEFER_DEVICE,
147 SUBCOMMAND_SUBSCRIBE,
152 static struct entry* read_entry(struct userdata *u, const char *name) {
159 key.data = (char*) name;
160 key.size = strlen(name);
164 if (!pa_database_get(u->database, &key, &data))
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));
172 e = (struct entry*) data.data;
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);
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);
188 pa_datum_free(&data);
193 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
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);
202 pa_log_debug(" %s No sink specified", human);
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);
208 pa_log_debug(" %s No source specified", human);
212 static void dump_database(struct userdata *u) {
218 done = !pa_database_first(u->database, &key, NULL);
220 pa_log_debug("Dumping database");
226 done = !pa_database_next(u->database, &key, &next_key, NULL);
228 name = pa_xstrndup(key.data, key.size);
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]);
246 pa_log_debug(" Highest priority devices per-role:");
248 pa_log_debug(" Sinks:");
249 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
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);
257 dump_database_helper(u, role, name, TRUE);
260 pa_log_debug(" Sources:");
261 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
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);
269 dump_database_helper(u, role, name, FALSE);
272 pa_log_debug("Completed database dump");
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;
283 pa_assert(e == u->save_time_event);
284 u->core->mainloop->time_free(u->save_time_event);
285 u->save_time_event = NULL;
287 pa_database_sync(u->database);
288 pa_log_info("Synced.");
295 static void trigger_save(struct userdata *u) {
296 pa_native_connection *c;
299 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
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);
309 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
312 if (u->save_time_event)
315 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
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)))
326 static char *get_name(const char *key, const char *prefix) {
329 if (strncmp(key, prefix, strlen(prefix)))
332 t = pa_xstrdup(key + strlen(prefix));
336 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
344 if ((old = read_entry(u, name)))
347 /* This is a new device, so make sure we write it's priority list correctly */
348 role_indexes_t max_priority;
352 pa_zero(max_priority);
353 done = !pa_database_first(u->database, &key, NULL);
355 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
359 done = !pa_database_next(u->database, &key, &next_key, NULL);
361 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
365 name2 = pa_xstrndup(key.data, key.size);
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]);
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;
390 static uint32_t get_role_index(const char* role) {
393 if (strcmp(role, "") == 0)
395 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
396 if (strcmp(role, role_names[i]) == 0)
399 return PA_INVALID_INDEX;
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;
405 pa_bool_t done, sink_mode;
410 sink_mode = (strcmp(prefix, "sink:") == 0);
413 indexes = &u->preferred_sinks;
415 indexes = &u->preferred_sources;
417 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
418 *indexes[i] = PA_INVALID_INDEX;
420 pa_zero(highest_priority_available);
422 done = !pa_database_first(u->database, &key, NULL);
424 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
428 done = !pa_database_next(u->database, &key, &next_key, NULL);
430 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
434 name = pa_xstrndup(key.data, key.size);
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 */
442 pa_bool_t found = FALSE;
443 char *device_name = get_name(name, prefix);
448 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
449 if ((pa_sink*) ignore_device == sink)
451 if (strcmp(sink->name, device_name) == 0) {
453 idx = sink->index; /* Is this needed? */
460 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
461 if ((pa_source*) ignore_device == source)
463 if (strcmp(source->name, device_name) == 0) {
465 idx = source->index; /* Is this needed? */
471 highest_priority_available[i] = e->priority[i];
475 pa_xfree(device_name);
491 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
493 uint32_t role_index, device_index;
497 pa_assert(u->do_routing);
502 /* Skip this if it is already in the process of being moved anyway */
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)))
512 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
513 role_index = get_role_index("");
515 role_index = get_role_index(role);
517 if (PA_INVALID_INDEX == role_index)
520 device_index = u->preferred_sinks[role_index];
521 if (PA_INVALID_INDEX == device_index)
524 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
527 if (si->sink != sink)
528 pa_sink_input_move_to(si, sink, TRUE);
531 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
540 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
542 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
543 route_sink_input(u, si);
549 static void route_source_output(struct userdata *u, pa_source_output *so) {
551 uint32_t role_index, device_index;
555 pa_assert(u->do_routing);
560 if (so->direct_on_input)
563 /* Skip this if it is already in the process of being moved anyway */
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)))
573 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
574 role_index = get_role_index("");
576 role_index = get_role_index(role);
578 if (PA_INVALID_INDEX == role_index)
581 device_index = u->preferred_sources[role_index];
582 if (PA_INVALID_INDEX == device_index)
585 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
588 if (so->source != source)
589 pa_source_output_move_to(so, source, TRUE);
592 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
593 pa_source_output *so;
601 update_highest_priority_device_indexes(u, "source:", ignore_source);
603 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
604 route_source_output(u, so);
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;
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) &&
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))
631 entry.version = ENTRY_VERSION;
633 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
638 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
641 /* The role may change mid-stream, so we reroute */
642 route_sink_input(u, si);
645 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
646 pa_source_output *so;
650 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
653 /* The role may change mid-stream, so we reroute */
654 route_source_output(u, so);
657 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
660 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
663 name = pa_sprintf_malloc("sink:%s", sink->name);
665 old = load_or_initialize_entry(u, &entry, name, "sink:");
667 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
669 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
672 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
674 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
677 if (source->monitor_of)
680 name = pa_sprintf_malloc("source:%s", source->name);
682 old = load_or_initialize_entry(u, &entry, name, "source:");
684 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
691 if (entries_equal(old, &entry)) {
701 key.size = strlen(name);
704 data.size = sizeof(entry);
706 pa_log_info("Storing device %s.", name);
708 pa_database_set(u->database, &key, &data, TRUE);
715 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
723 name = pa_sprintf_malloc("sink:%s", new_data->name);
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);
739 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
747 name = pa_sprintf_malloc("source:%s", new_data->name);
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);
764 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
773 pa_log_debug("Not restoring device for stream, because already set.");
778 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
779 role_index = get_role_index("");
781 role_index = get_role_index(role);
783 if (PA_INVALID_INDEX != role_index) {
784 uint32_t device_index;
786 device_index = u->preferred_sinks[role_index];
787 if (PA_INVALID_INDEX != device_index) {
790 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
791 new_data->sink = sink;
792 new_data->save_sink = TRUE;
801 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
809 if (new_data->direct_on_input)
812 if (new_data->source)
813 pa_log_debug("Not restoring device for stream, because already set");
818 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
819 role_index = get_role_index("");
821 role_index = get_role_index(role);
823 if (PA_INVALID_INDEX != role_index) {
824 uint32_t device_index;
826 device_index = u->preferred_sources[role_index];
827 if (PA_INVALID_INDEX != device_index) {
830 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
831 new_data->source = source;
832 new_data->save_source = TRUE;
842 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
845 pa_assert(u->core == c);
846 pa_assert(u->on_hotplug);
848 return route_sink_inputs(u, NULL);
851 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
854 pa_assert(u->core == c);
855 pa_assert(u->on_hotplug);
857 return route_source_outputs(u, NULL);
860 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
864 pa_assert(u->core == c);
865 pa_assert(u->on_rescue);
867 /* There's no point in doing anything if the core is shut down anyway */
868 if (c->state == PA_CORE_SHUTDOWN)
871 return route_sink_inputs(u, sink);
874 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
878 pa_assert(u->core == c);
879 pa_assert(u->on_rescue);
881 /* There's no point in doing anything if the core is shut down anyway */
882 if (c->state == PA_CORE_SHUTDOWN)
885 return route_source_outputs(u, source);
889 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
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)) {
905 pa_log_info("Setting description for sink %s.", sink->name);
906 pa_sink_set_description(sink, e->description);
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)) {
916 if (source->monitor_of) {
917 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
921 pa_log_info("Setting description for source %s.", source->name);
922 pa_source_set_description(source, e->description);
929 #define EXT_VERSION 1
931 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
934 pa_tagstruct *reply = NULL;
943 if (pa_tagstruct_getu32(t, &command) < 0)
946 reply = pa_tagstruct_new(NULL, 0);
947 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
948 pa_tagstruct_putu32(reply, tag);
951 case SUBCOMMAND_TEST: {
952 if (!pa_tagstruct_eof(t))
955 pa_tagstruct_putu32(reply, EXT_VERSION);
959 case SUBCOMMAND_READ: {
963 if (!pa_tagstruct_eof(t))
966 done = !pa_database_first(u->database, &key, NULL);
973 done = !pa_database_next(u->database, &key, &next_key, NULL);
975 name = pa_xstrndup(key.data, key.size);
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);
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]);
1001 case SUBCOMMAND_RENAME: {
1004 const char *device, *description;
1006 if (pa_tagstruct_gets(t, &device) < 0 ||
1007 pa_tagstruct_gets(t, &description) < 0)
1010 if (!device || !*device || !description || !*description)
1013 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1016 pa_strlcpy(e->description, description, sizeof(e->description));
1018 key.data = (char *) device;
1019 key.size = strlen(device);
1022 data.size = sizeof(*e);
1024 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1025 apply_entry(u, device, e);
1030 pa_log_warn("Could not save device");
1035 pa_log_warn("Could not rename device %s, no entry in database", device);
1040 case SUBCOMMAND_DELETE:
1042 while (!pa_tagstruct_eof(t)) {
1046 if (pa_tagstruct_gets(t, &name) < 0)
1049 key.data = (char*) name;
1050 key.size = strlen(name);
1052 /** @todo: Reindex the priorities */
1053 pa_database_unset(u->database, &key);
1060 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1064 if (pa_tagstruct_get_boolean(t, &enable) < 0)
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);
1076 case SUBCOMMAND_PREFER_DEVICE:
1077 case SUBCOMMAND_DEFER_DEVICE: {
1079 const char *role, *device;
1081 uint32_t role_index;
1083 if (pa_tagstruct_gets(t, &role) < 0 ||
1084 pa_tagstruct_gets(t, &device) < 0)
1087 if (!role || !device || !*device)
1090 role_index = get_role_index(role);
1091 if (PA_INVALID_INDEX == role_index)
1094 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1097 char* prefix = NULL;
1099 pa_bool_t haschanged = FALSE;
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:");
1109 priority = e->priority[role_index];
1111 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
1113 done = !pa_database_first(u->database, &key, NULL);
1115 while (!done && !haschanged) {
1118 done = !pa_database_next(u->database, &key, &next_key, NULL);
1120 /* Only read devices with the right prefix */
1121 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
1125 name = pa_xstrndup(key.data, key.size);
1127 if ((e2 = read_entry(u, name))) {
1128 if (SUBCOMMAND_PREFER_DEVICE == command) {
1130 if (e2->priority[role_index] == (priority - 1)) {
1131 e2->priority[role_index]++;
1136 if (e2->priority[role_index] == (priority + 1)) {
1137 e2->priority[role_index]--;
1144 data.size = sizeof(*e2);
1146 if (pa_database_set(u->database, &key, &data, TRUE))
1147 pa_log_warn("Could not save device");
1156 pa_datum_free(&key);
1160 /* Now write out our actual entry */
1162 if (SUBCOMMAND_PREFER_DEVICE == command)
1163 e->priority[role_index]--;
1165 e->priority[role_index]++;
1167 key.data = (char *) device;
1168 key.size = strlen(device);
1171 data.size = sizeof(*e);
1173 if (pa_database_set(u->database, &key, &data, TRUE))
1174 pa_log_warn("Could not save device");
1184 pa_log_warn("Could not reorder device %s, no entry in database", device);
1189 case SUBCOMMAND_SUBSCRIBE: {
1193 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1194 !pa_tagstruct_eof(t))
1198 pa_idxset_put(u->subscribed, c, NULL);
1200 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1209 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1215 pa_tagstruct_free(reply);
1220 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1225 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1229 int pa__init(pa_module*m) {
1230 pa_modargs *ma = NULL;
1236 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1240 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1241 pa_log("Failed to parse module arguments");
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");
1252 m->userdata = u = pa_xnew0(struct userdata, 1);
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);
1260 u->protocol = pa_native_protocol_get(m->core);
1261 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
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);
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);
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);
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);
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);
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);
1288 if (!(fname = pa_state_path("device-manager", TRUE)))
1291 if (!(u->database = pa_database_open(fname, TRUE))) {
1292 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1297 pa_log_info("Sucessfully opened database file '%s'.", fname);
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);
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);
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);
1311 #ifdef DUMP_DATABASE
1315 pa_modargs_free(ma);
1322 pa_modargs_free(ma);
1327 void pa__done(pa_module*m) {
1332 if (!(u = m->userdata))
1335 if (u->subscription)
1336 pa_subscription_free(u->subscription);
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);
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);
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);
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);
1358 if (u->save_time_event)
1359 u->core->mainloop->time_free(u->save_time_event);
1362 pa_database_close(u->database);
1365 pa_native_protocol_remove_ext(u->protocol, m);
1366 pa_native_protocol_unref(u->protocol);
1370 pa_idxset_free(u->subscribed, NULL, NULL);