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);
513 /* Skip this if it is already in the process of being moved anyway */
517 /* It might happen that a stream and a sink are set up at the
518 same time, in which case we want to make sure we don't
519 interfere with that */
520 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
523 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
524 role_index = get_role_index("none");
526 role_index = get_role_index(role);
528 if (PA_INVALID_INDEX == role_index)
531 device_index = u->preferred_sinks[role_index];
532 if (PA_INVALID_INDEX == device_index)
535 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
538 if (si->sink != sink)
539 pa_sink_input_move_to(si, sink, TRUE);
542 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
551 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
553 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
554 route_sink_input(u, si);
560 static void route_source_output(struct userdata *u, pa_source_output *so) {
562 uint32_t role_index, device_index;
566 pa_assert(u->do_routing);
568 if (so->direct_on_input)
571 /* Skip this if it is already in the process of being moved anyway */
575 /* It might happen that a stream and a source are set up at the
576 same time, in which case we want to make sure we don't
577 interfere with that */
578 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
581 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
582 role_index = get_role_index("none");
584 role_index = get_role_index(role);
586 if (PA_INVALID_INDEX == role_index)
589 device_index = u->preferred_sources[role_index];
590 if (PA_INVALID_INDEX == device_index)
593 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
596 if (so->source != source)
597 pa_source_output_move_to(so, source, TRUE);
600 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
601 pa_source_output *so;
609 update_highest_priority_device_indexes(u, "source:", ignore_source);
611 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
612 route_source_output(u, so);
618 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
619 struct userdata *u = userdata;
620 struct entry entry, *old = NULL;
627 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
628 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
629 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
630 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
632 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
633 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
634 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
635 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
639 entry.version = ENTRY_VERSION;
641 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
646 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
649 /* The role may change mid-stream, so we reroute */
650 route_sink_input(u, si);
653 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
654 pa_source_output *so;
658 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
661 /* The role may change mid-stream, so we reroute */
662 route_source_output(u, so);
665 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
668 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
671 name = pa_sprintf_malloc("sink:%s", sink->name);
673 old = load_or_initialize_entry(u, &entry, name, "sink:");
675 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
676 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
678 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
681 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
683 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
686 if (source->monitor_of)
689 name = pa_sprintf_malloc("source:%s", source->name);
691 old = load_or_initialize_entry(u, &entry, name, "source:");
693 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
694 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
701 if (entries_equal(old, &entry)) {
712 key.size = strlen(name);
715 data.size = sizeof(entry);
717 pa_log_info("Storing device %s.", name);
719 pa_database_set(u->database, &key, &data, TRUE);
726 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
734 name = pa_sprintf_malloc("sink:%s", new_data->name);
736 if ((e = read_entry(u, name))) {
737 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
738 pa_log_info("Restoring description for sink %s.", new_data->name);
739 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
750 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
758 name = pa_sprintf_malloc("source:%s", new_data->name);
760 if ((e = read_entry(u, name))) {
761 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
762 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
763 pa_log_info("Restoring description for source %s.", new_data->name);
764 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
775 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
787 pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
789 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
790 role_index = get_role_index("none");
792 role_index = get_role_index(role);
794 if (PA_INVALID_INDEX != role_index) {
795 uint32_t device_index;
797 device_index = u->preferred_sinks[role_index];
798 if (PA_INVALID_INDEX != device_index) {
801 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
802 new_data->sink = sink;
810 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
821 if (new_data->direct_on_input)
824 if (new_data->source)
825 pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
827 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
828 role_index = get_role_index("none");
830 role_index = get_role_index(role);
832 if (PA_INVALID_INDEX != role_index) {
833 uint32_t device_index;
835 device_index = u->preferred_sources[role_index];
836 if (PA_INVALID_INDEX != device_index) {
839 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
840 new_data->source = source;
849 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
852 pa_assert(u->core == c);
853 pa_assert(u->on_hotplug);
855 notify_subscribers(u);
857 return route_sink_inputs(u, NULL);
860 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
863 pa_assert(u->core == c);
864 pa_assert(u->on_hotplug);
866 notify_subscribers(u);
868 return route_source_outputs(u, NULL);
871 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
875 pa_assert(u->core == c);
876 pa_assert(u->on_rescue);
878 /* There's no point in doing anything if the core is shut down anyway */
879 if (c->state == PA_CORE_SHUTDOWN)
882 notify_subscribers(u);
884 return route_sink_inputs(u, sink);
887 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
891 pa_assert(u->core == c);
892 pa_assert(u->on_rescue);
894 /* There's no point in doing anything if the core is shut down anyway */
895 if (c->state == PA_CORE_SHUTDOWN)
898 notify_subscribers(u);
900 return route_source_outputs(u, source);
904 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
914 if ((n = get_name(name, "sink:"))) {
915 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
916 if (!pa_streq(sink->name, n)) {
920 pa_log_info("Setting description for sink %s.", sink->name);
921 pa_sink_set_description(sink, e->description);
925 else if ((n = get_name(name, "source:"))) {
926 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
927 if (!pa_streq(source->name, n)) {
931 if (source->monitor_of) {
932 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
936 pa_log_info("Setting description for source %s.", source->name);
937 pa_source_set_description(source, e->description);
944 #define EXT_VERSION 1
946 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
949 pa_tagstruct *reply = NULL;
958 if (pa_tagstruct_getu32(t, &command) < 0)
961 reply = pa_tagstruct_new(NULL, 0);
962 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
963 pa_tagstruct_putu32(reply, tag);
966 case SUBCOMMAND_TEST: {
967 if (!pa_tagstruct_eof(t))
970 pa_tagstruct_putu32(reply, EXT_VERSION);
974 case SUBCOMMAND_READ: {
978 if (!pa_tagstruct_eof(t))
981 done = !pa_database_first(u->database, &key, NULL);
988 done = !pa_database_next(u->database, &key, &next_key, NULL);
990 name = pa_xstrndup(key.data, key.size);
993 if ((e = read_entry(u, name))) {
996 pa_bool_t available = FALSE;
998 if ((devname = get_name(name, "sink:"))) {
1000 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1001 if (strcmp(s->name, devname) == 0) {
1007 } else if ((devname = get_name(name, "source:"))) {
1009 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1010 if (strcmp(s->name, devname) == 0) {
1018 pa_tagstruct_puts(reply, name);
1019 pa_tagstruct_puts(reply, e->description);
1020 pa_tagstruct_puts(reply, e->icon);
1021 pa_tagstruct_put_boolean(reply, available);
1022 pa_tagstruct_putu32(reply, NUM_ROLES);
1024 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1025 pa_tagstruct_puts(reply, role_names[i]);
1026 pa_tagstruct_putu32(reply, e->priority[i]);
1040 case SUBCOMMAND_RENAME: {
1043 const char *device, *description;
1045 if (pa_tagstruct_gets(t, &device) < 0 ||
1046 pa_tagstruct_gets(t, &description) < 0)
1049 if (!device || !*device || !description || !*description)
1052 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1055 pa_strlcpy(e->description, description, sizeof(e->description));
1057 key.data = (char *) device;
1058 key.size = strlen(device);
1061 data.size = sizeof(*e);
1063 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1064 apply_entry(u, device, e);
1069 pa_log_warn("Could not save device");
1074 pa_log_warn("Could not rename device %s, no entry in database", device);
1079 case SUBCOMMAND_DELETE:
1081 while (!pa_tagstruct_eof(t)) {
1085 if (pa_tagstruct_gets(t, &name) < 0)
1088 key.data = (char*) name;
1089 key.size = strlen(name);
1091 /** @todo: Reindex the priorities */
1092 pa_database_unset(u->database, &key);
1099 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1103 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1106 if ((u->do_routing = enable)) {
1107 /* Update our caches */
1108 update_highest_priority_device_indexes(u, "sink:", NULL);
1109 update_highest_priority_device_indexes(u, "source:", NULL);
1115 case SUBCOMMAND_REORDER: {
1119 uint32_t role_index, n_devices;
1121 pa_bool_t done, sink_mode = TRUE;
1122 struct device_t { uint32_t prio; char *device; };
1123 struct device_t *device;
1124 struct device_t **devices;
1125 uint32_t i, idx, offset;
1130 if (pa_tagstruct_gets(t, &role) < 0 ||
1131 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1135 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1138 /* Cycle through the devices given and make sure they exist */
1139 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1142 for (i = 0; i < n_devices; ++i) {
1144 if (pa_tagstruct_gets(t, &s) < 0) {
1145 while ((device = pa_hashmap_steal_first(h))) {
1146 pa_xfree(device->device);
1150 pa_hashmap_free(h, NULL, NULL);
1151 pa_log_error("Protocol error on reorder");
1155 /* Ensure this is a valid entry */
1156 if (!(e = read_entry(u, s))) {
1157 while ((device = pa_hashmap_steal_first(h))) {
1158 pa_xfree(device->device);
1162 pa_hashmap_free(h, NULL, NULL);
1163 pa_log_error("Client specified an unknown device in it's reorder list.");
1170 sink_mode = (0 == strncmp("sink:", s, 5));
1171 } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
1172 || (!sink_mode && 0 != strncmp("source:", s, 7)))
1174 while ((device = pa_hashmap_steal_first(h))) {
1175 pa_xfree(device->device);
1179 pa_hashmap_free(h, NULL, NULL);
1180 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1184 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1185 device = pa_xnew(struct device_t, 1);
1186 device->device = pa_xstrdup(s);
1187 if (pa_hashmap_put(h, device->device, device) == 0) {
1191 pa_xfree(device->device);
1196 /*pa_log_debug("Hashmap contents (received from client)");
1197 PA_HASHMAP_FOREACH(device, h, state) {
1198 pa_log_debug(" - %s (%d)", device->device, device->prio);
1201 /* Now cycle through our list and add all the devices.
1202 This has the effect of addign in any in our DB,
1203 not specified in the device list (and thus will be
1204 tacked on at the end) */
1206 done = !pa_database_first(u->database, &key, NULL);
1208 while (!done && idx < 256) {
1211 done = !pa_database_next(u->database, &key, &next_key, NULL);
1213 device = pa_xnew(struct device_t, 1);
1214 device->device = pa_xstrndup(key.data, key.size);
1215 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1216 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1218 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1219 if (pa_hashmap_put(h, device->device, device) == 0
1220 && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
1221 /* We add offset on to the existing priorirty so that when we order, the
1222 existing entries are always lower priority than the new ones. */
1223 device->prio = (offset + e->priority[role_index]);
1227 pa_xfree(device->device);
1231 pa_xfree(device->device);
1235 pa_datum_free(&key);
1240 /*pa_log_debug("Hashmap contents (combined with database)");
1241 PA_HASHMAP_FOREACH(device, h, state) {
1242 pa_log_debug(" - %s (%d)", device->device, device->prio);
1245 /* Now we put all the entries in a simple list for sorting it. */
1246 n_devices = pa_hashmap_size(h);
1247 devices = pa_xnew(struct device_t *, n_devices);
1249 while ((device = pa_hashmap_steal_first(h))) {
1250 devices[idx++] = device;
1252 pa_hashmap_free(h, NULL, NULL);
1254 /* Simple bubble sort */
1255 for (i = 0; i < n_devices; ++i) {
1256 for (uint32_t j = i; j < n_devices; ++j) {
1257 if (devices[i]->prio > devices[j]->prio) {
1258 struct device_t *tmp;
1260 devices[i] = devices[j];
1266 /*pa_log_debug("Sorted device list");
1267 for (i = 0; i < n_devices; ++i) {
1268 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1271 /* Go through in order and write the new entry and cleanup our own list */
1274 for (i = 0; i < n_devices; ++i) {
1275 if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
1276 if (e->priority[role_index] == idx)
1279 e->priority[role_index] = idx;
1281 key.data = (char *) devices[i]->device;
1282 key.size = strlen(devices[i]->device);
1285 data.size = sizeof(*e);
1287 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1295 pa_xfree(devices[i]->device);
1296 pa_xfree(devices[i]);
1303 route_sink_inputs(u, NULL);
1305 route_source_outputs(u, NULL);
1311 case SUBCOMMAND_SUBSCRIBE: {
1315 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1316 !pa_tagstruct_eof(t))
1320 pa_idxset_put(u->subscribed, c, NULL);
1322 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1331 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1337 pa_tagstruct_free(reply);
1342 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1347 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1351 int pa__init(pa_module*m) {
1352 pa_modargs *ma = NULL;
1358 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1362 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1363 pa_log("Failed to parse module arguments");
1367 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1368 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1369 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1370 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1374 m->userdata = u = pa_xnew0(struct userdata, 1);
1377 u->do_routing = do_routing;
1378 u->on_hotplug = on_hotplug;
1379 u->on_rescue = on_rescue;
1380 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1382 u->protocol = pa_native_protocol_get(m->core);
1383 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1385 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);
1387 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);
1389 /* Used to handle device description management */
1390 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);
1391 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);
1393 /* The following slots are used to deal with routing */
1394 /* A little bit later than module-stream-restore, module-intended-roles */
1395 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);
1396 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);
1399 /* A little bit later than module-stream-restore, module-intended-roles */
1400 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);
1401 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);
1405 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1406 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);
1407 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);
1410 if (!(fname = pa_state_path("device-manager", TRUE)))
1413 if (!(u->database = pa_database_open(fname, TRUE))) {
1414 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1419 pa_log_info("Sucessfully opened database file '%s'.", fname);
1422 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1423 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1424 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1426 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1427 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1429 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1430 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1431 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
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);