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_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[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]);
247 pa_log_debug(" Highest priority devices per-role:");
249 pa_log_debug(" Sinks:");
250 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
252 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
253 strncpy(name, role_names[role], len);
254 for (int i = len+1; i < 12; ++i) name[i] = ' ';
255 name[len] = ':'; name[0] -= 32; name[12] = '\0';
256 dump_database_helper(u, role, name, TRUE);
259 pa_log_debug(" Sources:");
260 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
262 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
263 strncpy(name, role_names[role], len);
264 for (int i = len+1; i < 12; ++i) name[i] = ' ';
265 name[len] = ':'; name[0] -= 32; name[12] = '\0';
266 dump_database_helper(u, role, name, FALSE);
270 pa_log_debug("Completed database dump");
274 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
275 struct userdata *u = userdata;
281 pa_assert(e == u->save_time_event);
282 u->core->mainloop->time_free(u->save_time_event);
283 u->save_time_event = NULL;
285 pa_database_sync(u->database);
286 pa_log_info("Synced.");
293 static void notify_subscribers(struct userdata *u) {
295 pa_native_connection *c;
300 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
303 t = pa_tagstruct_new(NULL, 0);
304 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
305 pa_tagstruct_putu32(t, 0);
306 pa_tagstruct_putu32(t, u->module->index);
307 pa_tagstruct_puts(t, u->module->name);
308 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
310 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
314 static void trigger_save(struct userdata *u) {
318 notify_subscribers(u);
320 if (u->save_time_event)
323 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
326 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
331 if (strncmp(a->description, b->description, sizeof(a->description))
332 || strncmp(a->icon, b->icon, sizeof(a->icon)))
335 for (int i=0; i < NUM_ROLES; ++i)
336 if (a->priority[i] != b->priority[i])
342 static char *get_name(const char *key, const char *prefix) {
345 if (strncmp(key, prefix, strlen(prefix)))
348 t = pa_xstrdup(key + strlen(prefix));
352 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
360 if ((old = read_entry(u, name)))
363 /* This is a new device, so make sure we write it's priority list correctly */
364 role_indexes_t max_priority;
368 pa_zero(max_priority);
369 done = !pa_database_first(u->database, &key, NULL);
371 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
375 done = !pa_database_next(u->database, &key, &next_key, NULL);
377 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
381 name2 = pa_xstrndup(key.data, key.size);
383 if ((e = read_entry(u, name2))) {
384 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
385 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
397 /* Actually initialise our entry now we've calculated it */
398 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
399 entry->priority[i] = max_priority[i] + 1;
406 static uint32_t get_role_index(const char* role) {
409 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
410 if (strcmp(role, role_names[i]) == 0)
413 return PA_INVALID_INDEX;
416 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
417 role_indexes_t *indexes, highest_priority_available;
419 pa_bool_t done, sink_mode;
424 sink_mode = (strcmp(prefix, "sink:") == 0);
427 indexes = &u->preferred_sinks;
429 indexes = &u->preferred_sources;
431 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
432 (*indexes)[i] = PA_INVALID_INDEX;
434 pa_zero(highest_priority_available);
436 done = !pa_database_first(u->database, &key, NULL);
438 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
442 done = !pa_database_next(u->database, &key, &next_key, NULL);
444 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
445 char *name, *device_name;
448 name = pa_xstrndup(key.data, key.size);
449 device_name = get_name(name, prefix);
451 if ((e = read_entry(u, name)) && ENTRY_VERSION == e->version) {
452 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
453 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
454 /* We've found a device with a higher priority than that we've currently got,
455 so see if it is currently available or not and update our list */
457 pa_bool_t found = FALSE;
462 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
463 if ((pa_sink*) ignore_device == sink)
465 if (strcmp(sink->name, device_name) == 0) {
467 idx = sink->index; /* Is this needed? */
474 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
475 if ((pa_source*) ignore_device == source)
477 if (strcmp(source->name, device_name) == 0) {
479 idx = source->index; /* Is this needed? */
485 highest_priority_available[i] = e->priority[i];
496 pa_xfree(device_name);
505 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
507 uint32_t role_index, device_index;
511 pa_assert(u->do_routing);
516 /* Skip this if it is already in the process of being moved anyway */
520 /* It might happen that a stream and a sink are set up at the
521 same time, in which case we want to make sure we don't
522 interfere with that */
523 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
526 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
527 role_index = get_role_index("none");
529 role_index = get_role_index(role);
531 if (PA_INVALID_INDEX == role_index)
534 device_index = u->preferred_sinks[role_index];
535 if (PA_INVALID_INDEX == device_index)
538 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
541 if (si->sink != sink)
542 pa_sink_input_move_to(si, sink, TRUE);
545 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
554 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
556 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
557 route_sink_input(u, si);
563 static void route_source_output(struct userdata *u, pa_source_output *so) {
565 uint32_t role_index, device_index;
569 pa_assert(u->do_routing);
574 if (so->direct_on_input)
577 /* Skip this if it is already in the process of being moved anyway */
581 /* It might happen that a stream and a source are set up at the
582 same time, in which case we want to make sure we don't
583 interfere with that */
584 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
587 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
588 role_index = get_role_index("none");
590 role_index = get_role_index(role);
592 if (PA_INVALID_INDEX == role_index)
595 device_index = u->preferred_sources[role_index];
596 if (PA_INVALID_INDEX == device_index)
599 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
602 if (so->source != source)
603 pa_source_output_move_to(so, source, TRUE);
606 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
607 pa_source_output *so;
615 update_highest_priority_device_indexes(u, "source:", ignore_source);
617 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
618 route_source_output(u, so);
624 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
625 struct userdata *u = userdata;
626 struct entry entry, *old = NULL;
633 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
634 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
635 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
636 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
638 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
639 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
640 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
641 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
645 entry.version = ENTRY_VERSION;
647 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
652 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
655 /* The role may change mid-stream, so we reroute */
656 route_sink_input(u, si);
659 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
660 pa_source_output *so;
664 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
667 /* The role may change mid-stream, so we reroute */
668 route_source_output(u, so);
671 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
674 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
677 name = pa_sprintf_malloc("sink:%s", sink->name);
679 old = load_or_initialize_entry(u, &entry, name, "sink:");
681 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
682 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
684 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
687 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
689 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
692 if (source->monitor_of)
695 name = pa_sprintf_malloc("source:%s", source->name);
697 old = load_or_initialize_entry(u, &entry, name, "source:");
699 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
700 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
707 if (entries_equal(old, &entry)) {
718 key.size = strlen(name);
721 data.size = sizeof(entry);
723 pa_log_info("Storing device %s.", name);
725 pa_database_set(u->database, &key, &data, TRUE);
732 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
740 name = pa_sprintf_malloc("sink:%s", new_data->name);
742 if ((e = read_entry(u, name))) {
743 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
744 pa_log_info("Restoring description for sink %s.", new_data->name);
745 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
756 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
764 name = pa_sprintf_malloc("source:%s", new_data->name);
766 if ((e = read_entry(u, name))) {
767 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
768 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
769 pa_log_info("Restoring description for source %s.", new_data->name);
770 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
781 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
790 pa_log_debug("Not restoring device for stream, because already set.");
795 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
796 role_index = get_role_index("");
798 role_index = get_role_index(role);
800 if (PA_INVALID_INDEX != role_index) {
801 uint32_t device_index;
803 device_index = u->preferred_sinks[role_index];
804 if (PA_INVALID_INDEX != device_index) {
807 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
808 new_data->sink = sink;
809 new_data->save_sink = TRUE;
818 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
826 if (new_data->direct_on_input)
829 if (new_data->source)
830 pa_log_debug("Not restoring device for stream, because already set");
835 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
836 role_index = get_role_index("");
838 role_index = get_role_index(role);
840 if (PA_INVALID_INDEX != role_index) {
841 uint32_t device_index;
843 device_index = u->preferred_sources[role_index];
844 if (PA_INVALID_INDEX != device_index) {
847 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
848 new_data->source = source;
849 new_data->save_source = TRUE;
859 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
862 pa_assert(u->core == c);
863 pa_assert(u->on_hotplug);
865 notify_subscribers(u);
867 return route_sink_inputs(u, NULL);
870 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
873 pa_assert(u->core == c);
874 pa_assert(u->on_hotplug);
876 notify_subscribers(u);
878 return route_source_outputs(u, NULL);
881 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
885 pa_assert(u->core == c);
886 pa_assert(u->on_rescue);
888 /* There's no point in doing anything if the core is shut down anyway */
889 if (c->state == PA_CORE_SHUTDOWN)
892 notify_subscribers(u);
894 return route_sink_inputs(u, sink);
897 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
901 pa_assert(u->core == c);
902 pa_assert(u->on_rescue);
904 /* There's no point in doing anything if the core is shut down anyway */
905 if (c->state == PA_CORE_SHUTDOWN)
908 notify_subscribers(u);
910 return route_source_outputs(u, source);
914 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
924 if ((n = get_name(name, "sink:"))) {
925 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
926 if (!pa_streq(sink->name, n)) {
930 pa_log_info("Setting description for sink %s.", sink->name);
931 pa_sink_set_description(sink, e->description);
935 else if ((n = get_name(name, "source:"))) {
936 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
937 if (!pa_streq(source->name, n)) {
941 if (source->monitor_of) {
942 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
946 pa_log_info("Setting description for source %s.", source->name);
947 pa_source_set_description(source, e->description);
954 #define EXT_VERSION 1
956 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
959 pa_tagstruct *reply = NULL;
968 if (pa_tagstruct_getu32(t, &command) < 0)
971 reply = pa_tagstruct_new(NULL, 0);
972 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
973 pa_tagstruct_putu32(reply, tag);
976 case SUBCOMMAND_TEST: {
977 if (!pa_tagstruct_eof(t))
980 pa_tagstruct_putu32(reply, EXT_VERSION);
984 case SUBCOMMAND_READ: {
988 if (!pa_tagstruct_eof(t))
991 done = !pa_database_first(u->database, &key, NULL);
998 done = !pa_database_next(u->database, &key, &next_key, NULL);
1000 name = pa_xstrndup(key.data, key.size);
1001 pa_datum_free(&key);
1003 if ((e = read_entry(u, name))) {
1006 pa_bool_t available = FALSE;
1008 if ((devname = get_name(name, "sink:"))) {
1010 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1011 if (strcmp(s->name, devname) == 0) {
1017 } else if ((devname = get_name(name, "source:"))) {
1019 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1020 if (strcmp(s->name, devname) == 0) {
1028 pa_tagstruct_puts(reply, name);
1029 pa_tagstruct_puts(reply, e->description);
1030 pa_tagstruct_puts(reply, e->icon);
1031 pa_tagstruct_put_boolean(reply, available);
1032 pa_tagstruct_putu32(reply, NUM_ROLES);
1034 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1035 pa_tagstruct_puts(reply, role_names[i]);
1036 pa_tagstruct_putu32(reply, e->priority[i]);
1050 case SUBCOMMAND_RENAME: {
1053 const char *device, *description;
1055 if (pa_tagstruct_gets(t, &device) < 0 ||
1056 pa_tagstruct_gets(t, &description) < 0)
1059 if (!device || !*device || !description || !*description)
1062 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1065 pa_strlcpy(e->description, description, sizeof(e->description));
1067 key.data = (char *) device;
1068 key.size = strlen(device);
1071 data.size = sizeof(*e);
1073 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1074 apply_entry(u, device, e);
1079 pa_log_warn("Could not save device");
1084 pa_log_warn("Could not rename device %s, no entry in database", device);
1089 case SUBCOMMAND_DELETE:
1091 while (!pa_tagstruct_eof(t)) {
1095 if (pa_tagstruct_gets(t, &name) < 0)
1098 key.data = (char*) name;
1099 key.size = strlen(name);
1101 /** @todo: Reindex the priorities */
1102 pa_database_unset(u->database, &key);
1109 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1113 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1116 if ((u->do_routing = enable)) {
1117 /* Update our caches */
1118 update_highest_priority_device_indexes(u, "sink:", NULL);
1119 update_highest_priority_device_indexes(u, "source:", NULL);
1125 case SUBCOMMAND_REORDER: {
1129 uint32_t role_index, n_devices;
1131 pa_bool_t done, sink_mode = TRUE;
1132 struct device_t { uint32_t prio; char *device; };
1133 struct device_t *device;
1134 struct device_t **devices;
1135 uint32_t i, idx, offset;
1140 if (pa_tagstruct_gets(t, &role) < 0 ||
1141 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1145 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1148 /* Cycle through the devices given and make sure they exist */
1149 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1152 for (i = 0; i < n_devices; ++i) {
1154 if (pa_tagstruct_gets(t, &s) < 0) {
1155 while ((device = pa_hashmap_steal_first(h))) {
1156 pa_xfree(device->device);
1160 pa_hashmap_free(h, NULL, NULL);
1161 pa_log_error("Protocol error on reorder");
1165 /* Ensure this is a valid entry */
1166 if (!(e = read_entry(u, s))) {
1167 while ((device = pa_hashmap_steal_first(h))) {
1168 pa_xfree(device->device);
1172 pa_hashmap_free(h, NULL, NULL);
1173 pa_log_error("Client specified an unknown device in it's reorder list.");
1180 sink_mode = (0 == strncmp("sink:", s, 5));
1181 } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
1182 || (!sink_mode && 0 != strncmp("source:", s, 7)))
1184 while ((device = pa_hashmap_steal_first(h))) {
1185 pa_xfree(device->device);
1189 pa_hashmap_free(h, NULL, NULL);
1190 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1194 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1195 device = pa_xnew(struct device_t, 1);
1196 device->device = pa_xstrdup(s);
1197 if (pa_hashmap_put(h, device->device, device) == 0) {
1201 pa_xfree(device->device);
1206 /*pa_log_debug("Hashmap contents (received from client)");
1207 PA_HASHMAP_FOREACH(device, h, state) {
1208 pa_log_debug(" - %s (%d)", device->device, device->prio);
1211 /* Now cycle through our list and add all the devices.
1212 This has the effect of addign in any in our DB,
1213 not specified in the device list (and thus will be
1214 tacked on at the end) */
1216 done = !pa_database_first(u->database, &key, NULL);
1218 while (!done && idx < 256) {
1221 done = !pa_database_next(u->database, &key, &next_key, NULL);
1223 device = pa_xnew(struct device_t, 1);
1224 device->device = pa_xstrndup(key.data, key.size);
1225 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1226 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1228 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1229 if (pa_hashmap_put(h, device->device, device) == 0
1230 && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
1231 /* We add offset on to the existing priorirty so that when we order, the
1232 existing entries are always lower priority than the new ones. */
1233 device->prio = (offset + e->priority[role_index]);
1237 pa_xfree(device->device);
1241 pa_xfree(device->device);
1245 pa_datum_free(&key);
1250 /*pa_log_debug("Hashmap contents (combined with database)");
1251 PA_HASHMAP_FOREACH(device, h, state) {
1252 pa_log_debug(" - %s (%d)", device->device, device->prio);
1255 /* Now we put all the entries in a simple list for sorting it. */
1256 n_devices = pa_hashmap_size(h);
1257 devices = pa_xnew(struct device_t *, n_devices);
1259 while ((device = pa_hashmap_steal_first(h))) {
1260 devices[idx++] = device;
1262 pa_hashmap_free(h, NULL, NULL);
1264 /* Simple bubble sort */
1265 for (i = 0; i < n_devices; ++i) {
1266 for (uint32_t j = i; j < n_devices; ++j) {
1267 if (devices[i]->prio > devices[j]->prio) {
1268 struct device_t *tmp;
1270 devices[i] = devices[j];
1276 /*pa_log_debug("Sorted device list");
1277 for (i = 0; i < n_devices; ++i) {
1278 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1281 /* Go through in order and write the new entry and cleanup our own list */
1284 for (i = 0; i < n_devices; ++i) {
1285 if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
1286 if (e->priority[role_index] == idx)
1289 e->priority[role_index] = idx;
1291 key.data = (char *) devices[i]->device;
1292 key.size = strlen(devices[i]->device);
1295 data.size = sizeof(*e);
1297 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1305 pa_xfree(devices[i]->device);
1306 pa_xfree(devices[i]);
1313 route_sink_inputs(u, NULL);
1315 route_source_outputs(u, NULL);
1321 case SUBCOMMAND_SUBSCRIBE: {
1325 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1326 !pa_tagstruct_eof(t))
1330 pa_idxset_put(u->subscribed, c, NULL);
1332 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1341 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1347 pa_tagstruct_free(reply);
1352 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1357 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1361 int pa__init(pa_module*m) {
1362 pa_modargs *ma = NULL;
1368 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1372 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1373 pa_log("Failed to parse module arguments");
1377 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1378 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1379 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1380 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1384 m->userdata = u = pa_xnew0(struct userdata, 1);
1387 u->do_routing = do_routing;
1388 u->on_hotplug = on_hotplug;
1389 u->on_rescue = on_rescue;
1390 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1392 u->protocol = pa_native_protocol_get(m->core);
1393 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1395 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);
1397 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);
1399 /* Used to handle device description management */
1400 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);
1401 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);
1403 /* The following slots are used to deal with routing */
1404 /* A little bit later than module-stream-restore, module-intended-roles */
1405 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);
1406 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);
1409 /* A little bit later than module-stream-restore, module-intended-roles */
1410 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);
1411 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);
1415 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1416 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);
1417 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);
1420 if (!(fname = pa_state_path("device-manager", TRUE)))
1423 if (!(u->database = pa_database_open(fname, TRUE))) {
1424 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1429 pa_log_info("Sucessfully opened database file '%s'.", fname);
1432 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1433 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1434 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1436 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1437 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1439 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1440 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1441 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1444 route_sink_inputs(u, NULL);
1445 route_source_outputs(u, NULL);
1447 #ifdef DUMP_DATABASE
1451 pa_modargs_free(ma);
1458 pa_modargs_free(ma);
1463 void pa__done(pa_module*m) {
1468 if (!(u = m->userdata))
1471 if (u->subscription)
1472 pa_subscription_free(u->subscription);
1474 if (u->sink_new_hook_slot)
1475 pa_hook_slot_free(u->sink_new_hook_slot);
1476 if (u->source_new_hook_slot)
1477 pa_hook_slot_free(u->source_new_hook_slot);
1479 if (u->sink_input_new_hook_slot)
1480 pa_hook_slot_free(u->sink_input_new_hook_slot);
1481 if (u->source_output_new_hook_slot)
1482 pa_hook_slot_free(u->source_output_new_hook_slot);
1484 if (u->sink_put_hook_slot)
1485 pa_hook_slot_free(u->sink_put_hook_slot);
1486 if (u->source_put_hook_slot)
1487 pa_hook_slot_free(u->source_put_hook_slot);
1489 if (u->sink_unlink_hook_slot)
1490 pa_hook_slot_free(u->sink_unlink_hook_slot);
1491 if (u->source_unlink_hook_slot)
1492 pa_hook_slot_free(u->source_unlink_hook_slot);
1494 if (u->save_time_event)
1495 u->core->mainloop->time_free(u->save_time_event);
1498 pa_database_close(u->database);
1501 pa_native_protocol_remove_ext(u->protocol, m);
1502 pa_native_protocol_unref(u->protocol);
1506 pa_idxset_free(u->subscribed, NULL, NULL);