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>
34 #include <pulse/gccmacro.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/timeval.h>
37 #include <pulse/rtclock.h>
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/modargs.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/core-subscribe.h>
45 #include <pulsecore/sink-input.h>
46 #include <pulsecore/source-output.h>
47 #include <pulsecore/namereg.h>
48 #include <pulsecore/protocol-native.h>
49 #include <pulsecore/pstream.h>
50 #include <pulsecore/pstream-util.h>
51 #include <pulsecore/database.h>
52 #include <pulsecore/tagstruct.h>
54 #include "module-device-manager-symdef.h"
56 PA_MODULE_AUTHOR("Colin Guthrie");
57 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
58 PA_MODULE_VERSION(PACKAGE_VERSION
);
59 PA_MODULE_LOAD_ONCE(true);
61 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
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)
68 static const char* const valid_modargs
[] = {
89 typedef uint32_t role_indexes_t
[NUM_ROLES
];
91 static const char* role_names
[NUM_ROLES
] = {
106 pa_subscription
*subscription
;
109 *source_new_hook_slot
,
110 *sink_input_new_hook_slot
,
111 *source_output_new_hook_slot
,
113 *source_put_hook_slot
,
114 *sink_unlink_hook_slot
,
115 *source_unlink_hook_slot
,
116 *connection_unlink_hook_slot
;
117 pa_time_event
*save_time_event
;
118 pa_database
*database
;
120 pa_native_protocol
*protocol
;
121 pa_idxset
*subscribed
;
127 role_indexes_t preferred_sinks
;
128 role_indexes_t preferred_sources
;
131 #define ENTRY_VERSION 1
136 bool user_set_description
;
138 role_indexes_t priority
;
146 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
148 SUBCOMMAND_SUBSCRIBE
,
152 /* Forward declarations */
154 static void dump_database(struct userdata
*);
156 static void notify_subscribers(struct userdata
*);
158 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
159 struct userdata
*u
= userdata
;
165 pa_assert(e
== u
->save_time_event
);
166 u
->core
->mainloop
->time_free(u
->save_time_event
);
167 u
->save_time_event
= NULL
;
169 pa_database_sync(u
->database
);
170 pa_log_info("Synced.");
177 static void trigger_save(struct userdata
*u
) {
181 notify_subscribers(u
);
183 if (u
->save_time_event
)
186 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
189 static struct entry
* entry_new(void) {
190 struct entry
*r
= pa_xnew0(struct entry
, 1);
191 r
->version
= ENTRY_VERSION
;
195 static void entry_free(struct entry
* e
) {
198 pa_xfree(e
->description
);
203 static bool entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
212 t
= pa_tagstruct_new(NULL
, 0);
213 pa_tagstruct_putu8(t
, e
->version
);
214 pa_tagstruct_puts(t
, e
->description
);
215 pa_tagstruct_put_boolean(t
, e
->user_set_description
);
216 pa_tagstruct_puts(t
, e
->icon
);
217 for (uint8_t i
=0; i
<ROLE_MAX
; ++i
)
218 pa_tagstruct_putu32(t
, e
->priority
[i
]);
220 key
.data
= (char *) name
;
221 key
.size
= strlen(name
);
223 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
225 r
= (pa_database_set(u
->database
, &key
, &data
, true) == 0);
227 pa_tagstruct_free(t
);
232 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
234 #define LEGACY_ENTRY_VERSION 1
235 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
236 struct legacy_entry
{
238 char description
[PA_NAME_MAX
];
239 bool user_set_description
;
240 char icon
[PA_NAME_MAX
];
241 role_indexes_t priority
;
243 struct legacy_entry
*le
;
249 if (data
->size
!= sizeof(struct legacy_entry
)) {
250 pa_log_debug("Size does not match.");
254 le
= (struct legacy_entry
*)data
->data
;
256 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
257 pa_log_debug("Version mismatch.");
261 if (!memchr(le
->description
, 0, sizeof(le
->description
))) {
262 pa_log_warn("Description has missing NUL byte.");
266 if (!le
->description
[0]) {
267 pa_log_warn("Description is empty.");
271 if (!memchr(le
->icon
, 0, sizeof(le
->icon
))) {
272 pa_log_warn("Icon has missing NUL byte.");
277 e
->description
= pa_xstrdup(le
->description
);
278 e
->icon
= pa_xstrdup(le
->icon
);
283 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
285 struct entry
*e
= NULL
;
286 pa_tagstruct
*t
= NULL
;
287 const char *description
, *icon
;
292 key
.data
= (char*) name
;
293 key
.size
= strlen(name
);
297 if (!pa_database_get(u
->database
, &key
, &data
))
300 t
= pa_tagstruct_new(data
.data
, data
.size
);
303 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
304 e
->version
> ENTRY_VERSION
||
305 pa_tagstruct_gets(t
, &description
) < 0 ||
306 pa_tagstruct_get_boolean(t
, &e
->user_set_description
) < 0 ||
307 pa_tagstruct_gets(t
, &icon
) < 0) {
312 if (e
->user_set_description
&& !description
) {
313 pa_log("Entry has user_set_description set, but the description is NULL.");
317 if (e
->user_set_description
&& !*description
) {
318 pa_log("Entry has user_set_description set, but the description is empty.");
322 e
->description
= pa_xstrdup(description
);
323 e
->icon
= pa_xstrdup(icon
);
325 for (uint8_t i
=0; i
<ROLE_MAX
; ++i
) {
326 if (pa_tagstruct_getu32(t
, &e
->priority
[i
]) < 0)
330 if (!pa_tagstruct_eof(t
))
333 pa_tagstruct_free(t
);
334 pa_datum_free(&data
);
339 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
344 pa_tagstruct_free(t
);
346 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
347 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
348 if ((e
= legacy_entry_read(u
, &data
))) {
349 pa_log_debug("Success. Saving new format for key: %s", name
);
350 if (entry_write(u
, name
, e
))
352 pa_datum_free(&data
);
355 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
358 pa_datum_free(&data
);
363 static void dump_database_helper(struct userdata
*u
, uint32_t role_index
, const char* human
, bool sink_mode
) {
369 if (PA_INVALID_INDEX
!= u
->preferred_sinks
[role_index
] && (s
= pa_idxset_get_by_index(u
->core
->sinks
, u
->preferred_sinks
[role_index
])))
370 pa_log_debug(" %s %s (%s)", human
, pa_strnull(pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), s
->name
);
372 pa_log_debug(" %s No sink specified", human
);
375 if (PA_INVALID_INDEX
!= u
->preferred_sources
[role_index
] && (s
= pa_idxset_get_by_index(u
->core
->sources
, u
->preferred_sources
[role_index
])))
376 pa_log_debug(" %s %s (%s)", human
, pa_strnull(pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), s
->name
);
378 pa_log_debug(" %s No source specified", human
);
382 static void dump_database(struct userdata
*u
) {
388 done
= !pa_database_first(u
->database
, &key
, NULL
);
390 pa_log_debug("Dumping database");
396 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
398 name
= pa_xstrndup(key
.data
, key
.size
);
400 if ((e
= entry_read(u
, name
))) {
401 pa_log_debug(" Got entry: %s", name
);
402 pa_log_debug(" Description: %s", e
->description
);
403 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
404 e
->priority
[ROLE_NONE
], e
->priority
[ROLE_VIDEO
], e
->priority
[ROLE_MUSIC
], e
->priority
[ROLE_GAME
], e
->priority
[ROLE_EVENT
]);
405 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
406 e
->priority
[ROLE_PHONE
], e
->priority
[ROLE_ANIMATION
], e
->priority
[ROLE_PRODUCTION
], e
->priority
[ROLE_A11Y
]);
417 pa_log_debug(" Highest priority devices per-role:");
419 pa_log_debug(" Sinks:");
420 for (uint32_t role
= ROLE_NONE
; role
< NUM_ROLES
; ++role
) {
422 uint32_t len
= PA_MIN(12u, strlen(role_names
[role
]));
423 strncpy(name
, role_names
[role
], len
);
424 for (int i
= len
+1; i
< 12; ++i
) name
[i
] = ' ';
425 name
[len
] = ':'; name
[0] -= 32; name
[12] = '\0';
426 dump_database_helper(u
, role
, name
, true);
429 pa_log_debug(" Sources:");
430 for (uint32_t role
= ROLE_NONE
; role
< NUM_ROLES
; ++role
) {
432 uint32_t len
= PA_MIN(12u, strlen(role_names
[role
]));
433 strncpy(name
, role_names
[role
], len
);
434 for (int i
= len
+1; i
< 12; ++i
) name
[i
] = ' ';
435 name
[len
] = ':'; name
[0] -= 32; name
[12] = '\0';
436 dump_database_helper(u
, role
, name
, false);
440 pa_log_debug("Completed database dump");
444 static void notify_subscribers(struct userdata
*u
) {
446 pa_native_connection
*c
;
451 PA_IDXSET_FOREACH(c
, u
->subscribed
, idx
) {
454 t
= pa_tagstruct_new(NULL
, 0);
455 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
456 pa_tagstruct_putu32(t
, 0);
457 pa_tagstruct_putu32(t
, u
->module
->index
);
458 pa_tagstruct_puts(t
, u
->module
->name
);
459 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
461 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
465 static bool entries_equal(const struct entry
*a
, const struct entry
*b
) {
470 if (!pa_streq(a
->description
, b
->description
)
471 || a
->user_set_description
!= b
->user_set_description
472 || !pa_streq(a
->icon
, b
->icon
))
475 for (int i
=0; i
< NUM_ROLES
; ++i
)
476 if (a
->priority
[i
] != b
->priority
[i
])
482 static char *get_name(const char *key
, const char *prefix
) {
485 if (strncmp(key
, prefix
, strlen(prefix
)))
488 t
= pa_xstrdup(key
+ strlen(prefix
));
492 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
500 if ((old
= entry_read(u
, name
))) {
502 entry
->description
= pa_xstrdup(old
->description
);
503 entry
->icon
= pa_xstrdup(old
->icon
);
505 /* This is a new device, so make sure we write its priority list correctly */
506 role_indexes_t max_priority
;
510 pa_zero(max_priority
);
511 done
= !pa_database_first(u
->database
, &key
, NULL
);
513 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
517 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
519 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
523 name2
= pa_xstrndup(key
.data
, key
.size
);
525 if ((e
= entry_read(u
, name2
))) {
526 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
527 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
539 /* Actually initialise our entry now we've calculated it */
540 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
541 entry
->priority
[i
] = max_priority
[i
] + 1;
543 entry
->user_set_description
= false;
549 static uint32_t get_role_index(const char* role
) {
552 for (uint32_t i
= ROLE_NONE
; i
< NUM_ROLES
; ++i
)
553 if (pa_streq(role
, role_names
[i
]))
556 return PA_INVALID_INDEX
;
559 static void update_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
, void *ignore_device
) {
560 role_indexes_t
*indexes
, highest_priority_available
;
562 bool done
, sink_mode
;
567 sink_mode
= pa_streq(prefix
, "sink:");
570 indexes
= &u
->preferred_sinks
;
572 indexes
= &u
->preferred_sources
;
574 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
575 (*indexes
)[i
] = PA_INVALID_INDEX
;
577 pa_zero(highest_priority_available
);
579 done
= !pa_database_first(u
->database
, &key
, NULL
);
581 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
585 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
587 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
588 char *name
, *device_name
;
591 name
= pa_xstrndup(key
.data
, key
.size
);
592 pa_assert_se(device_name
= get_name(name
, prefix
));
594 if ((e
= entry_read(u
, name
))) {
595 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
596 if (!highest_priority_available
[i
] || e
->priority
[i
] < highest_priority_available
[i
]) {
597 /* We've found a device with a higher priority than that we've currently got,
598 so see if it is currently available or not and update our list */
605 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
606 if ((pa_sink
*) ignore_device
== sink
)
608 if (!PA_SINK_IS_LINKED(sink
->state
))
610 if (pa_streq(sink
->name
, device_name
)) {
612 idx
= sink
->index
; /* Is this needed? */
619 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
620 if ((pa_source
*) ignore_device
== source
)
622 if (!PA_SOURCE_IS_LINKED(source
->state
))
624 if (pa_streq(source
->name
, device_name
)) {
626 idx
= source
->index
; /* Is this needed? */
632 highest_priority_available
[i
] = e
->priority
[i
];
643 pa_xfree(device_name
);
651 static void route_sink_input(struct userdata
*u
, pa_sink_input
*si
) {
653 uint32_t role_index
, device_index
;
657 pa_assert(u
->do_routing
);
662 /* Skip this if it is already in the process of being moved anyway */
666 /* It might happen that a stream and a sink are set up at the
667 same time, in which case we want to make sure we don't
668 interfere with that */
669 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
672 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
673 role_index
= get_role_index("none");
675 role_index
= get_role_index(role
);
677 if (PA_INVALID_INDEX
== role_index
)
680 device_index
= u
->preferred_sinks
[role_index
];
681 if (PA_INVALID_INDEX
== device_index
)
684 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
687 if (si
->sink
!= sink
)
688 pa_sink_input_move_to(si
, sink
, false);
691 static pa_hook_result_t
route_sink_inputs(struct userdata
*u
, pa_sink
*ignore_sink
) {
700 update_highest_priority_device_indexes(u
, "sink:", ignore_sink
);
702 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
703 route_sink_input(u
, si
);
709 static void route_source_output(struct userdata
*u
, pa_source_output
*so
) {
711 uint32_t role_index
, device_index
;
715 pa_assert(u
->do_routing
);
720 if (so
->direct_on_input
)
723 /* Skip this if it is already in the process of being moved anyway */
727 /* It might happen that a stream and a source are set up at the
728 same time, in which case we want to make sure we don't
729 interfere with that */
730 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
733 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
734 role_index
= get_role_index("none");
736 role_index
= get_role_index(role
);
738 if (PA_INVALID_INDEX
== role_index
)
741 device_index
= u
->preferred_sources
[role_index
];
742 if (PA_INVALID_INDEX
== device_index
)
745 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
748 if (so
->source
!= source
)
749 pa_source_output_move_to(so
, source
, false);
752 static pa_hook_result_t
route_source_outputs(struct userdata
*u
, pa_source
* ignore_source
) {
753 pa_source_output
*so
;
761 update_highest_priority_device_indexes(u
, "source:", ignore_source
);
763 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
764 route_source_output(u
, so
);
770 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
771 struct userdata
*u
= userdata
;
772 struct entry
*entry
, *old
= NULL
;
778 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
779 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
780 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
781 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
783 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
784 t
!= (PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
785 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
786 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
))
789 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK_INPUT
) {
794 if (!(si
= pa_idxset_get_by_index(c
->sink_inputs
, idx
)))
797 /* The role may change mid-stream, so we reroute */
798 route_sink_input(u
, si
);
801 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
) {
802 pa_source_output
*so
;
806 if (!(so
= pa_idxset_get_by_index(c
->source_outputs
, idx
)))
809 /* The role may change mid-stream, so we reroute */
810 route_source_output(u
, so
);
813 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
816 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
820 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
822 old
= load_or_initialize_entry(u
, entry
, name
, "sink:");
824 if (!entry
->user_set_description
) {
825 pa_xfree(entry
->description
);
826 entry
->description
= pa_xstrdup(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
));
827 } else if (!pa_streq(entry
->description
, pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
828 /* Warning: If two modules fight over the description, this could cause an infinite loop.
829 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
830 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
831 the description, this will fail... */
832 pa_sink_set_description(sink
, entry
->description
);
835 pa_xfree(entry
->icon
);
836 entry
->icon
= pa_xstrdup(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_ICON_NAME
));
838 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
) {
841 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
843 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
846 if (source
->monitor_of
)
850 name
= pa_sprintf_malloc("source:%s", source
->name
);
852 old
= load_or_initialize_entry(u
, entry
, name
, "source:");
854 if (!entry
->user_set_description
) {
855 pa_xfree(entry
->description
);
856 entry
->description
= pa_xstrdup(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
));
857 } else if (!pa_streq(entry
->description
, pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
858 /* Warning: If two modules fight over the description, this could cause an infinite loop.
859 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
860 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
861 the description, this will fail... */
862 pa_source_set_description(source
, entry
->description
);
865 pa_xfree(entry
->icon
);
866 entry
->icon
= pa_xstrdup(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_ICON_NAME
));
868 pa_assert_not_reached();
875 if (entries_equal(old
, entry
)) {
886 pa_log_info("Storing device %s.", name
);
888 if (entry_write(u
, name
, entry
))
891 pa_log_warn("Could not save device");;
897 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
905 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
907 if ((e
= entry_read(u
, name
))) {
908 if (e
->user_set_description
&& !pa_safe_streq(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
909 pa_log_info("Restoring description for sink %s.", new_data
->name
);
910 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
921 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
929 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
931 if ((e
= entry_read(u
, name
))) {
932 if (e
->user_set_description
&& !pa_safe_streq(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
933 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
934 pa_log_info("Restoring description for source %s.", new_data
->name
);
935 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
946 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
958 pa_log_debug("Not restoring device for stream because already set.");
960 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
961 role_index
= get_role_index("none");
963 role_index
= get_role_index(role
);
965 if (PA_INVALID_INDEX
!= role_index
) {
966 uint32_t device_index
;
968 device_index
= u
->preferred_sinks
[role_index
];
969 if (PA_INVALID_INDEX
!= device_index
) {
972 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
973 if (!pa_sink_input_new_data_set_sink(new_data
, sink
, false))
974 pa_log_debug("Not restoring device for stream because no supported format was found");
983 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
994 if (new_data
->direct_on_input
)
997 if (new_data
->source
)
998 pa_log_debug("Not restoring device for stream because already set.");
1000 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
1001 role_index
= get_role_index("none");
1003 role_index
= get_role_index(role
);
1005 if (PA_INVALID_INDEX
!= role_index
) {
1006 uint32_t device_index
;
1008 device_index
= u
->preferred_sources
[role_index
];
1009 if (PA_INVALID_INDEX
!= device_index
) {
1012 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
1013 if (!pa_source_output_new_data_set_source(new_data
, source
, false))
1014 pa_log_debug("Not restoring device for stream because no supported format was found");
1022 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
1025 pa_assert(u
->core
== c
);
1026 pa_assert(u
->on_hotplug
);
1028 notify_subscribers(u
);
1030 return route_sink_inputs(u
, NULL
);
1033 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
1036 pa_assert(u
->core
== c
);
1037 pa_assert(u
->on_hotplug
);
1039 notify_subscribers(u
);
1041 return route_source_outputs(u
, NULL
);
1044 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
1048 pa_assert(u
->core
== c
);
1049 pa_assert(u
->on_rescue
);
1051 /* There's no point in doing anything if the core is shut down anyway */
1052 if (c
->state
== PA_CORE_SHUTDOWN
)
1055 notify_subscribers(u
);
1057 return route_sink_inputs(u
, sink
);
1060 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
1064 pa_assert(u
->core
== c
);
1065 pa_assert(u
->on_rescue
);
1067 /* There's no point in doing anything if the core is shut down anyway */
1068 if (c
->state
== PA_CORE_SHUTDOWN
)
1071 notify_subscribers(u
);
1073 return route_source_outputs(u
, source
);
1076 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
1084 if (!e
->user_set_description
)
1087 if ((n
= get_name(name
, "sink:"))) {
1089 PA_IDXSET_FOREACH(s
, u
->core
->sinks
, idx
) {
1090 if (!pa_streq(s
->name
, n
)) {
1094 pa_log_info("Setting description for sink %s to '%s'", s
->name
, e
->description
);
1095 pa_sink_set_description(s
, e
->description
);
1099 else if ((n
= get_name(name
, "source:"))) {
1101 PA_IDXSET_FOREACH(s
, u
->core
->sources
, idx
) {
1102 if (!pa_streq(s
->name
, n
)) {
1106 if (s
->monitor_of
) {
1107 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s
->name
);
1111 pa_log_info("Setting description for source %s to '%s'", s
->name
, e
->description
);
1112 pa_source_set_description(s
, e
->description
);
1118 #define EXT_VERSION 1
1120 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
1123 pa_tagstruct
*reply
= NULL
;
1132 if (pa_tagstruct_getu32(t
, &command
) < 0)
1135 reply
= pa_tagstruct_new(NULL
, 0);
1136 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
1137 pa_tagstruct_putu32(reply
, tag
);
1140 case SUBCOMMAND_TEST
: {
1141 if (!pa_tagstruct_eof(t
))
1144 pa_tagstruct_putu32(reply
, EXT_VERSION
);
1148 case SUBCOMMAND_READ
: {
1152 if (!pa_tagstruct_eof(t
))
1155 done
= !pa_database_first(u
->database
, &key
, NULL
);
1162 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1164 name
= pa_xstrndup(key
.data
, key
.size
);
1165 pa_datum_free(&key
);
1167 if ((e
= entry_read(u
, name
))) {
1170 uint32_t found_index
= PA_INVALID_INDEX
;
1172 if ((device_name
= get_name(name
, "sink:"))) {
1174 PA_IDXSET_FOREACH(s
, u
->core
->sinks
, idx
) {
1175 if (pa_streq(s
->name
, device_name
)) {
1176 found_index
= s
->index
;
1180 pa_xfree(device_name
);
1181 } else if ((device_name
= get_name(name
, "source:"))) {
1183 PA_IDXSET_FOREACH(s
, u
->core
->sources
, idx
) {
1184 if (pa_streq(s
->name
, device_name
)) {
1185 found_index
= s
->index
;
1189 pa_xfree(device_name
);
1192 pa_tagstruct_puts(reply
, name
);
1193 pa_tagstruct_puts(reply
, e
->description
);
1194 pa_tagstruct_puts(reply
, e
->icon
);
1195 pa_tagstruct_putu32(reply
, found_index
);
1196 pa_tagstruct_putu32(reply
, NUM_ROLES
);
1198 for (uint32_t i
= ROLE_NONE
; i
< NUM_ROLES
; ++i
) {
1199 pa_tagstruct_puts(reply
, role_names
[i
]);
1200 pa_tagstruct_putu32(reply
, e
->priority
[i
]);
1214 case SUBCOMMAND_RENAME
: {
1217 const char *device
, *description
;
1219 if (pa_tagstruct_gets(t
, &device
) < 0 ||
1220 pa_tagstruct_gets(t
, &description
) < 0)
1223 if (!device
|| !*device
|| !description
|| !*description
)
1226 if ((e
= entry_read(u
, device
))) {
1227 pa_xfree(e
->description
);
1228 e
->description
= pa_xstrdup(description
);
1229 e
->user_set_description
= true;
1231 if (entry_write(u
, (char *)device
, e
)) {
1232 apply_entry(u
, device
, e
);
1237 pa_log_warn("Could not save device");
1242 pa_log_warn("Could not rename device %s, no entry in database", device
);
1247 case SUBCOMMAND_DELETE
:
1249 while (!pa_tagstruct_eof(t
)) {
1253 if (pa_tagstruct_gets(t
, &name
) < 0)
1256 key
.data
= (char*) name
;
1257 key
.size
= strlen(name
);
1259 /** @todo: Reindex the priorities */
1260 pa_database_unset(u
->database
, &key
);
1267 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
1271 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
1274 if ((u
->do_routing
= enable
)) {
1275 /* Update our caches */
1276 update_highest_priority_device_indexes(u
, "sink:", NULL
);
1277 update_highest_priority_device_indexes(u
, "source:", NULL
);
1283 case SUBCOMMAND_REORDER
: {
1287 uint32_t role_index
, n_devices
;
1289 bool done
, sink_mode
= true;
1290 struct device_t
{ uint32_t prio
; char *device
; };
1291 struct device_t
*device
;
1292 struct device_t
**devices
;
1293 uint32_t i
, idx
, offset
;
1298 if (pa_tagstruct_gets(t
, &role
) < 0 ||
1299 pa_tagstruct_getu32(t
, &n_devices
) < 0 ||
1303 if (PA_INVALID_INDEX
== (role_index
= get_role_index(role
)))
1306 /* Cycle through the devices given and make sure they exist */
1307 h
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1310 for (i
= 0; i
< n_devices
; ++i
) {
1312 if (pa_tagstruct_gets(t
, &s
) < 0) {
1313 while ((device
= pa_hashmap_steal_first(h
))) {
1314 pa_xfree(device
->device
);
1319 pa_log_error("Protocol error on reorder");
1323 /* Ensure this is a valid entry */
1324 if (!(e
= entry_read(u
, s
))) {
1325 while ((device
= pa_hashmap_steal_first(h
))) {
1326 pa_xfree(device
->device
);
1331 pa_log_error("Client specified an unknown device in its reorder list.");
1338 sink_mode
= (0 == strncmp("sink:", s
, 5));
1339 } else if ((sink_mode
&& 0 != strncmp("sink:", s
, 5)) || (!sink_mode
&& 0 != strncmp("source:", s
, 7))) {
1340 while ((device
= pa_hashmap_steal_first(h
))) {
1341 pa_xfree(device
->device
);
1346 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1350 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1351 device
= pa_xnew(struct device_t
, 1);
1352 device
->device
= pa_xstrdup(s
);
1353 if (pa_hashmap_put(h
, device
->device
, device
) == 0) {
1357 pa_xfree(device
->device
);
1362 /*pa_log_debug("Hashmap contents (received from client)");
1363 PA_HASHMAP_FOREACH(device, h, state) {
1364 pa_log_debug(" - %s (%d)", device->device, device->prio);
1367 /* Now cycle through our list and add all the devices.
1368 This has the effect of adding in any in our DB,
1369 not specified in the device list (and thus will be
1370 tacked on at the end) */
1372 done
= !pa_database_first(u
->database
, &key
, NULL
);
1374 while (!done
&& idx
< 256) {
1377 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1379 device
= pa_xnew(struct device_t
, 1);
1380 device
->device
= pa_xstrndup(key
.data
, key
.size
);
1381 if ((sink_mode
&& 0 == strncmp("sink:", device
->device
, 5))
1382 || (!sink_mode
&& 0 == strncmp("source:", device
->device
, 7))) {
1384 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1385 if (pa_hashmap_put(h
, device
->device
, device
) == 0
1386 && (e
= entry_read(u
, device
->device
))) {
1387 /* We add offset on to the existing priority so that when we order, the
1388 existing entries are always lower priority than the new ones. */
1389 device
->prio
= (offset
+ e
->priority
[role_index
]);
1393 pa_xfree(device
->device
);
1397 pa_xfree(device
->device
);
1401 pa_datum_free(&key
);
1406 /*pa_log_debug("Hashmap contents (combined with database)");
1407 PA_HASHMAP_FOREACH(device, h, state) {
1408 pa_log_debug(" - %s (%d)", device->device, device->prio);
1411 /* Now we put all the entries in a simple list for sorting it. */
1412 n_devices
= pa_hashmap_size(h
);
1413 devices
= pa_xnew(struct device_t
*, n_devices
);
1415 while ((device
= pa_hashmap_steal_first(h
))) {
1416 devices
[idx
++] = device
;
1420 /* Simple bubble sort */
1421 for (i
= 0; i
< n_devices
; ++i
) {
1422 for (uint32_t j
= i
; j
< n_devices
; ++j
) {
1423 if (devices
[i
]->prio
> devices
[j
]->prio
) {
1424 struct device_t
*tmp
;
1426 devices
[i
] = devices
[j
];
1432 /*pa_log_debug("Sorted device list");
1433 for (i = 0; i < n_devices; ++i) {
1434 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1437 /* Go through in order and write the new entry and cleanup our own list */
1440 for (i
= 0; i
< n_devices
; ++i
) {
1441 if ((e
= entry_read(u
, devices
[i
]->device
))) {
1442 if (e
->priority
[role_index
] == idx
)
1445 e
->priority
[role_index
] = idx
;
1447 if (entry_write(u
, (char *) devices
[i
]->device
, e
)) {
1455 pa_xfree(devices
[i
]->device
);
1456 pa_xfree(devices
[i
]);
1465 route_sink_inputs(u
, NULL
);
1467 route_source_outputs(u
, NULL
);
1473 case SUBCOMMAND_SUBSCRIBE
: {
1477 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1478 !pa_tagstruct_eof(t
))
1482 pa_idxset_put(u
->subscribed
, c
, NULL
);
1484 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1493 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1499 pa_tagstruct_free(reply
);
1504 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1509 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1513 struct prioritised_indexes
{
1518 int pa__init(pa_module
*m
) {
1519 pa_modargs
*ma
= NULL
;
1525 bool do_routing
= false, on_hotplug
= true, on_rescue
= true;
1526 uint32_t total_devices
;
1530 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1531 pa_log("Failed to parse module arguments");
1535 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1536 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1537 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1538 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1542 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1545 u
->do_routing
= do_routing
;
1546 u
->on_hotplug
= on_hotplug
;
1547 u
->on_rescue
= on_rescue
;
1548 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1550 u
->protocol
= pa_native_protocol_get(m
->core
);
1551 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1553 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
);
1555 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
);
1557 /* Used to handle device description management */
1558 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
);
1559 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
);
1561 /* The following slots are used to deal with routing */
1562 /* A little bit later than module-stream-restore, but before module-intended-roles */
1563 u
->sink_input_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_NEW
], PA_HOOK_EARLY
+5, (pa_hook_cb_t
) sink_input_new_hook_callback
, u
);
1564 u
->source_output_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_OUTPUT_NEW
], PA_HOOK_EARLY
+5, (pa_hook_cb_t
) source_output_new_hook_callback
, u
);
1567 /* A little bit later than module-stream-restore, but before module-intended-roles */
1568 u
->sink_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) sink_put_hook_callback
, u
);
1569 u
->source_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_PUT
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) source_put_hook_callback
, u
);
1573 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1574 u
->sink_unlink_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) sink_unlink_hook_callback
, u
);
1575 u
->source_unlink_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_UNLINK
], PA_HOOK_LATE
+5, (pa_hook_cb_t
) source_unlink_hook_callback
, u
);
1578 if (!(fname
= pa_state_path("device-manager", true)))
1581 if (!(u
->database
= pa_database_open(fname
, true))) {
1582 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1587 pa_log_info("Successfully opened database file '%s'.", fname
);
1590 /* Attempt to inject the devices into the list in priority order */
1591 total_devices
= PA_MAX(pa_idxset_size(m
->core
->sinks
), pa_idxset_size(m
->core
->sources
));
1592 if (total_devices
> 0 && total_devices
< 128) {
1594 struct prioritised_indexes p_i
[128];
1596 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1598 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
) {
1599 pa_log_debug("Found sink index %u", sink
->index
);
1600 p_i
[i
].index
= sink
->index
;
1601 p_i
[i
++].priority
= sink
->priority
;
1603 /* Bubble sort it (only really useful for first time creation) */
1605 for (uint32_t j
= 0; j
< i
; ++j
)
1606 for (uint32_t k
= 0; k
< i
; ++k
)
1607 if (p_i
[j
].priority
> p_i
[k
].priority
) {
1608 struct prioritised_indexes tmp_pi
= p_i
[k
];
1613 for (uint32_t j
= 0; j
< i
; ++j
)
1614 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, p_i
[j
].index
, u
);
1616 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1618 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
) {
1619 p_i
[i
].index
= source
->index
;
1620 p_i
[i
++].priority
= source
->priority
;
1622 /* Bubble sort it (only really useful for first time creation) */
1624 for (uint32_t j
= 0; j
< i
; ++j
)
1625 for (uint32_t k
= 0; k
< i
; ++k
)
1626 if (p_i
[j
].priority
> p_i
[k
].priority
) {
1627 struct prioritised_indexes tmp_pi
= p_i
[k
];
1632 for (uint32_t j
= 0; j
< i
; ++j
)
1633 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, p_i
[j
].index
, u
);
1635 else if (total_devices
> 0) {
1636 /* This user has a *lot* of devices... */
1637 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1638 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1640 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1641 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1644 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1645 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
1646 u
->preferred_sinks
[i
] = u
->preferred_sources
[i
] = PA_INVALID_INDEX
;
1649 route_sink_inputs(u
, NULL
);
1650 route_source_outputs(u
, NULL
);
1652 #ifdef DUMP_DATABASE
1656 pa_modargs_free(ma
);
1663 pa_modargs_free(ma
);
1668 void pa__done(pa_module
*m
) {
1673 if (!(u
= m
->userdata
))
1676 if (u
->subscription
)
1677 pa_subscription_free(u
->subscription
);
1679 if (u
->sink_new_hook_slot
)
1680 pa_hook_slot_free(u
->sink_new_hook_slot
);
1681 if (u
->source_new_hook_slot
)
1682 pa_hook_slot_free(u
->source_new_hook_slot
);
1684 if (u
->sink_input_new_hook_slot
)
1685 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1686 if (u
->source_output_new_hook_slot
)
1687 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1689 if (u
->sink_put_hook_slot
)
1690 pa_hook_slot_free(u
->sink_put_hook_slot
);
1691 if (u
->source_put_hook_slot
)
1692 pa_hook_slot_free(u
->source_put_hook_slot
);
1694 if (u
->sink_unlink_hook_slot
)
1695 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1696 if (u
->source_unlink_hook_slot
)
1697 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1699 if (u
->connection_unlink_hook_slot
)
1700 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1702 if (u
->save_time_event
)
1703 u
->core
->mainloop
->time_free(u
->save_time_event
);
1706 pa_database_close(u
->database
);
1709 pa_native_protocol_remove_ext(u
->protocol
, m
);
1710 pa_native_protocol_unref(u
->protocol
);
1714 pa_idxset_free(u
->subscribed
, NULL
);