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 char icon[PA_NAME_MAX];
137 role_indexes_t priority;
145 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
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_MIN(12u, strlen(role_names[role]));
252 strncpy(name, role_names[role], len);
253 for (int i = len+1; i < 12; ++i) name[i] = ' ';
254 name[len] = ':'; name[0] -= 32; name[12] = '\0';
255 dump_database_helper(u, role, name, TRUE);
258 pa_log_debug(" Sources:");
259 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
261 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
262 strncpy(name, role_names[role], len);
263 for (int i = len+1; i < 12; ++i) name[i] = ' ';
264 name[len] = ':'; name[0] -= 32; name[12] = '\0';
265 dump_database_helper(u, role, name, FALSE);
268 pa_log_debug("Completed database dump");
272 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
273 struct userdata *u = userdata;
279 pa_assert(e == u->save_time_event);
280 u->core->mainloop->time_free(u->save_time_event);
281 u->save_time_event = NULL;
283 pa_database_sync(u->database);
284 pa_log_info("Synced.");
291 static void notify_subscribers(struct userdata *u) {
293 pa_native_connection *c;
298 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
301 t = pa_tagstruct_new(NULL, 0);
302 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
303 pa_tagstruct_putu32(t, 0);
304 pa_tagstruct_putu32(t, u->module->index);
305 pa_tagstruct_puts(t, u->module->name);
306 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
308 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
312 static void trigger_save(struct userdata *u) {
316 notify_subscribers(u);
318 if (u->save_time_event)
321 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
324 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
329 if (strncmp(a->description, b->description, sizeof(a->description))
330 || strncmp(a->icon, b->icon, sizeof(a->icon)))
333 for (int i=0; i < NUM_ROLES; ++i)
334 if (a->priority[i] != b->priority[i])
340 static char *get_name(const char *key, const char *prefix) {
343 if (strncmp(key, prefix, strlen(prefix)))
346 t = pa_xstrdup(key + strlen(prefix));
350 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
358 if ((old = read_entry(u, name)))
361 /* This is a new device, so make sure we write it's priority list correctly */
362 role_indexes_t max_priority;
366 pa_zero(max_priority);
367 done = !pa_database_first(u->database, &key, NULL);
369 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
373 done = !pa_database_next(u->database, &key, &next_key, NULL);
375 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
379 name2 = pa_xstrndup(key.data, key.size);
381 if ((e = read_entry(u, name2))) {
382 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
383 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
395 /* Actually initialise our entry now we've calculated it */
396 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
397 entry->priority[i] = max_priority[i] + 1;
404 static uint32_t get_role_index(const char* role) {
407 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
408 if (strcmp(role, role_names[i]) == 0)
411 return PA_INVALID_INDEX;
414 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
415 role_indexes_t *indexes, highest_priority_available;
417 pa_bool_t done, sink_mode;
422 sink_mode = (strcmp(prefix, "sink:") == 0);
425 indexes = &u->preferred_sinks;
427 indexes = &u->preferred_sources;
429 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
430 *indexes[i] = PA_INVALID_INDEX;
432 pa_zero(highest_priority_available);
434 done = !pa_database_first(u->database, &key, NULL);
436 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
440 done = !pa_database_next(u->database, &key, &next_key, NULL);
442 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
446 name = pa_xstrndup(key.data, key.size);
448 if ((e = read_entry(u, name))) {
449 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
450 if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
451 /* We've found a device with a higher priority than that we've currently got,
452 so see if it is currently available or not and update our list */
454 pa_bool_t found = FALSE;
455 char *device_name = get_name(name, prefix);
460 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
461 if ((pa_sink*) ignore_device == sink)
463 if (strcmp(sink->name, device_name) == 0) {
465 idx = sink->index; /* Is this needed? */
472 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
473 if ((pa_source*) ignore_device == source)
475 if (strcmp(source->name, device_name) == 0) {
477 idx = source->index; /* Is this needed? */
483 highest_priority_available[i] = e->priority[i];
487 pa_xfree(device_name);
503 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
505 uint32_t role_index, device_index;
509 pa_assert(u->do_routing);
514 /* Skip this if it is already in the process of being moved anyway */
518 /* It might happen that a stream and a sink are set up at the
519 same time, in which case we want to make sure we don't
520 interfere with that */
521 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
524 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
525 role_index = get_role_index("none");
527 role_index = get_role_index(role);
529 if (PA_INVALID_INDEX == role_index)
532 device_index = u->preferred_sinks[role_index];
533 if (PA_INVALID_INDEX == device_index)
536 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
539 if (si->sink != sink)
540 pa_sink_input_move_to(si, sink, TRUE);
543 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
552 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
554 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
555 route_sink_input(u, si);
561 static void route_source_output(struct userdata *u, pa_source_output *so) {
563 uint32_t role_index, device_index;
567 pa_assert(u->do_routing);
572 if (so->direct_on_input)
575 /* Skip this if it is already in the process of being moved anyway */
579 /* It might happen that a stream and a source are set up at the
580 same time, in which case we want to make sure we don't
581 interfere with that */
582 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
585 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
586 role_index = get_role_index("none");
588 role_index = get_role_index(role);
590 if (PA_INVALID_INDEX == role_index)
593 device_index = u->preferred_sources[role_index];
594 if (PA_INVALID_INDEX == device_index)
597 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
600 if (so->source != source)
601 pa_source_output_move_to(so, source, TRUE);
604 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
605 pa_source_output *so;
613 update_highest_priority_device_indexes(u, "source:", ignore_source);
615 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
616 route_source_output(u, so);
622 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
623 struct userdata *u = userdata;
624 struct entry entry, *old = NULL;
631 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
632 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
633 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
634 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
636 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
637 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
638 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
639 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
643 entry.version = ENTRY_VERSION;
645 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
650 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
653 /* The role may change mid-stream, so we reroute */
654 route_sink_input(u, si);
657 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
658 pa_source_output *so;
662 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
665 /* The role may change mid-stream, so we reroute */
666 route_source_output(u, so);
669 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
672 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
675 name = pa_sprintf_malloc("sink:%s", sink->name);
677 old = load_or_initialize_entry(u, &entry, name, "sink:");
679 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
680 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
682 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
685 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
687 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
690 if (source->monitor_of)
693 name = pa_sprintf_malloc("source:%s", source->name);
695 old = load_or_initialize_entry(u, &entry, name, "source:");
697 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
698 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
705 if (entries_equal(old, &entry)) {
716 key.size = strlen(name);
719 data.size = sizeof(entry);
721 pa_log_info("Storing device %s.", name);
723 pa_database_set(u->database, &key, &data, TRUE);
730 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
738 name = pa_sprintf_malloc("sink:%s", new_data->name);
740 if ((e = read_entry(u, name))) {
741 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
742 pa_log_info("Restoring description for sink %s.", new_data->name);
743 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
754 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
762 name = pa_sprintf_malloc("source:%s", new_data->name);
764 if ((e = read_entry(u, name))) {
765 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
766 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
767 pa_log_info("Restoring description for source %s.", new_data->name);
768 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
779 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
788 pa_log_debug("Not restoring device for stream, because already set.");
793 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
794 role_index = get_role_index("");
796 role_index = get_role_index(role);
798 if (PA_INVALID_INDEX != role_index) {
799 uint32_t device_index;
801 device_index = u->preferred_sinks[role_index];
802 if (PA_INVALID_INDEX != device_index) {
805 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
806 new_data->sink = sink;
807 new_data->save_sink = TRUE;
816 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
824 if (new_data->direct_on_input)
827 if (new_data->source)
828 pa_log_debug("Not restoring device for stream, because already set");
833 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
834 role_index = get_role_index("");
836 role_index = get_role_index(role);
838 if (PA_INVALID_INDEX != role_index) {
839 uint32_t device_index;
841 device_index = u->preferred_sources[role_index];
842 if (PA_INVALID_INDEX != device_index) {
845 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
846 new_data->source = source;
847 new_data->save_source = TRUE;
857 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
860 pa_assert(u->core == c);
861 pa_assert(u->on_hotplug);
863 notify_subscribers(u);
865 return route_sink_inputs(u, NULL);
868 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
871 pa_assert(u->core == c);
872 pa_assert(u->on_hotplug);
874 notify_subscribers(u);
876 return route_source_outputs(u, NULL);
879 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
883 pa_assert(u->core == c);
884 pa_assert(u->on_rescue);
886 /* There's no point in doing anything if the core is shut down anyway */
887 if (c->state == PA_CORE_SHUTDOWN)
890 notify_subscribers(u);
892 return route_sink_inputs(u, sink);
895 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
899 pa_assert(u->core == c);
900 pa_assert(u->on_rescue);
902 /* There's no point in doing anything if the core is shut down anyway */
903 if (c->state == PA_CORE_SHUTDOWN)
906 notify_subscribers(u);
908 return route_source_outputs(u, source);
912 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
922 if ((n = get_name(name, "sink:"))) {
923 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
924 if (!pa_streq(sink->name, n)) {
928 pa_log_info("Setting description for sink %s.", sink->name);
929 pa_sink_set_description(sink, e->description);
933 else if ((n = get_name(name, "source:"))) {
934 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
935 if (!pa_streq(source->name, n)) {
939 if (source->monitor_of) {
940 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
944 pa_log_info("Setting description for source %s.", source->name);
945 pa_source_set_description(source, e->description);
952 #define EXT_VERSION 1
954 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
957 pa_tagstruct *reply = NULL;
966 if (pa_tagstruct_getu32(t, &command) < 0)
969 reply = pa_tagstruct_new(NULL, 0);
970 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
971 pa_tagstruct_putu32(reply, tag);
974 case SUBCOMMAND_TEST: {
975 if (!pa_tagstruct_eof(t))
978 pa_tagstruct_putu32(reply, EXT_VERSION);
982 case SUBCOMMAND_READ: {
986 if (!pa_tagstruct_eof(t))
989 done = !pa_database_first(u->database, &key, NULL);
996 done = !pa_database_next(u->database, &key, &next_key, NULL);
998 name = pa_xstrndup(key.data, key.size);
1001 if ((e = read_entry(u, name))) {
1004 pa_bool_t available = FALSE;
1006 if ((devname = get_name(name, "sink:"))) {
1008 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1009 if (strcmp(s->name, devname) == 0) {
1015 } else if ((devname = get_name(name, "source:"))) {
1017 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1018 if (strcmp(s->name, devname) == 0) {
1026 pa_tagstruct_puts(reply, name);
1027 pa_tagstruct_puts(reply, e->description);
1028 pa_tagstruct_puts(reply, e->icon);
1029 pa_tagstruct_put_boolean(reply, available);
1030 pa_tagstruct_putu32(reply, NUM_ROLES);
1032 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1033 pa_tagstruct_puts(reply, role_names[i]);
1034 pa_tagstruct_putu32(reply, e->priority[i]);
1048 case SUBCOMMAND_RENAME: {
1051 const char *device, *description;
1053 if (pa_tagstruct_gets(t, &device) < 0 ||
1054 pa_tagstruct_gets(t, &description) < 0)
1057 if (!device || !*device || !description || !*description)
1060 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1063 pa_strlcpy(e->description, description, sizeof(e->description));
1065 key.data = (char *) device;
1066 key.size = strlen(device);
1069 data.size = sizeof(*e);
1071 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1072 apply_entry(u, device, e);
1077 pa_log_warn("Could not save device");
1082 pa_log_warn("Could not rename device %s, no entry in database", device);
1087 case SUBCOMMAND_DELETE:
1089 while (!pa_tagstruct_eof(t)) {
1093 if (pa_tagstruct_gets(t, &name) < 0)
1096 key.data = (char*) name;
1097 key.size = strlen(name);
1099 /** @todo: Reindex the priorities */
1100 pa_database_unset(u->database, &key);
1107 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1111 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1114 if ((u->do_routing = enable)) {
1115 /* Update our caches */
1116 update_highest_priority_device_indexes(u, "sink:", NULL);
1117 update_highest_priority_device_indexes(u, "source:", NULL);
1123 case SUBCOMMAND_REORDER: {
1127 uint32_t role_index, n_devices;
1129 pa_bool_t done, sink_mode = TRUE;
1130 struct device_t { uint32_t prio; char *device; };
1131 struct device_t *device;
1132 struct device_t **devices;
1133 uint32_t i, idx, offset;
1138 if (pa_tagstruct_gets(t, &role) < 0 ||
1139 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1143 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1146 /* Cycle through the devices given and make sure they exist */
1147 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1150 for (i = 0; i < n_devices; ++i) {
1152 if (pa_tagstruct_gets(t, &s) < 0) {
1153 while ((device = pa_hashmap_steal_first(h))) {
1154 pa_xfree(device->device);
1158 pa_hashmap_free(h, NULL, NULL);
1159 pa_log_error("Protocol error on reorder");
1163 /* Ensure this is a valid entry */
1164 if (!(e = read_entry(u, s))) {
1165 while ((device = pa_hashmap_steal_first(h))) {
1166 pa_xfree(device->device);
1170 pa_hashmap_free(h, NULL, NULL);
1171 pa_log_error("Client specified an unknown device in it's reorder list.");
1178 sink_mode = (0 == strncmp("sink:", s, 5));
1179 } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
1180 || (!sink_mode && 0 != strncmp("source:", s, 7)))
1182 while ((device = pa_hashmap_steal_first(h))) {
1183 pa_xfree(device->device);
1187 pa_hashmap_free(h, NULL, NULL);
1188 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1192 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1193 device = pa_xnew(struct device_t, 1);
1194 device->device = pa_xstrdup(s);
1195 if (pa_hashmap_put(h, device->device, device) == 0) {
1199 pa_xfree(device->device);
1204 pa_log_debug("Hashmap contents (received from client)");
1205 PA_HASHMAP_FOREACH(device, h, state) {
1206 pa_log_debug(" - %s (%d)", device->device, device->prio);
1209 /* Now cycle through our list and add all the devices.
1210 This has the effect of addign in any in our DB,
1211 not specified in the device list (and thus will be
1212 tacked on at the end) */
1214 done = !pa_database_first(u->database, &key, NULL);
1216 while (!done && idx < 256) {
1219 done = !pa_database_next(u->database, &key, &next_key, NULL);
1221 device = pa_xnew(struct device_t, 1);
1222 device->device = pa_xstrndup(key.data, key.size);
1223 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1224 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1226 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1227 if (pa_hashmap_put(h, device->device, device) == 0
1228 && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
1229 /* We add offset on to the existing priorirty so that when we order, the
1230 existing entries are always lower priority than the new ones. */
1231 device->prio = (offset + e->priority[role_index]);
1235 pa_xfree(device->device);
1239 pa_xfree(device->device);
1243 pa_datum_free(&key);
1248 pa_log_debug("Hashmap contents (combined with database)");
1249 PA_HASHMAP_FOREACH(device, h, state) {
1250 pa_log_debug(" - %s (%d)", device->device, device->prio);
1253 /* Now we put all the entries in a simple list for sorting it. */
1254 n_devices = pa_hashmap_size(h);
1255 devices = pa_xnew(struct device_t *, n_devices);
1257 while ((device = pa_hashmap_steal_first(h))) {
1258 devices[idx++] = device;
1260 pa_hashmap_free(h, NULL, NULL);
1262 /* Simple bubble sort */
1263 for (i = 0; i < n_devices; ++i) {
1264 for (uint32_t j = i; j < n_devices; ++j) {
1265 if (devices[i]->prio > devices[j]->prio) {
1266 struct device_t *tmp;
1268 devices[i] = devices[j];
1274 pa_log_debug("Sorted device list");
1275 for (i = 0; i < n_devices; ++i) {
1276 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1279 /* Go through in order and write the new entry and cleanup our own list */
1282 for (i = 0; i < n_devices; ++i) {
1283 if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
1284 if (e->priority[role_index] == idx)
1287 e->priority[role_index] = idx;
1289 key.data = (char *) devices[i]->device;
1290 key.size = strlen(devices[i]->device);
1293 data.size = sizeof(*e);
1295 pa_log_debug("Attempting to write record: %d. %s", e->priority[role_index], e->description);
1296 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1297 pa_log_debug("..... write successfull");
1305 pa_xfree(devices[i]->device);
1306 pa_xfree(devices[i]);
1315 case SUBCOMMAND_SUBSCRIBE: {
1319 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1320 !pa_tagstruct_eof(t))
1324 pa_idxset_put(u->subscribed, c, NULL);
1326 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1335 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1341 pa_tagstruct_free(reply);
1346 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1351 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1355 int pa__init(pa_module*m) {
1356 pa_modargs *ma = NULL;
1362 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1366 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1367 pa_log("Failed to parse module arguments");
1371 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1372 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1373 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1374 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1378 m->userdata = u = pa_xnew0(struct userdata, 1);
1381 u->do_routing = do_routing;
1382 u->on_hotplug = on_hotplug;
1383 u->on_rescue = on_rescue;
1384 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1386 u->protocol = pa_native_protocol_get(m->core);
1387 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1389 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);
1391 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);
1393 /* Used to handle device description management */
1394 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);
1395 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);
1397 /* The following slots are used to deal with routing */
1398 /* A little bit later than module-stream-restore, module-intended-roles */
1399 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);
1400 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);
1403 /* A little bit later than module-stream-restore, module-intended-roles */
1404 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);
1405 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);
1409 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1410 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);
1411 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);
1414 if (!(fname = pa_state_path("device-manager", TRUE)))
1417 if (!(u->database = pa_database_open(fname, TRUE))) {
1418 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1423 pa_log_info("Sucessfully opened database file '%s'.", fname);
1426 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1427 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1428 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1430 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1431 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1433 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1434 route_sink_inputs(u, NULL);
1435 route_source_outputs(u, NULL);
1437 #ifdef DUMP_DATABASE
1441 pa_modargs_free(ma);
1448 pa_modargs_free(ma);
1453 void pa__done(pa_module*m) {
1458 if (!(u = m->userdata))
1461 if (u->subscription)
1462 pa_subscription_free(u->subscription);
1464 if (u->sink_new_hook_slot)
1465 pa_hook_slot_free(u->sink_new_hook_slot);
1466 if (u->source_new_hook_slot)
1467 pa_hook_slot_free(u->source_new_hook_slot);
1469 if (u->sink_input_new_hook_slot)
1470 pa_hook_slot_free(u->sink_input_new_hook_slot);
1471 if (u->source_output_new_hook_slot)
1472 pa_hook_slot_free(u->source_output_new_hook_slot);
1474 if (u->sink_put_hook_slot)
1475 pa_hook_slot_free(u->sink_put_hook_slot);
1476 if (u->source_put_hook_slot)
1477 pa_hook_slot_free(u->source_put_hook_slot);
1479 if (u->sink_unlink_hook_slot)
1480 pa_hook_slot_free(u->sink_unlink_hook_slot);
1481 if (u->source_unlink_hook_slot)
1482 pa_hook_slot_free(u->source_unlink_hook_slot);
1484 if (u->save_time_event)
1485 u->core->mainloop->time_free(u->save_time_event);
1488 pa_database_close(u->database);
1491 pa_native_protocol_remove_ext(u->protocol, m);
1492 pa_native_protocol_unref(u->protocol);
1496 pa_idxset_free(u->subscribed, NULL, NULL);