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
[] = {
78 pa_subscription
*subscription
;
81 *source_new_hook_slot
,
82 *sink_input_new_hook_slot
,
83 *source_output_new_hook_slot
,
85 *source_put_hook_slot
,
86 *sink_unlink_hook_slot
,
87 *source_unlink_hook_slot
,
88 *connection_unlink_hook_slot
;
89 pa_time_event
*save_time_event
;
90 pa_database
*database
;
92 pa_native_protocol
*protocol
;
93 pa_idxset
*subscribed
;
100 #define ENTRY_VERSION 1
115 typedef uint32_t role_indexes_t
[NUM_ROLES
];
119 char description
[PA_NAME_MAX
];
120 role_indexes_t priority
;
128 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
129 SUBCOMMAND_PREFER_DEVICE
,
130 SUBCOMMAND_DEFER_DEVICE
,
131 SUBCOMMAND_SUBSCRIBE
,
135 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
136 struct userdata
*u
= userdata
;
142 pa_assert(e
== u
->save_time_event
);
143 u
->core
->mainloop
->time_free(u
->save_time_event
);
144 u
->save_time_event
= NULL
;
146 pa_database_sync(u
->database
);
147 pa_log_info("Synced.");
150 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
157 key
.data
= (char*) name
;
158 key
.size
= strlen(name
);
162 if (!pa_database_get(u
->database
, &key
, &data
))
165 if (data
.size
!= sizeof(struct entry
)) {
166 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
));
170 e
= (struct entry
*) data
.data
;
172 if (e
->version
!= ENTRY_VERSION
) {
173 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
177 if (!memchr(e
->description
, 0, sizeof(e
->description
))) {
178 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name
);
186 pa_datum_free(&data
);
190 static void trigger_save(struct userdata
*u
) {
191 pa_native_connection
*c
;
194 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
197 t
= pa_tagstruct_new(NULL
, 0);
198 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
199 pa_tagstruct_putu32(t
, 0);
200 pa_tagstruct_putu32(t
, u
->module
->index
);
201 pa_tagstruct_puts(t
, u
->module
->name
);
202 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
204 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
207 if (u
->save_time_event
)
210 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
213 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
214 if (strncmp(a
->description
, b
->description
, sizeof(a
->description
)))
220 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
228 if ((old
= read_entry(u
, name
)))
231 /* This is a new device, so make sure we write it's priority list correctly */
232 role_indexes_t max_priority
;
236 pa_zero(max_priority
);
237 done
= !pa_database_first(u
->database
, &key
, NULL
);
239 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
243 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
245 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
249 name2
= pa_xstrndup(key
.data
, key
.size
);
251 if ((e
= read_entry(u
, name2
))) {
252 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
253 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
265 /* Actually initialise our entry now we've calculated it */
266 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
267 entry
->priority
[i
] = max_priority
[i
] + 1;
274 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
275 struct userdata
*u
= userdata
;
276 struct entry entry
, *old
= NULL
;
283 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
284 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
285 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
286 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
290 entry
.version
= ENTRY_VERSION
;
292 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
295 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
298 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
300 old
= load_or_initialize_entry(u
, &entry
, name
, "sink:");
302 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
307 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
309 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
312 if (source
->monitor_of
)
315 name
= pa_sprintf_malloc("source:%s", source
->name
);
317 old
= load_or_initialize_entry(u
, &entry
, name
, "source:");
319 pa_strlcpy(entry
.description
, pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), sizeof(entry
.description
));
324 if (entries_equal(old
, &entry
)) {
334 key
.size
= strlen(name
);
337 data
.size
= sizeof(entry
);
339 pa_log_info("Storing device %s.", name
);
341 pa_database_set(u
->database
, &key
, &data
, TRUE
);
348 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
356 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
358 if ((e
= read_entry(u
, name
))) {
359 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
360 pa_log_info("Restoring description for sink %s.", new_data
->name
);
361 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
372 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
380 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
382 if ((e
= read_entry(u
, name
))) {
383 if (strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
384 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
385 pa_log_info("Restoring description for source %s.", new_data
->name
);
386 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
397 static char *get_name(const char *key
, const char *prefix
) {
400 if (strncmp(key
, prefix
, strlen(prefix
)))
403 t
= pa_xstrdup(key
+ strlen(prefix
));
407 static uint32_t get_role_index(const char* role
) {
410 if (strcmp(role
, "") == 0)
412 if (strcmp(role
, "video") == 0)
414 if (strcmp(role
, "music") == 0)
416 if (strcmp(role
, "game") == 0)
418 if (strcmp(role
, "event") == 0)
420 if (strcmp(role
, "phone") == 0)
422 if (strcmp(role
, "animation") == 0)
423 return ROLE_ANIMATION
;
424 if (strcmp(role
, "production") == 0)
425 return ROLE_PRODUCTION
;
426 if (strcmp(role
, "a11y") == 0)
428 return PA_INVALID_INDEX
;
431 static role_indexes_t
*get_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
) {
432 role_indexes_t
*indexes
, highest_priority_available
;
439 indexes
= pa_xnew(role_indexes_t
, 1);
440 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
441 *indexes
[i
] = PA_INVALID_INDEX
;
443 pa_zero(highest_priority_available
);
445 done
= !pa_database_first(u
->database
, &key
, NULL
);
447 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
451 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
453 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
457 name
= pa_xstrndup(key
.data
, key
.size
);
459 if ((e
= read_entry(u
, name
))) {
460 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
461 if (highest_priority_available
[i
] && e
->priority
[i
] < highest_priority_available
[i
]) {
462 /* We've found a device with a higher priority than that we've currently got,
463 so see if it is currently available or not and update our list */
465 pa_bool_t found
= FALSE
;
466 char *device_name
= get_name(name
, prefix
);
468 if (strcmp(prefix
, "sink:") == 0) {
471 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
472 if (strcmp(sink
->name
, device_name
) == 0) {
474 idx
= sink
->index
; /* Is this needed? */
481 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
482 if (strcmp(source
->name
, device_name
) == 0) {
484 idx
= source
->index
; /* Is this needed? */
490 highest_priority_available
[i
] = e
->priority
[i
];
494 pa_xfree(device_name
);
512 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
521 pa_log_debug("Not restoring device for stream, because already set.");
526 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
527 role_index
= get_role_index("");
529 role_index
= get_role_index(role
);
531 if (PA_INVALID_INDEX
!= role_index
) {
532 role_indexes_t
*indexes
;
533 uint32_t device_index
;
535 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "sink:"));
537 device_index
= *indexes
[role_index
];
538 if (PA_INVALID_INDEX
!= device_index
) {
541 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
542 new_data
->sink
= sink
;
543 new_data
->save_sink
= TRUE
;
552 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
560 if (new_data
->direct_on_input
)
563 if (new_data
->source
)
564 pa_log_debug("Not restoring device for stream, because already set");
569 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
570 role_index
= get_role_index("");
572 role_index
= get_role_index(role
);
574 if (PA_INVALID_INDEX
!= role_index
) {
575 role_indexes_t
*indexes
;
576 uint32_t device_index
;
578 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "source:"));
580 device_index
= *indexes
[role_index
];
581 if (PA_INVALID_INDEX
!= device_index
) {
584 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
))) {
585 new_data
->source
= source
;
586 new_data
->save_source
= TRUE
;
595 static pa_hook_result_t
reroute_sinks(struct userdata
*u
) {
597 role_indexes_t
*indexes
;
605 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "sink:"));
607 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
609 uint32_t role_index
, device_index
;
615 /* Skip this if it is already in the process of being moved anyway */
619 /* It might happen that a stream and a sink are set up at the
620 same time, in which case we want to make sure we don't
621 interfere with that */
622 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
625 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
626 role_index
= get_role_index("");
628 role_index
= get_role_index(role
);
630 if (PA_INVALID_INDEX
== role_index
)
633 device_index
= *indexes
[role_index
];
634 if (PA_INVALID_INDEX
== device_index
)
637 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
640 if (si
->sink
!= sink
)
641 pa_sink_input_move_to(si
, sink
, TRUE
);
649 static pa_hook_result_t
reroute_sources(struct userdata
*u
) {
650 pa_source_output
*so
;
651 role_indexes_t
*indexes
;
659 pa_assert_se(indexes
= get_highest_priority_device_indexes(u
, "source:"));
661 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
663 uint32_t role_index
, device_index
;
669 if (so
->direct_on_input
)
672 /* Skip this if it is already in the process of being moved anyway */
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
: {
918 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
921 u
->do_routing
= enable
;
926 case SUBCOMMAND_PREFER_DEVICE
:
927 case SUBCOMMAND_DEFER_DEVICE
: {
929 const char *role
, *device
;
933 if (pa_tagstruct_gets(t
, &role
) < 0 ||
934 pa_tagstruct_gets(t
, &device
) < 0)
937 if (!role
|| !device
|| !*device
)
940 role_index
= get_role_index(role
);
941 if (PA_INVALID_INDEX
== role_index
)
944 if ((e
= read_entry(u
, device
)) && ENTRY_VERSION
== e
->version
) {
949 pa_bool_t haschanged
= FALSE
;
951 if (strncmp(device
, "sink:", 5) == 0)
952 prefix
= pa_xstrdup("sink:");
954 prefix
= pa_xstrdup("source:");
956 priority
= e
->priority
[role_index
];
958 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
960 done
= !pa_database_first(u
->database
, &key
, NULL
);
962 while (!done
&& !haschanged
) {
965 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
967 /* Only read devices with the right prefix */
968 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
972 name
= pa_xstrndup(key
.data
, key
.size
);
974 if ((e2
= read_entry(u
, name
))) {
975 if (SUBCOMMAND_PREFER_DEVICE
== command
) {
977 if (e2
->priority
[role_index
] == (priority
- 1)) {
978 e2
->priority
[role_index
]++;
983 if (e2
->priority
[role_index
] == (priority
+ 1)) {
984 e2
->priority
[role_index
]--;
991 data
.size
= sizeof(*e2
);
993 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
994 pa_log_warn("Could not save device");
1003 pa_datum_free(&key
);
1007 /* Now write out our actual entry */
1009 if (SUBCOMMAND_PREFER_DEVICE
== command
)
1010 e
->priority
[role_index
]--;
1012 e
->priority
[role_index
]++;
1014 key
.data
= (char *) device
;
1015 key
.size
= strlen(device
);
1018 data
.size
= sizeof(*e
);
1020 if (pa_database_set(u
->database
, &key
, &data
, FALSE
))
1021 pa_log_warn("Could not save device");
1031 pa_log_warn("Could not reorder device %s, no entry in database", device
);
1036 case SUBCOMMAND_SUBSCRIBE
: {
1040 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1041 !pa_tagstruct_eof(t
))
1045 pa_idxset_put(u
->subscribed
, c
, NULL
);
1047 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1056 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1062 pa_tagstruct_free(reply
);
1067 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1072 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1076 int pa__init(pa_module
*m
) {
1077 pa_modargs
*ma
= NULL
;
1083 pa_source_output
*so
;
1085 pa_bool_t do_routing
= FALSE
, on_hotplug
= TRUE
, on_rescue
= TRUE
;
1089 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1090 pa_log("Failed to parse module arguments");
1094 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1095 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1096 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1097 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1101 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1104 u
->do_routing
= do_routing
;
1105 u
->on_hotplug
= on_hotplug
;
1106 u
->on_rescue
= on_rescue
;
1107 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1109 u
->protocol
= pa_native_protocol_get(m
->core
);
1110 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1112 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
);
1114 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1116 /* Used to handle device description management */
1117 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
);
1118 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
);
1120 /* The following slots are used to deal with routing */
1121 /* A little bit later than module-stream-restore, module-intended-roles */
1122 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
);
1123 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
);
1126 /* A little bit later than module-stream-restore, module-intended-roles */
1127 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
);
1128 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
);
1132 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1133 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
);
1134 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
);
1137 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1140 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1141 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1146 pa_log_info("Sucessfully opened database file '%s'.", fname
);
1149 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1150 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1152 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1153 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1155 PA_IDXSET_FOREACH(si
, m
->core
->sink_inputs
, idx
)
1156 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_NEW
, si
->index
, u
);
1158 PA_IDXSET_FOREACH(so
, m
->core
->source_outputs
, idx
)
1159 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_NEW
, so
->index
, u
);
1161 pa_modargs_free(ma
);
1168 pa_modargs_free(ma
);
1173 void pa__done(pa_module
*m
) {
1178 if (!(u
= m
->userdata
))
1181 if (u
->subscription
)
1182 pa_subscription_free(u
->subscription
);
1184 if (u
->sink_new_hook_slot
)
1185 pa_hook_slot_free(u
->sink_new_hook_slot
);
1186 if (u
->source_new_hook_slot
)
1187 pa_hook_slot_free(u
->source_new_hook_slot
);
1189 if (u
->sink_input_new_hook_slot
)
1190 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1191 if (u
->source_output_new_hook_slot
)
1192 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1194 if (u
->sink_put_hook_slot
)
1195 pa_hook_slot_free(u
->sink_put_hook_slot
);
1196 if (u
->source_put_hook_slot
)
1197 pa_hook_slot_free(u
->source_put_hook_slot
);
1199 if (u
->sink_unlink_hook_slot
)
1200 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1201 if (u
->source_unlink_hook_slot
)
1202 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1204 if (u
->save_time_event
)
1205 u
->core
->mainloop
->time_free(u
->save_time_event
);
1208 pa_database_close(u
->database
);
1211 pa_native_protocol_remove_ext(u
->protocol
, m
);
1212 pa_native_protocol_unref(u
->protocol
);
1216 pa_idxset_free(u
->subscribed
, NULL
, NULL
);