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 "on_hotplug=<When new device becomes available, recheck streams?> "
63 "on_rescue=<When device becomes unavailable, recheck streams?>");
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 static const char* const valid_modargs
[] = {
76 pa_subscription
*subscription
;
79 *source_new_hook_slot
,
80 *sink_input_new_hook_slot
,
81 *source_output_new_hook_slot
,
83 *source_put_hook_slot
,
84 *sink_unlink_hook_slot
,
85 *source_unlink_hook_slot
,
86 *connection_unlink_hook_slot
;
87 pa_time_event
*save_time_event
;
88 pa_database
*database
;
90 pa_native_protocol
*protocol
;
91 pa_idxset
*subscribed
;
95 pa_bool_t role_device_priority_routing
;
98 #define ENTRY_VERSION 1
113 typedef uint32_t role_indexes_t
[NUM_ROLES
];
117 char description
[PA_NAME_MAX
];
118 role_indexes_t priority
;
126 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
127 SUBCOMMAND_PREFER_DEVICE
,
128 SUBCOMMAND_DEFER_DEVICE
,
129 SUBCOMMAND_SUBSCRIBE
,
133 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
134 struct userdata
*u
= userdata
;
140 pa_assert(e
== u
->save_time_event
);
141 u
->core
->mainloop
->time_free(u
->save_time_event
);
142 u
->save_time_event
= NULL
;
144 pa_database_sync(u
->database
);
145 pa_log_info("Synced.");
148 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
155 key
.data
= (char*) name
;
156 key
.size
= strlen(name
);
160 if (!pa_database_get(u
->database
, &key
, &data
))
163 if (data
.size
!= sizeof(struct entry
)) {
164 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
));
168 e
= (struct entry
*) data
.data
;
170 if (e
->version
!= ENTRY_VERSION
) {
171 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
175 if (!memchr(e
->description
, 0, sizeof(e
->description
))) {
176 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name
);
184 pa_datum_free(&data
);
188 static void trigger_save(struct userdata
*u
) {
189 pa_native_connection
*c
;
192 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
195 t
= pa_tagstruct_new(NULL
, 0);
196 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
197 pa_tagstruct_putu32(t
, 0);
198 pa_tagstruct_putu32(t
, u
->module
->index
);
199 pa_tagstruct_puts(t
, u
->module
->name
);
200 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
202 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
205 if (u
->save_time_event
)
208 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
211 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
212 if (strncmp(a
->description
, b
->description
, sizeof(a
->description
)))
218 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
226 if ((old
= read_entry(u
, name
)))
229 /* This is a new device, so make sure we write it's priority list correctly */
230 role_indexes_t max_priority
;
234 pa_zero(max_priority
);
235 done
= !pa_database_first(u
->database
, &key
, NULL
);
237 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
241 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
243 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
247 name2
= pa_xstrndup(key
.data
, key
.size
);
249 if ((e
= read_entry(u
, name2
))) {
250 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
251 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
263 /* Actually initialise our entry now we've calculated it */
264 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
265 entry
->priority
[i
] = max_priority
[i
] + 1;
272 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
273 struct userdata
*u
= userdata
;
274 struct entry entry
, *old
= NULL
;
281 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
282 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
283 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
284 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
288 entry
.version
= ENTRY_VERSION
;
290 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
293 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
296 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
298 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
300 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
305 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
307 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
310 if (source
->monitor_of
)
313 name
= pa_sprintf_malloc("source:%s", source
->name
);
315 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
317 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
322 if (entries_equal(old
, &entry
)) {
332 key
.size
= strlen(name
);
335 data
.size
= sizeof(entry
);
337 pa_log_info("Storing device %s.", name
);
339 pa_database_set(u
->database
, &key
, &data
, TRUE
);
346 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
354 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
356 if ((e
= read_entry(u
, name
))) {
357 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
358 pa_log_info("Restoring description for sink %s.", new_data
->name
);
359 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
370 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
378 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
380 if ((e
= read_entry(u
, name
))) {
381 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
382 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
383 pa_log_info("Restoring description for source %s.", new_data
->name
);
384 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
395 static char *get_name(const char *key
, const char *prefix
) {
398 if (strncmp(key
, prefix
, strlen(prefix
)))
401 t
= pa_xstrdup(key
+ strlen(prefix
));
405 static uint32_t get_role_index(const char* role
) {
408 if (strcmp(role
, "") == 0)
410 if (strcmp(role
, "video") == 0)
412 if (strcmp(role
, "music") == 0)
414 if (strcmp(role
, "game") == 0)
416 if (strcmp(role
, "event") == 0)
418 if (strcmp(role
, "phone") == 0)
420 if (strcmp(role
, "animation") == 0)
421 return ROLE_ANIMATION
;
422 if (strcmp(role
, "production") == 0)
423 return ROLE_PRODUCTION
;
424 if (strcmp(role
, "a11y") == 0)
426 return PA_INVALID_INDEX
;
429 static role_indexes_t
*get_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
) {
430 role_indexes_t
*indexes
, highest_priority_available
;
437 indexes
= pa_xnew(role_indexes_t
, 1);
438 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
439 *indexes
[i
] = PA_INVALID_INDEX
;
441 pa_zero(highest_priority_available
);
443 done
= !pa_database_first(u
->database
, &key
, NULL
);
445 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
449 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
451 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
455 name
= pa_xstrndup(key
.data
, key
.size
);
457 if ((e
= read_entry(u
, name
))) {
458 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
459 if (highest_priority_available
[i
] && e
->priority
[i
] < highest_priority_available
[i
]) {
460 /* We've found a device with a higher priority than that we've currently got,
461 so see if it is currently available or not and update our list */
463 pa_bool_t found
= FALSE
;
464 char *device_name
= get_name(name
, prefix
);
466 if (strcmp(prefix
, "sink:") == 0) {
469 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
470 if (strcmp(sink
->name
, device_name
) == 0) {
472 idx
= sink
->index
; /* Is this needed? */
479 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
480 if (strcmp(source
->name
, device_name
) == 0) {
482 idx
= source
->index
; /* Is this needed? */
488 highest_priority_available
[i
] = e
->priority
[i
];
492 pa_xfree(device_name
);
510 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
515 if (!u
->role_device_priority_routing
)
519 pa_log_debug("Not restoring device for stream, because already set.");
524 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
525 role_index
= get_role_index("");
527 role_index
= get_role_index(role
);
529 if (PA_INVALID_INDEX
!= role_index
) {
530 role_indexes_t
*indexes
;
531 uint32_t device_index
;
533 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "sink:"));
535 device_index
= *indexes
[role_index
];
536 if (PA_INVALID_INDEX
!= device_index
) {
539 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
540 new_data
->sink
= sink
;
541 new_data
->save_sink
= TRUE
;
550 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
555 if (!u
->role_device_priority_routing
)
558 if (new_data
->direct_on_input
)
561 if (new_data
->source
)
562 pa_log_debug("Not restoring device for stream, because already set");
567 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
568 role_index
= get_role_index("");
570 role_index
= get_role_index(role
);
572 if (PA_INVALID_INDEX
!= role_index
) {
573 role_indexes_t
*indexes
;
574 uint32_t device_index
;
576 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "source:"));
578 device_index
= *indexes
[role_index
];
579 if (PA_INVALID_INDEX
!= device_index
) {
582 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
))) {
583 new_data
->source
= source
;
584 new_data
->save_source
= TRUE
;
593 static pa_hook_result_t
reroute_sinks(struct userdata
*u
) {
595 role_indexes_t
*indexes
;
600 if (!u
->role_device_priority_routing
)
603 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "sink:"));
605 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
607 uint32_t role_index
, device_index
;
613 /* Skip this if it is already in the process of being moved
618 /* It might happen that a stream and a sink are set up at the
619 same time, in which case we want to make sure we don't
620 interfere with that */
621 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
624 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
625 role_index
= get_role_index("");
627 role_index
= get_role_index(role
);
629 if (PA_INVALID_INDEX
== role_index
)
632 device_index
= *indexes
[role_index
];
633 if (PA_INVALID_INDEX
== device_index
)
636 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
639 if (si
->sink
!= sink
)
640 pa_sink_input_move_to(si
, sink
, TRUE
);
648 static pa_hook_result_t
reroute_sources(struct userdata
*u
) {
649 pa_source_output
*so
;
650 role_indexes_t
*indexes
;
655 if (!u
->role_device_priority_routing
)
658 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "source:"));
660 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
662 uint32_t role_index
, device_index
;
668 if (so
->direct_on_input
)
671 /* Skip this if it is already in the process of being moved
676 /* It might happen that a stream and a source are set up at the
677 same time, in which case we want to make sure we don't
678 interfere with that */
679 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
682 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
683 role_index
= get_role_index("");
685 role_index
= get_role_index(role
);
687 if (PA_INVALID_INDEX
== role_index
)
690 device_index
= *indexes
[role_index
];
691 if (PA_INVALID_INDEX
== device_index
)
694 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
697 if (so
->source
!= source
)
698 pa_source_output_move_to(so
, source
, TRUE
);
706 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
709 pa_assert(u
->core
== c
);
710 pa_assert(u
->on_hotplug
);
712 return reroute_sinks(u
);
715 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
718 pa_assert(u
->core
== c
);
719 pa_assert(u
->on_hotplug
);
721 return reroute_sources(u
);
724 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
727 pa_assert(u
->core
== c
);
728 pa_assert(u
->on_rescue
);
730 /* There's no point in doing anything if the core is shut down anyway */
731 if (c
->state
== PA_CORE_SHUTDOWN
)
734 return reroute_sinks(u
);
737 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
740 pa_assert(u
->core
== c
);
741 pa_assert(u
->on_rescue
);
743 /* There's no point in doing anything if the core is shut down anyway */
744 if (c
->state
== PA_CORE_SHUTDOWN
)
747 return reroute_sinks(u
);
751 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
761 if ((n
= get_name(name
, "sink:"))) {
762 for (sink
= pa_idxset_first(u
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(u
->core
->sinks
, &idx
)) {
763 if (!pa_streq(sink
->name
, n
)) {
767 pa_log_info("Setting description for sink %s.", sink
->name
);
768 pa_sink_set_description(sink
, e
->description
);
772 else if ((n
= get_name(name
, "source:"))) {
773 for (source
= pa_idxset_first(u
->core
->sources
, &idx
); source
; source
= pa_idxset_next(u
->core
->sources
, &idx
)) {
774 if (!pa_streq(source
->name
, n
)) {
778 if (source
->monitor_of
) {
779 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source
->name
);
783 pa_log_info("Setting description for source %s.", source
->name
);
784 pa_source_set_description(source
, e
->description
);
791 #define EXT_VERSION 1
793 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
796 pa_tagstruct
*reply
= NULL
;
805 if (pa_tagstruct_getu32(t
, &command
) < 0)
808 reply
= pa_tagstruct_new(NULL
, 0);
809 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
810 pa_tagstruct_putu32(reply
, tag
);
813 case SUBCOMMAND_TEST
: {
814 if (!pa_tagstruct_eof(t
))
817 pa_tagstruct_putu32(reply
, EXT_VERSION
);
821 case SUBCOMMAND_READ
: {
825 if (!pa_tagstruct_eof(t
))
828 done
= !pa_database_first(u
->database
, &key
, NULL
);
835 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
837 name
= pa_xstrndup(key
.data
, key
.size
);
840 if ((e
= read_entry(u
, name
))) {
841 pa_tagstruct_puts(reply
, name
);
842 pa_tagstruct_puts(reply
, e
->description
);
855 case SUBCOMMAND_RENAME
: {
858 const char *device
, *description
;
860 if (pa_tagstruct_gets(t
, &device
) < 0 ||
861 pa_tagstruct_gets(t
, &description
) < 0)
864 if (!device
|| !*device
|| !description
|| !*description
)
867 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
870 pa_strlcpy(e
->description
, description
, sizeof(e
->description
));
872 key
.data
= (char *) device
;
873 key
.size
= strlen(device
);
876 data
.size
= sizeof(*e
);
878 if (pa_database_set(u
->database
, &key
, &data
, FALSE
) == 0) {
879 apply_entry(u
, device
, e
);
884 pa_log_warn("Could not save device");
889 pa_log_warn("Could not rename device %s, no entry in database", device
);
894 case SUBCOMMAND_DELETE
:
896 while (!pa_tagstruct_eof(t
)) {
900 if (pa_tagstruct_gets(t
, &name
) < 0)
903 key
.data
= (char*) name
;
904 key
.size
= strlen(name
);
906 /** @todo: Reindex the priorities */
907 pa_database_unset(u
->database
, &key
);
914 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
917 uint32_t sridx
= PA_INVALID_INDEX
;
921 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
924 u
->role_device_priority_routing
= enable
;
929 case SUBCOMMAND_PREFER_DEVICE
:
930 case SUBCOMMAND_DEFER_DEVICE
: {
932 const char *role
, *device
;
936 if (pa_tagstruct_gets(t
, &role
) < 0 ||
937 pa_tagstruct_gets(t
, &device
) < 0)
940 if (!role
|| !device
|| !*device
)
943 role_index
= get_role_index(role
);
944 if (PA_INVALID_INDEX
== role_index
)
947 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
952 pa_bool_t haschanged
= FALSE
;
954 if (strncmp(device
, "sink:", 5) == 0)
955 prefix
= pa_xstrdup("sink:");
957 prefix
= pa_xstrdup("source:");
959 priority
= e
->priority
[role_index
];
961 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
963 done
= !pa_database_first(u
->database
, &key
, NULL
);
965 while (!done
&& !haschanged
) {
968 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
970 /* Only read devices with the right prefix */
971 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
975 name
= pa_xstrndup(key
.data
, key
.size
);
977 if ((e2
= read_entry(u
, name
))) {
978 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
980 if (e2
->priority
[role_index
] == (priority
- 1)) {
981 e2
->priority
[role_index
]++;
986 if (e2
->priority
[role_index
] == (priority
+ 1)) {
987 e2
->priority
[role_index
]--;
994 data
.size
= sizeof(*e2
);
996 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
997 pa_log_warn("Could not save device");
1006 pa_datum_free(&key
);
1010 /* Now write out our actual entry */
1012 if (SUBCOMMAND_PREFER_DEVICE
== command
)
1013 e
->priority
[role_index
]--;
1015 e
->priority
[role_index
]++;
1017 key
.data
= (char *) device
;
1018 key
.size
= strlen(device
);
1021 data
.size
= sizeof(*e
);
1023 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
1024 pa_log_warn("Could not save device");
1034 pa_log_warn("Could not reorder device %s, no entry in database", device
);
1039 case SUBCOMMAND_SUBSCRIBE
: {
1043 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1044 !pa_tagstruct_eof(t
))
1048 pa_idxset_put(u
->subscribed
, c
, NULL
);
1050 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1059 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1065 pa_tagstruct_free(reply
);
1070 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1075 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1079 int pa__init(pa_module
*m
) {
1080 pa_modargs
*ma
= NULL
;
1086 pa_source_output
*so
;
1088 pa_bool_t on_hotplug
= TRUE
, on_rescue
= TRUE
;
1092 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1093 pa_log("Failed to parse module arguments");
1097 if (pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1098 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1099 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1103 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1106 u
->on_hotplug
= on_hotplug
;
1107 u
->on_rescue
= on_rescue
;
1108 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1110 u
->protocol
= pa_native_protocol_get(m
->core
);
1111 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1113 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
);
1115 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1117 /* Used to handle device description management */
1118 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
);
1119 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
);
1121 /* The following slots are used to deal with routing */
1122 /* A little bit later than module-stream-restore, module-intended-roles */
1123 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
);
1124 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
);
1127 /* A little bit later than module-stream-restore, module-intended-roles */
1128 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
);
1129 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
);
1133 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1134 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
);
1135 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
);
1138 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1141 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1142 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1147 pa_log_info("Sucessfully opened database file '%s'.", fname
);
1150 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1151 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1153 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1154 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1156 PA_IDXSET_FOREACH(si
, m
->core
->sink_inputs
, idx
)
1157 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_NEW
, si
->index
, u
);
1159 PA_IDXSET_FOREACH(so
, m
->core
->source_outputs
, idx
)
1160 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_NEW
, so
->index
, u
);
1162 pa_modargs_free(ma
);
1169 pa_modargs_free(ma
);
1174 void pa__done(pa_module
*m
) {
1179 if (!(u
= m
->userdata
))
1182 if (u
->subscription
)
1183 pa_subscription_free(u
->subscription
);
1185 if (u
->sink_new_hook_slot
)
1186 pa_hook_slot_free(u
->sink_new_hook_slot
);
1187 if (u
->source_new_hook_slot
)
1188 pa_hook_slot_free(u
->source_new_hook_slot
);
1190 if (u
->sink_input_new_hook_slot
)
1191 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1192 if (u
->source_output_new_hook_slot
)
1193 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1195 if (u
->sink_put_hook_slot
)
1196 pa_hook_slot_free(u
->sink_put_hook_slot
);
1197 if (u
->source_put_hook_slot
)
1198 pa_hook_slot_free(u
->source_put_hook_slot
);
1200 if (u
->sink_unlink_hook_slot
)
1201 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1202 if (u
->source_unlink_hook_slot
)
1203 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1205 if (u
->save_time_event
)
1206 u
->core
->mainloop
->time_free(u
->save_time_event
);
1209 pa_database_close(u
->database
);
1212 pa_native_protocol_remove_ext(u
->protocol
, m
);
1213 pa_native_protocol_unref(u
->protocol
);
1217 pa_idxset_free(u
->subscribed
, NULL
, NULL
);