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 char *get_name(const char *key
, const char *prefix
) {
227 if (strncmp(key
, prefix
, strlen(prefix
)))
230 t
= pa_xstrdup(key
+ strlen(prefix
));
234 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
242 if ((old
= read_entry(u
, name
)))
245 /* This is a new device, so make sure we write it's priority list correctly */
246 role_indexes_t max_priority
;
250 pa_zero(max_priority
);
251 done
= !pa_database_first(u
->database
, &key
, NULL
);
253 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
257 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
259 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
263 name2
= pa_xstrndup(key
.data
, key
.size
);
265 if ((e
= read_entry(u
, name2
))) {
266 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
267 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
279 /* Actually initialise our entry now we've calculated it */
280 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
281 entry
->priority
[i
] = max_priority
[i
] + 1;
288 static uint32_t get_role_index(const char* role
) {
291 if (strcmp(role
, "") == 0)
293 if (strcmp(role
, "video") == 0)
295 if (strcmp(role
, "music") == 0)
297 if (strcmp(role
, "game") == 0)
299 if (strcmp(role
, "event") == 0)
301 if (strcmp(role
, "phone") == 0)
303 if (strcmp(role
, "animation") == 0)
304 return ROLE_ANIMATION
;
305 if (strcmp(role
, "production") == 0)
306 return ROLE_PRODUCTION
;
307 if (strcmp(role
, "a11y") == 0)
309 return PA_INVALID_INDEX
;
312 static void update_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
, void *ignore_device
) {
313 role_indexes_t
*indexes
, highest_priority_available
;
315 pa_bool_t done
, sink_mode
;
320 sink_mode
= (strcmp(prefix
, "sink:") == 0);
323 indexes
= &u
->preferred_sinks
;
325 indexes
= &u
->preferred_sources
;
327 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
328 *indexes
[i
] = PA_INVALID_INDEX
;
330 pa_zero(highest_priority_available
);
332 done
= !pa_database_first(u
->database
, &key
, NULL
);
334 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
338 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
340 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
344 name
= pa_xstrndup(key
.data
, key
.size
);
346 if ((e
= read_entry(u
, name
))) {
347 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
348 if (highest_priority_available
[i
] && e
->priority
[i
] < highest_priority_available
[i
]) {
349 /* We've found a device with a higher priority than that we've currently got,
350 so see if it is currently available or not and update our list */
352 pa_bool_t found
= FALSE
;
353 char *device_name
= get_name(name
, prefix
);
358 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
359 if ((pa_sink
*) ignore_device
== sink
)
361 if (strcmp(sink
->name
, device_name
) == 0) {
363 idx
= sink
->index
; /* Is this needed? */
370 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
371 if ((pa_source
*) ignore_device
== source
)
373 if (strcmp(source
->name
, device_name
) == 0) {
375 idx
= source
->index
; /* Is this needed? */
381 highest_priority_available
[i
] = e
->priority
[i
];
385 pa_xfree(device_name
);
401 static void route_sink_input(struct userdata
*u
, pa_sink_input
*si
) {
403 uint32_t role_index
, device_index
;
407 pa_assert(u
->do_routing
);
412 /* Skip this if it is already in the process of being moved anyway */
416 /* It might happen that a stream and a sink are set up at the
417 same time, in which case we want to make sure we don't
418 interfere with that */
419 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
422 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
423 role_index
= get_role_index("");
425 role_index
= get_role_index(role
);
427 if (PA_INVALID_INDEX
== role_index
)
430 device_index
= u
->preferred_sinks
[role_index
];
431 if (PA_INVALID_INDEX
== device_index
)
434 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
437 if (si
->sink
!= sink
)
438 pa_sink_input_move_to(si
, sink
, TRUE
);
441 static pa_hook_result_t
route_sink_inputs(struct userdata
*u
, pa_sink
*ignore_sink
) {
450 update_highest_priority_device_indexes(u
, "sink:", ignore_sink
);
452 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
453 route_sink_input(u
, si
);
459 static void route_source_output(struct userdata
*u
, pa_source_output
*so
) {
461 uint32_t role_index
, device_index
;
465 pa_assert(u
->do_routing
);
470 if (so
->direct_on_input
)
473 /* Skip this if it is already in the process of being moved anyway */
477 /* It might happen that a stream and a source are set up at the
478 same time, in which case we want to make sure we don't
479 interfere with that */
480 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
483 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
484 role_index
= get_role_index("");
486 role_index
= get_role_index(role
);
488 if (PA_INVALID_INDEX
== role_index
)
491 device_index
= u
->preferred_sources
[role_index
];
492 if (PA_INVALID_INDEX
== device_index
)
495 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
498 if (so
->source
!= source
)
499 pa_source_output_move_to(so
, source
, TRUE
);
502 static pa_hook_result_t
route_source_outputs(struct userdata
*u
, pa_source
* ignore_source
) {
503 pa_source_output
*so
;
511 update_highest_priority_device_indexes(u
, "source:", ignore_source
);
513 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
514 route_source_output(u
, so
);
520 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
521 struct userdata
*u
= userdata
;
522 struct entry entry
, *old
= NULL
;
529 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
530 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
531 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
532 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
534 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
535 t
!= (PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
536 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
537 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
))
541 entry
.version
= ENTRY_VERSION
;
543 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK_INPUT
) {
548 if (!(si
= pa_idxset_get_by_index(c
->sink_inputs
, idx
)))
551 /* The role may change mid-stream, so we reroute */
552 route_sink_input(u
, si
);
555 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
) {
556 pa_source_output
*so
;
560 if (!(so
= pa_idxset_get_by_index(c
->source_outputs
, idx
)))
563 /* The role may change mid-stream, so we reroute */
564 route_source_output(u
, so
);
567 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
570 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
573 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
575 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
577 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
579 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
) {
582 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
584 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
587 if (source
->monitor_of
)
590 name
= pa_sprintf_malloc("source:%s", source
->name
);
592 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
594 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
601 if (entries_equal(old
, &entry
)) {
611 key
.size
= strlen(name
);
614 data
.size
= sizeof(entry
);
616 pa_log_info("Storing device %s.", name
);
618 pa_database_set(u
->database
, &key
, &data
, TRUE
);
625 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
633 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
635 if ((e
= read_entry(u
, name
))) {
636 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
637 pa_log_info("Restoring description for sink %s.", new_data
->name
);
638 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
649 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
657 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
659 if ((e
= read_entry(u
, name
))) {
660 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
661 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
662 pa_log_info("Restoring description for source %s.", new_data
->name
);
663 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
674 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
683 pa_log_debug("Not restoring device for stream, because already set.");
688 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
689 role_index
= get_role_index("");
691 role_index
= get_role_index(role
);
693 if (PA_INVALID_INDEX
!= role_index
) {
694 uint32_t device_index
;
696 device_index
= u
->preferred_sinks
[role_index
];
697 if (PA_INVALID_INDEX
!= device_index
) {
700 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
701 new_data
->sink
= sink
;
702 new_data
->save_sink
= TRUE
;
711 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
719 if (new_data
->direct_on_input
)
722 if (new_data
->source
)
723 pa_log_debug("Not restoring device for stream, because already set");
728 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
729 role_index
= get_role_index("");
731 role_index
= get_role_index(role
);
733 if (PA_INVALID_INDEX
!= role_index
) {
734 uint32_t device_index
;
736 device_index
= u
->preferred_sources
[role_index
];
737 if (PA_INVALID_INDEX
!= device_index
) {
740 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
))) {
741 new_data
->source
= source
;
742 new_data
->save_source
= TRUE
;
752 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
755 pa_assert(u
->core
== c
);
756 pa_assert(u
->on_hotplug
);
758 return route_sink_inputs(u
, NULL
);
761 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
764 pa_assert(u
->core
== c
);
765 pa_assert(u
->on_hotplug
);
767 return route_source_outputs(u
, NULL
);
770 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
774 pa_assert(u
->core
== c
);
775 pa_assert(u
->on_rescue
);
777 /* There's no point in doing anything if the core is shut down anyway */
778 if (c
->state
== PA_CORE_SHUTDOWN
)
781 return route_sink_inputs(u
, sink
);
784 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
788 pa_assert(u
->core
== c
);
789 pa_assert(u
->on_rescue
);
791 /* There's no point in doing anything if the core is shut down anyway */
792 if (c
->state
== PA_CORE_SHUTDOWN
)
795 return route_source_outputs(u
, source
);
799 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
809 if ((n
= get_name(name
, "sink:"))) {
810 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
811 if (!pa_streq(sink
->name
, n
)) {
815 pa_log_info("Setting description for sink %s.", sink
->name
);
816 pa_sink_set_description(sink
, e
->description
);
820 else if ((n
= get_name(name
, "source:"))) {
821 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
822 if (!pa_streq(source
->name
, n
)) {
826 if (source
->monitor_of
) {
827 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
831 pa_log_info("Setting description for source %s.", source
->name
);
832 pa_source_set_description(source
, e
->description
);
839 #define EXT_VERSION 1
841 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
844 pa_tagstruct
*reply
= NULL
;
853 if (pa_tagstruct_getu32(t
, &command
) < 0)
856 reply
= pa_tagstruct_new(NULL
, 0);
857 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
858 pa_tagstruct_putu32(reply
, tag
);
861 case SUBCOMMAND_TEST
: {
862 if (!pa_tagstruct_eof(t
))
865 pa_tagstruct_putu32(reply
, EXT_VERSION
);
869 case SUBCOMMAND_READ
: {
873 if (!pa_tagstruct_eof(t
))
876 done
= !pa_database_first(u
->database
, &key
, NULL
);
883 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
885 name
= pa_xstrndup(key
.data
, key
.size
);
888 if ((e
= read_entry(u
, name
))) {
889 pa_tagstruct_puts(reply
, name
);
890 pa_tagstruct_puts(reply
, e
->description
);
903 case SUBCOMMAND_RENAME
: {
906 const char *device
, *description
;
908 if (pa_tagstruct_gets(t
, &device
) < 0 ||
909 pa_tagstruct_gets(t
, &description
) < 0)
912 if (!device
|| !*device
|| !description
|| !*description
)
915 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
918 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
920 key
.data
= (char *) device
;
921 key
.size
= strlen(device
);
924 data
.size
= sizeof(*e
);
926 if (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0) {
927 apply_entry(u
, device
, e
);
932 pa_log_warn("Could not save device");
937 pa_log_warn("Could not rename device %s, no entry in database", device
);
942 case SUBCOMMAND_DELETE
:
944 while (!pa_tagstruct_eof(t
)) {
948 if (pa_tagstruct_gets(t
, &name
) < 0)
951 key
.data
= (char*) name
;
952 key
.size
= strlen(name
);
954 /** @todo: Reindex the priorities */
955 pa_database_unset(u
->database
, &key
);
962 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
966 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
969 if ((u
->do_routing
= enable
)) {
970 /* Update our caches */
971 update_highest_priority_device_indexes(u
, "sink:", NULL
);
972 update_highest_priority_device_indexes(u
, "source:", NULL
);
978 case SUBCOMMAND_PREFER_DEVICE
:
979 case SUBCOMMAND_DEFER_DEVICE
: {
981 const char *role
, *device
;
985 if (pa_tagstruct_gets(t
, &role
) < 0 ||
986 pa_tagstruct_gets(t
, &device
) < 0)
989 if (!role
|| !device
|| !*device
)
992 role_index
= get_role_index(role
);
993 if (PA_INVALID_INDEX
== role_index
)
996 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
1001 pa_bool_t haschanged
= FALSE
;
1003 if (strncmp(device
, "sink:", 5) == 0)
1004 prefix
= pa_xstrdup("sink:");
1005 else if (strncmp(device
, "source:", 7) == 0)
1006 prefix
= pa_xstrdup("source:");
1011 priority
= e
->priority
[role_index
];
1013 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
1015 done
= !pa_database_first(u
->database
, &key
, NULL
);
1017 while (!done
&& !haschanged
) {
1020 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1022 /* Only read devices with the right prefix */
1023 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
1027 name
= pa_xstrndup(key
.data
, key
.size
);
1029 if ((e2
= read_entry(u
, name
))) {
1030 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
1032 if (e2
->priority
[role_index
] == (priority
- 1)) {
1033 e2
->priority
[role_index
]++;
1038 if (e2
->priority
[role_index
] == (priority
+ 1)) {
1039 e2
->priority
[role_index
]--;
1046 data
.size
= sizeof(*e2
);
1048 if (pa_database_set(u
->database
, &key
, &data
, TRUE
))
1049 pa_log_warn("Could not save device");
1058 pa_datum_free(&key
);
1062 /* Now write out our actual entry */
1064 if (SUBCOMMAND_PREFER_DEVICE
== command
)
1065 e
->priority
[role_index
]--;
1067 e
->priority
[role_index
]++;
1069 key
.data
= (char *) device
;
1070 key
.size
= strlen(device
);
1073 data
.size
= sizeof(*e
);
1075 if (pa_database_set(u
->database
, &key
, &data
, TRUE
))
1076 pa_log_warn("Could not save device");
1086 pa_log_warn("Could not reorder device %s, no entry in database", device
);
1091 case SUBCOMMAND_SUBSCRIBE
: {
1095 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1096 !pa_tagstruct_eof(t
))
1100 pa_idxset_put(u
->subscribed
, c
, NULL
);
1102 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1111 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1117 pa_tagstruct_free(reply
);
1122 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1127 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1131 int pa__init(pa_module
*m
) {
1132 pa_modargs
*ma
= NULL
;
1138 pa_bool_t do_routing
= FALSE
, on_hotplug
= TRUE
, on_rescue
= TRUE
;
1142 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1143 pa_log("Failed to parse module arguments");
1147 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1148 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1149 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1150 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1154 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1157 u
->do_routing
= do_routing
;
1158 u
->on_hotplug
= on_hotplug
;
1159 u
->on_rescue
= on_rescue
;
1160 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1162 u
->protocol
= pa_native_protocol_get(m
->core
);
1163 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1165 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
);
1167 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
);
1169 /* Used to handle device description management */
1170 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
);
1171 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
);
1173 /* The following slots are used to deal with routing */
1174 /* A little bit later than module-stream-restore, module-intended-roles */
1175 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
);
1176 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
);
1179 /* A little bit later than module-stream-restore, module-intended-roles */
1180 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
);
1181 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
);
1185 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1186 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
);
1187 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
);
1190 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1193 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1194 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1199 pa_log_info("Sucessfully opened database file '%s'.", fname
);
1202 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1203 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1204 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1206 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1207 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1209 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1210 route_sink_inputs(u
, NULL
);
1211 route_source_outputs(u
, NULL
);
1213 pa_modargs_free(ma
);
1220 pa_modargs_free(ma
);
1225 void pa__done(pa_module
*m
) {
1230 if (!(u
= m
->userdata
))
1233 if (u
->subscription
)
1234 pa_subscription_free(u
->subscription
);
1236 if (u
->sink_new_hook_slot
)
1237 pa_hook_slot_free(u
->sink_new_hook_slot
);
1238 if (u
->source_new_hook_slot
)
1239 pa_hook_slot_free(u
->source_new_hook_slot
);
1241 if (u
->sink_input_new_hook_slot
)
1242 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1243 if (u
->source_output_new_hook_slot
)
1244 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1246 if (u
->sink_put_hook_slot
)
1247 pa_hook_slot_free(u
->sink_put_hook_slot
);
1248 if (u
->source_put_hook_slot
)
1249 pa_hook_slot_free(u
->source_put_hook_slot
);
1251 if (u
->sink_unlink_hook_slot
)
1252 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1253 if (u
->source_unlink_hook_slot
)
1254 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1256 if (u
->save_time_event
)
1257 u
->core
->mainloop
->time_free(u
->save_time_event
);
1260 pa_database_close(u
->database
);
1263 pa_native_protocol_remove_ext(u
->protocol
, m
);
1264 pa_native_protocol_unref(u
->protocol
);
1268 pa_idxset_free(u
->subscribed
, NULL
, NULL
);