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");
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)
68 static const char* const valid_modargs
[] = {
88 typedef uint32_t role_indexes_t
[NUM_ROLES
];
93 pa_subscription
*subscription
;
96 *source_new_hook_slot
,
97 *sink_input_new_hook_slot
,
98 *source_output_new_hook_slot
,
100 *source_put_hook_slot
,
101 *sink_unlink_hook_slot
,
102 *source_unlink_hook_slot
,
103 *connection_unlink_hook_slot
;
104 pa_time_event
*save_time_event
;
105 pa_database
*database
;
107 pa_native_protocol
*protocol
;
108 pa_idxset
*subscribed
;
110 pa_bool_t on_hotplug
;
112 pa_bool_t do_routing
;
114 role_indexes_t preferred_sinks
;
115 role_indexes_t preferred_sources
;
118 #define ENTRY_VERSION 1
122 char description
[PA_NAME_MAX
];
123 role_indexes_t priority
;
131 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
132 SUBCOMMAND_PREFER_DEVICE
,
133 SUBCOMMAND_DEFER_DEVICE
,
134 SUBCOMMAND_SUBSCRIBE
,
138 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
139 struct userdata
*u
= userdata
;
145 pa_assert(e
== u
->save_time_event
);
146 u
->core
->mainloop
->time_free(u
->save_time_event
);
147 u
->save_time_event
= NULL
;
149 pa_database_sync(u
->database
);
150 pa_log_info("Synced.");
153 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
160 key
.data
= (char*) name
;
161 key
.size
= strlen(name
);
165 if (!pa_database_get(u
->database
, &key
, &data
))
168 if (data
.size
!= sizeof(struct entry
)) {
169 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name
, (unsigned long) data
.size
, (unsigned long) sizeof(struct entry
));
173 e
= (struct entry
*) data
.data
;
175 if (e
->version
!= ENTRY_VERSION
) {
176 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
180 if (!memchr(e
->description
, 0, sizeof(e
->description
))) {
181 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name
);
189 pa_datum_free(&data
);
193 static void trigger_save(struct userdata
*u
) {
194 pa_native_connection
*c
;
197 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
200 t
= pa_tagstruct_new(NULL
, 0);
201 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
202 pa_tagstruct_putu32(t
, 0);
203 pa_tagstruct_putu32(t
, u
->module
->index
);
204 pa_tagstruct_puts(t
, u
->module
->name
);
205 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
207 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
210 if (u
->save_time_event
)
213 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
216 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
217 /** @todo: Compare the priority lists too */
218 if (strncmp(a
->description
, b
->description
, sizeof(a
->description
)))
224 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
232 if ((old
= read_entry(u
, name
)))
235 /* This is a new device, so make sure we write it's priority list correctly */
236 role_indexes_t max_priority
;
240 pa_zero(max_priority
);
241 done
= !pa_database_first(u
->database
, &key
, NULL
);
243 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
247 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
249 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
253 name2
= pa_xstrndup(key
.data
, key
.size
);
255 if ((e
= read_entry(u
, name2
))) {
256 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
257 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
269 /* Actually initialise our entry now we've calculated it */
270 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
271 entry
->priority
[i
] = max_priority
[i
] + 1;
278 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
279 struct userdata
*u
= userdata
;
280 struct entry entry
, *old
= NULL
;
287 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
288 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
289 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
290 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
294 entry
.version
= ENTRY_VERSION
;
296 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
299 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
302 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
304 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
306 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
311 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
313 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
316 if (source
->monitor_of
)
319 name
= pa_sprintf_malloc("source:%s", source
->name
);
321 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
323 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
328 if (entries_equal(old
, &entry
)) {
338 key
.size
= strlen(name
);
341 data
.size
= sizeof(entry
);
343 pa_log_info("Storing device %s.", name
);
345 pa_database_set(u
->database
, &key
, &data
, TRUE
);
352 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
360 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
362 if ((e
= read_entry(u
, name
))) {
363 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
364 pa_log_info("Restoring description for sink %s.", new_data
->name
);
365 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
376 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
384 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
386 if ((e
= read_entry(u
, name
))) {
387 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
388 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
389 pa_log_info("Restoring description for source %s.", new_data
->name
);
390 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
401 static char *get_name(const char *key
, const char *prefix
) {
404 if (strncmp(key
, prefix
, strlen(prefix
)))
407 t
= pa_xstrdup(key
+ strlen(prefix
));
411 static uint32_t get_role_index(const char* role
) {
414 if (strcmp(role
, "") == 0)
416 if (strcmp(role
, "video") == 0)
418 if (strcmp(role
, "music") == 0)
420 if (strcmp(role
, "game") == 0)
422 if (strcmp(role
, "event") == 0)
424 if (strcmp(role
, "phone") == 0)
426 if (strcmp(role
, "animation") == 0)
427 return ROLE_ANIMATION
;
428 if (strcmp(role
, "production") == 0)
429 return ROLE_PRODUCTION
;
430 if (strcmp(role
, "a11y") == 0)
432 return PA_INVALID_INDEX
;
435 static void update_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
, void *ignore_device
) {
436 role_indexes_t
*indexes
, highest_priority_available
;
438 pa_bool_t done
, sink_mode
;
443 sink_mode
= (strcmp(prefix
, "sink:") == 0);
446 indexes
= &u
->preferred_sinks
;
448 indexes
= &u
->preferred_sources
;
450 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
451 *indexes
[i
] = PA_INVALID_INDEX
;
453 pa_zero(highest_priority_available
);
455 done
= !pa_database_first(u
->database
, &key
, NULL
);
457 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
461 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
463 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
467 name
= pa_xstrndup(key
.data
, key
.size
);
469 if ((e
= read_entry(u
, name
))) {
470 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
471 if (highest_priority_available
[i
] && e
->priority
[i
] < highest_priority_available
[i
]) {
472 /* We've found a device with a higher priority than that we've currently got,
473 so see if it is currently available or not and update our list */
475 pa_bool_t found
= FALSE
;
476 char *device_name
= get_name(name
, prefix
);
481 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
482 if ((pa_sink
*) ignore_device
== sink
)
484 if (strcmp(sink
->name
, device_name
) == 0) {
486 idx
= sink
->index
; /* Is this needed? */
493 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
494 if ((pa_source
*) ignore_device
== source
)
496 if (strcmp(source
->name
, device_name
) == 0) {
498 idx
= source
->index
; /* Is this needed? */
504 highest_priority_available
[i
] = e
->priority
[i
];
508 pa_xfree(device_name
);
524 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
533 pa_log_debug("Not restoring device for stream, because already set.");
538 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
539 role_index
= get_role_index("");
541 role_index
= get_role_index(role
);
543 if (PA_INVALID_INDEX
!= role_index
) {
544 uint32_t device_index
;
546 device_index
= u
->preferred_sinks
[role_index
];
547 if (PA_INVALID_INDEX
!= device_index
) {
550 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
551 new_data
->sink
= sink
;
552 new_data
->save_sink
= TRUE
;
561 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
569 if (new_data
->direct_on_input
)
572 if (new_data
->source
)
573 pa_log_debug("Not restoring device for stream, because already set");
578 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
579 role_index
= get_role_index("");
581 role_index
= get_role_index(role
);
583 if (PA_INVALID_INDEX
!= role_index
) {
584 uint32_t device_index
;
586 device_index
= u
->preferred_sources
[role_index
];
587 if (PA_INVALID_INDEX
!= device_index
) {
590 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
))) {
591 new_data
->source
= source
;
592 new_data
->save_source
= TRUE
;
602 static void route_sink_input(struct userdata
*u
, pa_sink_input
*si
) {
604 uint32_t role_index
, device_index
;
608 pa_assert(u
->do_routing
);
613 /* Skip this if it is already in the process of being moved anyway */
617 /* It might happen that a stream and a sink are set up at the
618 same time, in which case we want to make sure we don't
619 interfere with that */
620 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
623 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
624 role_index
= get_role_index("");
626 role_index
= get_role_index(role
);
628 if (PA_INVALID_INDEX
== role_index
)
631 device_index
= u
->preferred_sinks
[role_index
];
632 if (PA_INVALID_INDEX
== device_index
)
635 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
638 if (si
->sink
!= sink
)
639 pa_sink_input_move_to(si
, sink
, TRUE
);
642 static pa_hook_result_t
route_sink_inputs(struct userdata
*u
, pa_sink
*ignore_sink
) {
651 update_highest_priority_device_indexes(u
, "sink:", ignore_sink
);
653 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
654 route_sink_input(u
, si
);
660 static void route_source_output(struct userdata
*u
, pa_source_output
*so
) {
662 uint32_t role_index
, device_index
;
666 pa_assert(u
->do_routing
);
671 if (so
->direct_on_input
)
674 /* Skip this if it is already in the process of being moved anyway */
678 /* It might happen that a stream and a source are set up at the
679 same time, in which case we want to make sure we don't
680 interfere with that */
681 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
684 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
685 role_index
= get_role_index("");
687 role_index
= get_role_index(role
);
689 if (PA_INVALID_INDEX
== role_index
)
692 device_index
= u
->preferred_sources
[role_index
];
693 if (PA_INVALID_INDEX
== device_index
)
696 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
699 if (so
->source
!= source
)
700 pa_source_output_move_to(so
, source
, TRUE
);
703 static pa_hook_result_t
route_source_outputs(struct userdata
*u
, pa_source
* ignore_source
) {
704 pa_source_output
*so
;
712 update_highest_priority_device_indexes(u
, "source:", ignore_source
);
714 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
715 route_source_output(u
, so
);
721 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
724 pa_assert(u
->core
== c
);
725 pa_assert(u
->on_hotplug
);
727 return route_sink_inputs(u
, NULL
);
730 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
733 pa_assert(u
->core
== c
);
734 pa_assert(u
->on_hotplug
);
736 return route_source_outputs(u
, NULL
);
739 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
743 pa_assert(u
->core
== c
);
744 pa_assert(u
->on_rescue
);
746 /* There's no point in doing anything if the core is shut down anyway */
747 if (c
->state
== PA_CORE_SHUTDOWN
)
750 return route_sink_inputs(u
, sink
);
753 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
757 pa_assert(u
->core
== c
);
758 pa_assert(u
->on_rescue
);
760 /* There's no point in doing anything if the core is shut down anyway */
761 if (c
->state
== PA_CORE_SHUTDOWN
)
764 return route_source_outputs(u
, source
);
768 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
778 if ((n
= get_name(name
, "sink:"))) {
779 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
780 if (!pa_streq(sink
->name
, n
)) {
784 pa_log_info("Setting description for sink %s.", sink
->name
);
785 pa_sink_set_description(sink
, e
->description
);
789 else if ((n
= get_name(name
, "source:"))) {
790 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
791 if (!pa_streq(source
->name
, n
)) {
795 if (source
->monitor_of
) {
796 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
800 pa_log_info("Setting description for source %s.", source
->name
);
801 pa_source_set_description(source
, e
->description
);
808 #define EXT_VERSION 1
810 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
813 pa_tagstruct
*reply
= NULL
;
822 if (pa_tagstruct_getu32(t
, &command
) < 0)
825 reply
= pa_tagstruct_new(NULL
, 0);
826 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
827 pa_tagstruct_putu32(reply
, tag
);
830 case SUBCOMMAND_TEST
: {
831 if (!pa_tagstruct_eof(t
))
834 pa_tagstruct_putu32(reply
, EXT_VERSION
);
838 case SUBCOMMAND_READ
: {
842 if (!pa_tagstruct_eof(t
))
845 done
= !pa_database_first(u
->database
, &key
, NULL
);
852 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
854 name
= pa_xstrndup(key
.data
, key
.size
);
857 if ((e
= read_entry(u
, name
))) {
858 pa_tagstruct_puts(reply
, name
);
859 pa_tagstruct_puts(reply
, e
->description
);
872 case SUBCOMMAND_RENAME
: {
875 const char *device
, *description
;
877 if (pa_tagstruct_gets(t
, &device
) < 0 ||
878 pa_tagstruct_gets(t
, &description
) < 0)
881 if (!device
|| !*device
|| !description
|| !*description
)
884 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
887 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
889 key
.data
= (char *) device
;
890 key
.size
= strlen(device
);
893 data
.size
= sizeof(*e
);
895 if (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0) {
896 apply_entry(u
, device
, e
);
901 pa_log_warn("Could not save device");
906 pa_log_warn("Could not rename device %s, no entry in database", device
);
911 case SUBCOMMAND_DELETE
:
913 while (!pa_tagstruct_eof(t
)) {
917 if (pa_tagstruct_gets(t
, &name
) < 0)
920 key
.data
= (char*) name
;
921 key
.size
= strlen(name
);
923 /** @todo: Reindex the priorities */
924 pa_database_unset(u
->database
, &key
);
931 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
935 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
938 if ((u
->do_routing
= enable
)) {
939 /* Update our caches */
940 update_highest_priority_device_indexes(u
, "sink:", NULL
);
941 update_highest_priority_device_indexes(u
, "source:", NULL
);
947 case SUBCOMMAND_PREFER_DEVICE
:
948 case SUBCOMMAND_DEFER_DEVICE
: {
950 const char *role
, *device
;
954 if (pa_tagstruct_gets(t
, &role
) < 0 ||
955 pa_tagstruct_gets(t
, &device
) < 0)
958 if (!role
|| !device
|| !*device
)
961 role_index
= get_role_index(role
);
962 if (PA_INVALID_INDEX
== role_index
)
965 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
970 pa_bool_t haschanged
= FALSE
;
972 if (strncmp(device
, "sink:", 5) == 0)
973 prefix
= pa_xstrdup("sink:");
974 else if (strncmp(device
, "source:", 7) == 0)
975 prefix
= pa_xstrdup("source:");
980 priority
= e
->priority
[role_index
];
982 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
984 done
= !pa_database_first(u
->database
, &key
, NULL
);
986 while (!done
&& !haschanged
) {
989 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
991 /* Only read devices with the right prefix */
992 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
996 name
= pa_xstrndup(key
.data
, key
.size
);
998 if ((e2
= read_entry(u
, name
))) {
999 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
1001 if (e2
->priority
[role_index
] == (priority
- 1)) {
1002 e2
->priority
[role_index
]++;
1007 if (e2
->priority
[role_index
] == (priority
+ 1)) {
1008 e2
->priority
[role_index
]--;
1015 data
.size
= sizeof(*e2
);
1017 if (pa_database_set(u
->database
, &key
, &data
, TRUE
))
1018 pa_log_warn("Could not save device");
1027 pa_datum_free(&key
);
1031 /* Now write out our actual entry */
1033 if (SUBCOMMAND_PREFER_DEVICE
== command
)
1034 e
->priority
[role_index
]--;
1036 e
->priority
[role_index
]++;
1038 key
.data
= (char *) device
;
1039 key
.size
= strlen(device
);
1042 data
.size
= sizeof(*e
);
1044 if (pa_database_set(u
->database
, &key
, &data
, TRUE
))
1045 pa_log_warn("Could not save device");
1055 pa_log_warn("Could not reorder device %s, no entry in database", device
);
1060 case SUBCOMMAND_SUBSCRIBE
: {
1064 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1065 !pa_tagstruct_eof(t
))
1069 pa_idxset_put(u
->subscribed
, c
, NULL
);
1071 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1080 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1086 pa_tagstruct_free(reply
);
1091 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1096 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1100 int pa__init(pa_module
*m
) {
1101 pa_modargs
*ma
= NULL
;
1107 pa_bool_t do_routing
= FALSE
, on_hotplug
= TRUE
, on_rescue
= TRUE
;
1111 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1112 pa_log("Failed to parse module arguments");
1116 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1117 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1118 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1119 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1123 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1126 u
->do_routing
= do_routing
;
1127 u
->on_hotplug
= on_hotplug
;
1128 u
->on_rescue
= on_rescue
;
1129 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1131 u
->protocol
= pa_native_protocol_get(m
->core
);
1132 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1134 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
);
1136 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1138 /* Used to handle device description management */
1139 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
);
1140 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
);
1142 /* The following slots are used to deal with routing */
1143 /* A little bit later than module-stream-restore, module-intended-roles */
1144 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
);
1145 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
);
1148 /* A little bit later than module-stream-restore, module-intended-roles */
1149 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
);
1150 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
);
1154 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1155 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
);
1156 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
);
1159 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1162 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1163 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1168 pa_log_info("Sucessfully opened database file '%s'.", fname
);
1171 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1172 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1173 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1175 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1176 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1178 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1179 route_sink_inputs(u
, NULL
);
1180 route_source_outputs(u
, NULL
);
1182 pa_modargs_free(ma
);
1189 pa_modargs_free(ma
);
1194 void pa__done(pa_module
*m
) {
1199 if (!(u
= m
->userdata
))
1202 if (u
->subscription
)
1203 pa_subscription_free(u
->subscription
);
1205 if (u
->sink_new_hook_slot
)
1206 pa_hook_slot_free(u
->sink_new_hook_slot
);
1207 if (u
->source_new_hook_slot
)
1208 pa_hook_slot_free(u
->source_new_hook_slot
);
1210 if (u
->sink_input_new_hook_slot
)
1211 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1212 if (u
->source_output_new_hook_slot
)
1213 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1215 if (u
->sink_put_hook_slot
)
1216 pa_hook_slot_free(u
->sink_put_hook_slot
);
1217 if (u
->source_put_hook_slot
)
1218 pa_hook_slot_free(u
->source_put_hook_slot
);
1220 if (u
->sink_unlink_hook_slot
)
1221 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1222 if (u
->source_unlink_hook_slot
)
1223 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1225 if (u
->save_time_event
)
1226 u
->core
->mainloop
->time_free(u
->save_time_event
);
1229 pa_database_close(u
->database
);
1232 pa_native_protocol_remove_ext(u
->protocol
, m
);
1233 pa_native_protocol_unref(u
->protocol
);
1237 pa_idxset_free(u
->subscribed
, NULL
, NULL
);