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>
54 #include <pulsecore/tagstruct.h>
56 #include "module-device-manager-symdef.h"
58 PA_MODULE_AUTHOR("Colin Guthrie");
59 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
60 PA_MODULE_VERSION(PACKAGE_VERSION
);
61 PA_MODULE_LOAD_ONCE(TRUE
);
63 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
64 "on_hotplug=<When new device becomes available, recheck streams?> "
65 "on_rescue=<When device becomes unavailable, recheck streams?>");
67 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
70 static const char* const valid_modargs
[] = {
91 typedef uint32_t role_indexes_t
[NUM_ROLES
];
93 static const char* role_names
[NUM_ROLES
] = {
108 pa_subscription
*subscription
;
111 *source_new_hook_slot
,
112 *sink_input_new_hook_slot
,
113 *source_output_new_hook_slot
,
115 *source_put_hook_slot
,
116 *sink_unlink_hook_slot
,
117 *source_unlink_hook_slot
,
118 *connection_unlink_hook_slot
;
119 pa_time_event
*save_time_event
;
120 pa_database
*database
;
122 pa_native_protocol
*protocol
;
123 pa_idxset
*subscribed
;
125 pa_bool_t on_hotplug
;
127 pa_bool_t do_routing
;
129 role_indexes_t preferred_sinks
;
130 role_indexes_t preferred_sources
;
133 #define ENTRY_VERSION 1
138 pa_bool_t user_set_description
;
140 role_indexes_t priority
;
148 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
,
150 SUBCOMMAND_SUBSCRIBE
,
155 /* Forward declarations */
157 static void dump_database(struct userdata
*);
159 static void notify_subscribers(struct userdata
*);
162 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
163 struct userdata
*u
= userdata
;
169 pa_assert(e
== u
->save_time_event
);
170 u
->core
->mainloop
->time_free(u
->save_time_event
);
171 u
->save_time_event
= NULL
;
173 pa_database_sync(u
->database
);
174 pa_log_info("Synced.");
181 static void trigger_save(struct userdata
*u
) {
185 notify_subscribers(u
);
187 if (u
->save_time_event
)
190 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
193 static struct entry
* entry_new(void) {
194 struct entry
*r
= pa_xnew0(struct entry
, 1);
195 r
->version
= ENTRY_VERSION
;
199 static void entry_free(struct entry
* e
) {
202 pa_xfree(e
->description
);
206 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
215 t
= pa_tagstruct_new(NULL
, 0);
216 pa_tagstruct_putu8(t
, e
->version
);
217 pa_tagstruct_puts(t
, e
->description
);
218 pa_tagstruct_put_boolean(t
, e
->user_set_description
);
219 pa_tagstruct_puts(t
, e
->icon
);
220 for (uint8_t i
=0; i
<ROLE_MAX
; ++i
)
221 pa_tagstruct_putu32(t
, e
->priority
[i
]);
223 key
.data
= (char *) name
;
224 key
.size
= strlen(name
);
226 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
228 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
230 pa_tagstruct_free(t
);
235 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
237 #define LEGACY_ENTRY_VERSION 1
238 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
239 struct legacy_entry
{
241 char description
[PA_NAME_MAX
];
242 pa_bool_t user_set_description
;
243 char icon
[PA_NAME_MAX
];
244 role_indexes_t priority
;
246 struct legacy_entry
*le
;
252 if (data
->size
!= sizeof(struct legacy_entry
)) {
253 pa_log_debug("Size does not match.");
257 le
= (struct legacy_entry
*)data
->data
;
259 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
260 pa_log_debug("Version mismatch.");
264 if (!memchr(le
->description
, 0, sizeof(le
->description
))) {
265 pa_log_warn("Description has missing NUL byte.");
269 if (!memchr(le
->icon
, 0, sizeof(le
->icon
))) {
270 pa_log_warn("Icon has missing NUL byte.");
275 e
->description
= pa_xstrdup(le
->description
);
276 e
->icon
= pa_xstrdup(le
->icon
);
281 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
283 struct entry
*e
= NULL
;
284 pa_tagstruct
*t
= NULL
;
285 const char *description
, *icon
;
290 key
.data
= (char*) name
;
291 key
.size
= strlen(name
);
295 if (!pa_database_get(u
->database
, &key
, &data
))
298 t
= pa_tagstruct_new(data
.data
, data
.size
);
301 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
302 e
->version
> ENTRY_VERSION
||
303 pa_tagstruct_gets(t
, &description
) < 0 ||
304 pa_tagstruct_get_boolean(t
, &e
->user_set_description
) < 0 ||
305 pa_tagstruct_gets(t
, &icon
) < 0) {
310 e
->description
= pa_xstrdup(description
);
311 e
->icon
= pa_xstrdup(icon
);
313 for (uint8_t i
=0; i
<ROLE_MAX
; ++i
) {
314 if (pa_tagstruct_getu32(t
, &e
->priority
[i
]) < 0)
318 if (!pa_tagstruct_eof(t
))
321 pa_tagstruct_free(t
);
322 pa_datum_free(&data
);
327 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
332 pa_tagstruct_free(t
);
334 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
335 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
336 if ((e
= legacy_entry_read(u
, &data
))) {
337 pa_log_debug("Success. Saving new format for key: %s", name
);
338 if (entry_write(u
, name
, e
))
340 pa_datum_free(&data
);
343 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
346 pa_datum_free(&data
);
351 static void dump_database_helper(struct userdata
*u
, uint32_t role_index
, const char* human
, pa_bool_t sink_mode
) {
357 if (PA_INVALID_INDEX
!= u
->preferred_sinks
[role_index
] && (s
= pa_idxset_get_by_index(u
->core
->sinks
, u
->preferred_sinks
[role_index
])))
358 pa_log_debug(" %s %s (%s)", human
, pa_strnull(pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), s
->name
);
360 pa_log_debug(" %s No sink specified", human
);
363 if (PA_INVALID_INDEX
!= u
->preferred_sources
[role_index
] && (s
= pa_idxset_get_by_index(u
->core
->sources
, u
->preferred_sources
[role_index
])))
364 pa_log_debug(" %s %s (%s)", human
, pa_strnull(pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)), s
->name
);
366 pa_log_debug(" %s No source specified", human
);
370 static void dump_database(struct userdata
*u
) {
376 done
= !pa_database_first(u
->database
, &key
, NULL
);
378 pa_log_debug("Dumping database");
384 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
386 name
= pa_xstrndup(key
.data
, key
.size
);
388 if ((e
= entry_read(u
, name
))) {
389 pa_log_debug(" Got entry: %s", name
);
390 pa_log_debug(" Description: %s", e
->description
);
391 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
392 e
->priority
[ROLE_NONE
], e
->priority
[ROLE_VIDEO
], e
->priority
[ROLE_MUSIC
], e
->priority
[ROLE_GAME
], e
->priority
[ROLE_EVENT
]);
393 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
394 e
->priority
[ROLE_PHONE
], e
->priority
[ROLE_ANIMATION
], e
->priority
[ROLE_PRODUCTION
], e
->priority
[ROLE_A11Y
]);
405 pa_log_debug(" Highest priority devices per-role:");
407 pa_log_debug(" Sinks:");
408 for (uint32_t role
= ROLE_NONE
; role
< NUM_ROLES
; ++role
) {
410 uint32_t len
= PA_MIN(12u, strlen(role_names
[role
]));
411 strncpy(name
, role_names
[role
], len
);
412 for (int i
= len
+1; i
< 12; ++i
) name
[i
] = ' ';
413 name
[len
] = ':'; name
[0] -= 32; name
[12] = '\0';
414 dump_database_helper(u
, role
, name
, TRUE
);
417 pa_log_debug(" Sources:");
418 for (uint32_t role
= ROLE_NONE
; role
< NUM_ROLES
; ++role
) {
420 uint32_t len
= PA_MIN(12u, strlen(role_names
[role
]));
421 strncpy(name
, role_names
[role
], len
);
422 for (int i
= len
+1; i
< 12; ++i
) name
[i
] = ' ';
423 name
[len
] = ':'; name
[0] -= 32; name
[12] = '\0';
424 dump_database_helper(u
, role
, name
, FALSE
);
428 pa_log_debug("Completed database dump");
432 static void notify_subscribers(struct userdata
*u
) {
434 pa_native_connection
*c
;
439 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
442 t
= pa_tagstruct_new(NULL
, 0);
443 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
444 pa_tagstruct_putu32(t
, 0);
445 pa_tagstruct_putu32(t
, u
->module
->index
);
446 pa_tagstruct_puts(t
, u
->module
->name
);
447 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
449 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
453 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
458 if (!pa_streq(a
->description
, b
->description
)
459 || a
->user_set_description
!= b
->user_set_description
460 || !pa_streq(a
->icon
, b
->icon
))
463 for (int i
=0; i
< NUM_ROLES
; ++i
)
464 if (a
->priority
[i
] != b
->priority
[i
])
470 static char *get_name(const char *key
, const char *prefix
) {
473 if (strncmp(key
, prefix
, strlen(prefix
)))
476 t
= pa_xstrdup(key
+ strlen(prefix
));
480 static inline struct entry
*load_or_initialize_entry(struct userdata
*u
, struct entry
*entry
, const char *name
, const char *prefix
) {
488 if ((old
= entry_read(u
, name
))) {
490 entry
->description
= pa_xstrdup(old
->description
);
491 entry
->icon
= pa_xstrdup(old
->icon
);
493 /* This is a new device, so make sure we write it's priority list correctly */
494 role_indexes_t max_priority
;
498 pa_zero(max_priority
);
499 done
= !pa_database_first(u
->database
, &key
, NULL
);
501 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
505 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
507 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
511 name2
= pa_xstrndup(key
.data
, key
.size
);
513 if ((e
= entry_read(u
, name2
))) {
514 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
515 max_priority
[i
] = PA_MAX(max_priority
[i
], e
->priority
[i
]);
527 /* Actually initialise our entry now we've calculated it */
528 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
529 entry
->priority
[i
] = max_priority
[i
] + 1;
531 entry
->user_set_description
= FALSE
;
537 static uint32_t get_role_index(const char* role
) {
540 for (uint32_t i
= ROLE_NONE
; i
< NUM_ROLES
; ++i
)
541 if (strcmp(role
, role_names
[i
]) == 0)
544 return PA_INVALID_INDEX
;
547 static void update_highest_priority_device_indexes(struct userdata
*u
, const char *prefix
, void *ignore_device
) {
548 role_indexes_t
*indexes
, highest_priority_available
;
550 pa_bool_t done
, sink_mode
;
555 sink_mode
= (strcmp(prefix
, "sink:") == 0);
558 indexes
= &u
->preferred_sinks
;
560 indexes
= &u
->preferred_sources
;
562 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
563 (*indexes
)[i
] = PA_INVALID_INDEX
;
565 pa_zero(highest_priority_available
);
567 done
= !pa_database_first(u
->database
, &key
, NULL
);
569 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
573 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
575 if (key
.size
> strlen(prefix
) && strncmp(key
.data
, prefix
, strlen(prefix
)) == 0) {
576 char *name
, *device_name
;
579 name
= pa_xstrndup(key
.data
, key
.size
);
580 device_name
= get_name(name
, prefix
);
582 if ((e
= entry_read(u
, name
))) {
583 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
584 if (!highest_priority_available
[i
] || e
->priority
[i
] < highest_priority_available
[i
]) {
585 /* We've found a device with a higher priority than that we've currently got,
586 so see if it is currently available or not and update our list */
588 pa_bool_t found
= FALSE
;
593 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
594 if ((pa_sink
*) ignore_device
== sink
)
596 if (strcmp(sink
->name
, device_name
) == 0) {
598 idx
= sink
->index
; /* Is this needed? */
605 PA_IDXSET_FOREACH(source
, u
->core
->sources
, idx
) {
606 if ((pa_source
*) ignore_device
== source
)
608 if (strcmp(source
->name
, device_name
) == 0) {
610 idx
= source
->index
; /* Is this needed? */
616 highest_priority_available
[i
] = e
->priority
[i
];
627 pa_xfree(device_name
);
636 static void route_sink_input(struct userdata
*u
, pa_sink_input
*si
) {
638 uint32_t role_index
, device_index
;
642 pa_assert(u
->do_routing
);
647 /* Skip this if it is already in the process of being moved anyway */
651 /* It might happen that a stream and a sink are set up at the
652 same time, in which case we want to make sure we don't
653 interfere with that */
654 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si
)))
657 if (!(role
= pa_proplist_gets(si
->proplist
, PA_PROP_MEDIA_ROLE
)))
658 role_index
= get_role_index("none");
660 role_index
= get_role_index(role
);
662 if (PA_INVALID_INDEX
== role_index
)
665 device_index
= u
->preferred_sinks
[role_index
];
666 if (PA_INVALID_INDEX
== device_index
)
669 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
)))
672 if (si
->sink
!= sink
)
673 pa_sink_input_move_to(si
, sink
, FALSE
);
676 static pa_hook_result_t
route_sink_inputs(struct userdata
*u
, pa_sink
*ignore_sink
) {
685 update_highest_priority_device_indexes(u
, "sink:", ignore_sink
);
687 PA_IDXSET_FOREACH(si
, u
->core
->sink_inputs
, idx
) {
688 route_sink_input(u
, si
);
694 static void route_source_output(struct userdata
*u
, pa_source_output
*so
) {
696 uint32_t role_index
, device_index
;
700 pa_assert(u
->do_routing
);
705 if (so
->direct_on_input
)
708 /* Skip this if it is already in the process of being moved anyway */
712 /* It might happen that a stream and a source are set up at the
713 same time, in which case we want to make sure we don't
714 interfere with that */
715 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so
)))
718 if (!(role
= pa_proplist_gets(so
->proplist
, PA_PROP_MEDIA_ROLE
)))
719 role_index
= get_role_index("none");
721 role_index
= get_role_index(role
);
723 if (PA_INVALID_INDEX
== role_index
)
726 device_index
= u
->preferred_sources
[role_index
];
727 if (PA_INVALID_INDEX
== device_index
)
730 if (!(source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
733 if (so
->source
!= source
)
734 pa_source_output_move_to(so
, source
, FALSE
);
737 static pa_hook_result_t
route_source_outputs(struct userdata
*u
, pa_source
* ignore_source
) {
738 pa_source_output
*so
;
746 update_highest_priority_device_indexes(u
, "source:", ignore_source
);
748 PA_IDXSET_FOREACH(so
, u
->core
->source_outputs
, idx
) {
749 route_source_output(u
, so
);
755 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
756 struct userdata
*u
= userdata
;
757 struct entry
*entry
, *old
= NULL
;
763 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
764 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
765 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
766 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
768 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
769 t
!= (PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
770 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
771 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
))
776 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK_INPUT
) {
781 if (!(si
= pa_idxset_get_by_index(c
->sink_inputs
, idx
)))
784 /* The role may change mid-stream, so we reroute */
785 route_sink_input(u
, si
);
788 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
) {
789 pa_source_output
*so
;
793 if (!(so
= pa_idxset_get_by_index(c
->source_outputs
, idx
)))
796 /* The role may change mid-stream, so we reroute */
797 route_source_output(u
, so
);
800 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
803 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
806 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
808 old
= load_or_initialize_entry(u
, entry
, name
, "sink:");
810 if (!entry
->user_set_description
) {
811 pa_xfree(entry
->description
);
812 entry
->description
= pa_xstrdup(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
));
813 } else if (!pa_streq(entry
->description
, pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
814 /* Warning: If two modules fight over the description, this could cause an infinite loop.
815 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
816 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
817 the description, this will fail... */
818 pa_sink_set_description(sink
, entry
->description
);
821 pa_xfree(entry
->icon
);
822 entry
->icon
= pa_xstrdup(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_ICON_NAME
));
824 } else if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
) {
827 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
829 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
832 if (source
->monitor_of
)
835 name
= pa_sprintf_malloc("source:%s", source
->name
);
837 old
= load_or_initialize_entry(u
, entry
, name
, "source:");
839 if (!entry
->user_set_description
) {
840 pa_xfree(entry
->description
);
841 entry
->description
= pa_xstrdup(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
));
842 } else if (!pa_streq(entry
->description
, pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
843 /* Warning: If two modules fight over the description, this could cause an infinite loop.
844 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
845 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
846 the description, this will fail... */
847 pa_source_set_description(source
, entry
->description
);
850 pa_xfree(entry
->icon
);
851 entry
->icon
= pa_xstrdup(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_ICON_NAME
));
858 if (entries_equal(old
, entry
)) {
869 pa_log_info("Storing device %s.", name
);
871 if (entry_write(u
, name
, entry
))
874 pa_log_warn("Could not save device");;
880 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
888 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
890 if ((e
= entry_read(u
, name
))) {
891 if (e
->user_set_description
&& strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
892 pa_log_info("Restoring description for sink %s.", new_data
->name
);
893 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
904 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
912 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
914 if ((e
= entry_read(u
, name
))) {
915 if (e
->user_set_description
&& strncmp(e
->description
, pa_proplist_gets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
), sizeof(e
->description
)) != 0) {
916 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
917 pa_log_info("Restoring description for source %s.", new_data
->name
);
918 pa_proplist_sets(new_data
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, e
->description
);
929 static pa_hook_result_t
sink_input_new_hook_callback(pa_core
*c
, pa_sink_input_new_data
*new_data
, struct userdata
*u
) {
941 pa_log_debug("Not restoring device for stream because already set.");
943 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
944 role_index
= get_role_index("none");
946 role_index
= get_role_index(role
);
948 if (PA_INVALID_INDEX
!= role_index
) {
949 uint32_t device_index
;
951 device_index
= u
->preferred_sinks
[role_index
];
952 if (PA_INVALID_INDEX
!= device_index
) {
955 if ((sink
= pa_idxset_get_by_index(u
->core
->sinks
, device_index
))) {
956 if (!pa_sink_input_new_data_set_sink(new_data
, sink
, FALSE
))
957 pa_log_debug("Not restoring device for stream because no supported format was found");
966 static pa_hook_result_t
source_output_new_hook_callback(pa_core
*c
, pa_source_output_new_data
*new_data
, struct userdata
*u
) {
977 if (new_data
->direct_on_input
)
980 if (new_data
->source
)
981 pa_log_debug("Not restoring device for stream because already set.");
983 if (!(role
= pa_proplist_gets(new_data
->proplist
, PA_PROP_MEDIA_ROLE
)))
984 role_index
= get_role_index("none");
986 role_index
= get_role_index(role
);
988 if (PA_INVALID_INDEX
!= role_index
) {
989 uint32_t device_index
;
991 device_index
= u
->preferred_sources
[role_index
];
992 if (PA_INVALID_INDEX
!= device_index
) {
995 if ((source
= pa_idxset_get_by_index(u
->core
->sources
, device_index
)))
996 if (!pa_source_output_new_data_set_source(new_data
, source
, FALSE
))
997 pa_log_debug("Not restoring device for stream because no supported format was found");
1006 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_sink
*sink
, struct userdata
*u
) {
1009 pa_assert(u
->core
== c
);
1010 pa_assert(u
->on_hotplug
);
1012 notify_subscribers(u
);
1014 return route_sink_inputs(u
, NULL
);
1017 static pa_hook_result_t
source_put_hook_callback(pa_core
*c
, PA_GCC_UNUSED pa_source
*source
, struct userdata
*u
) {
1020 pa_assert(u
->core
== c
);
1021 pa_assert(u
->on_hotplug
);
1023 notify_subscribers(u
);
1025 return route_source_outputs(u
, NULL
);
1028 static pa_hook_result_t
sink_unlink_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
1032 pa_assert(u
->core
== c
);
1033 pa_assert(u
->on_rescue
);
1035 /* There's no point in doing anything if the core is shut down anyway */
1036 if (c
->state
== PA_CORE_SHUTDOWN
)
1039 notify_subscribers(u
);
1041 return route_sink_inputs(u
, sink
);
1044 static pa_hook_result_t
source_unlink_hook_callback(pa_core
*c
, pa_source
*source
, 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_source_outputs(u
, source
);
1061 static void apply_entry(struct userdata
*u
, const char *name
, struct entry
*e
) {
1069 if (!e
->user_set_description
)
1072 if ((n
= get_name(name
, "sink:"))) {
1074 PA_IDXSET_FOREACH(s
, u
->core
->sinks
, idx
) {
1075 if (!pa_streq(s
->name
, n
)) {
1079 pa_log_info("Setting description for sink %s to '%s'", s
->name
, e
->description
);
1080 pa_sink_set_description(s
, e
->description
);
1084 else if ((n
= get_name(name
, "source:"))) {
1086 PA_IDXSET_FOREACH(s
, u
->core
->sources
, idx
) {
1087 if (!pa_streq(s
->name
, n
)) {
1091 if (s
->monitor_of
) {
1092 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s
->name
);
1096 pa_log_info("Setting description for source %s to '%s'", s
->name
, e
->description
);
1097 pa_source_set_description(s
, e
->description
);
1104 #define EXT_VERSION 1
1106 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
1109 pa_tagstruct
*reply
= NULL
;
1118 if (pa_tagstruct_getu32(t
, &command
) < 0)
1121 reply
= pa_tagstruct_new(NULL
, 0);
1122 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
1123 pa_tagstruct_putu32(reply
, tag
);
1126 case SUBCOMMAND_TEST
: {
1127 if (!pa_tagstruct_eof(t
))
1130 pa_tagstruct_putu32(reply
, EXT_VERSION
);
1134 case SUBCOMMAND_READ
: {
1138 if (!pa_tagstruct_eof(t
))
1141 done
= !pa_database_first(u
->database
, &key
, NULL
);
1148 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1150 name
= pa_xstrndup(key
.data
, key
.size
);
1151 pa_datum_free(&key
);
1153 if ((e
= entry_read(u
, name
))) {
1156 uint32_t found_index
= PA_INVALID_INDEX
;
1158 if ((device_name
= get_name(name
, "sink:"))) {
1160 PA_IDXSET_FOREACH(s
, u
->core
->sinks
, idx
) {
1161 if (strcmp(s
->name
, device_name
) == 0) {
1162 found_index
= s
->index
;
1166 pa_xfree(device_name
);
1167 } else if ((device_name
= get_name(name
, "source:"))) {
1169 PA_IDXSET_FOREACH(s
, u
->core
->sources
, idx
) {
1170 if (strcmp(s
->name
, device_name
) == 0) {
1171 found_index
= s
->index
;
1175 pa_xfree(device_name
);
1178 pa_tagstruct_puts(reply
, name
);
1179 pa_tagstruct_puts(reply
, e
->description
);
1180 pa_tagstruct_puts(reply
, e
->icon
);
1181 pa_tagstruct_putu32(reply
, found_index
);
1182 pa_tagstruct_putu32(reply
, NUM_ROLES
);
1184 for (uint32_t i
= ROLE_NONE
; i
< NUM_ROLES
; ++i
) {
1185 pa_tagstruct_puts(reply
, role_names
[i
]);
1186 pa_tagstruct_putu32(reply
, e
->priority
[i
]);
1200 case SUBCOMMAND_RENAME
: {
1203 const char *device
, *description
;
1205 if (pa_tagstruct_gets(t
, &device
) < 0 ||
1206 pa_tagstruct_gets(t
, &description
) < 0)
1209 if (!device
|| !*device
|| !description
|| !*description
)
1212 if ((e
= entry_read(u
, device
))) {
1213 pa_xfree(e
->description
);
1214 e
->description
= pa_xstrdup(description
);
1215 e
->user_set_description
= TRUE
;
1217 if (entry_write(u
, (char *)device
, e
)) {
1218 apply_entry(u
, device
, e
);
1223 pa_log_warn("Could not save device");
1228 pa_log_warn("Could not rename device %s, no entry in database", device
);
1233 case SUBCOMMAND_DELETE
:
1235 while (!pa_tagstruct_eof(t
)) {
1239 if (pa_tagstruct_gets(t
, &name
) < 0)
1242 key
.data
= (char*) name
;
1243 key
.size
= strlen(name
);
1245 /** @todo: Reindex the priorities */
1246 pa_database_unset(u
->database
, &key
);
1253 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING
: {
1257 if (pa_tagstruct_get_boolean(t
, &enable
) < 0)
1260 if ((u
->do_routing
= enable
)) {
1261 /* Update our caches */
1262 update_highest_priority_device_indexes(u
, "sink:", NULL
);
1263 update_highest_priority_device_indexes(u
, "source:", NULL
);
1269 case SUBCOMMAND_REORDER
: {
1273 uint32_t role_index
, n_devices
;
1275 pa_bool_t done
, sink_mode
= TRUE
;
1276 struct device_t
{ uint32_t prio
; char *device
; };
1277 struct device_t
*device
;
1278 struct device_t
**devices
;
1279 uint32_t i
, idx
, offset
;
1284 if (pa_tagstruct_gets(t
, &role
) < 0 ||
1285 pa_tagstruct_getu32(t
, &n_devices
) < 0 ||
1289 if (PA_INVALID_INDEX
== (role_index
= get_role_index(role
)))
1292 /* Cycle through the devices given and make sure they exist */
1293 h
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1296 for (i
= 0; i
< n_devices
; ++i
) {
1298 if (pa_tagstruct_gets(t
, &s
) < 0) {
1299 while ((device
= pa_hashmap_steal_first(h
))) {
1300 pa_xfree(device
->device
);
1304 pa_hashmap_free(h
, NULL
, NULL
);
1305 pa_log_error("Protocol error on reorder");
1309 /* Ensure this is a valid entry */
1310 if (!(e
= entry_read(u
, s
))) {
1311 while ((device
= pa_hashmap_steal_first(h
))) {
1312 pa_xfree(device
->device
);
1316 pa_hashmap_free(h
, NULL
, NULL
);
1317 pa_log_error("Client specified an unknown device in it's reorder list.");
1324 sink_mode
= (0 == strncmp("sink:", s
, 5));
1325 } else if ((sink_mode
&& 0 != strncmp("sink:", s
, 5)) || (!sink_mode
&& 0 != strncmp("source:", s
, 7))) {
1326 while ((device
= pa_hashmap_steal_first(h
))) {
1327 pa_xfree(device
->device
);
1331 pa_hashmap_free(h
, NULL
, NULL
);
1332 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1336 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1337 device
= pa_xnew(struct device_t
, 1);
1338 device
->device
= pa_xstrdup(s
);
1339 if (pa_hashmap_put(h
, device
->device
, device
) == 0) {
1343 pa_xfree(device
->device
);
1348 /*pa_log_debug("Hashmap contents (received from client)");
1349 PA_HASHMAP_FOREACH(device, h, state) {
1350 pa_log_debug(" - %s (%d)", device->device, device->prio);
1353 /* Now cycle through our list and add all the devices.
1354 This has the effect of addign in any in our DB,
1355 not specified in the device list (and thus will be
1356 tacked on at the end) */
1358 done
= !pa_database_first(u
->database
, &key
, NULL
);
1360 while (!done
&& idx
< 256) {
1363 done
= !pa_database_next(u
->database
, &key
, &next_key
, NULL
);
1365 device
= pa_xnew(struct device_t
, 1);
1366 device
->device
= pa_xstrndup(key
.data
, key
.size
);
1367 if ((sink_mode
&& 0 == strncmp("sink:", device
->device
, 5))
1368 || (!sink_mode
&& 0 == strncmp("source:", device
->device
, 7))) {
1370 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1371 if (pa_hashmap_put(h
, device
->device
, device
) == 0
1372 && (e
= entry_read(u
, device
->device
))) {
1373 /* We add offset on to the existing priorirty so that when we order, the
1374 existing entries are always lower priority than the new ones. */
1375 device
->prio
= (offset
+ e
->priority
[role_index
]);
1379 pa_xfree(device
->device
);
1383 pa_xfree(device
->device
);
1387 pa_datum_free(&key
);
1392 /*pa_log_debug("Hashmap contents (combined with database)");
1393 PA_HASHMAP_FOREACH(device, h, state) {
1394 pa_log_debug(" - %s (%d)", device->device, device->prio);
1397 /* Now we put all the entries in a simple list for sorting it. */
1398 n_devices
= pa_hashmap_size(h
);
1399 devices
= pa_xnew(struct device_t
*, n_devices
);
1401 while ((device
= pa_hashmap_steal_first(h
))) {
1402 devices
[idx
++] = device
;
1404 pa_hashmap_free(h
, NULL
, NULL
);
1406 /* Simple bubble sort */
1407 for (i
= 0; i
< n_devices
; ++i
) {
1408 for (uint32_t j
= i
; j
< n_devices
; ++j
) {
1409 if (devices
[i
]->prio
> devices
[j
]->prio
) {
1410 struct device_t
*tmp
;
1412 devices
[i
] = devices
[j
];
1418 /*pa_log_debug("Sorted device list");
1419 for (i = 0; i < n_devices; ++i) {
1420 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1423 /* Go through in order and write the new entry and cleanup our own list */
1426 for (i
= 0; i
< n_devices
; ++i
) {
1427 if ((e
= entry_read(u
, devices
[i
]->device
))) {
1428 if (e
->priority
[role_index
] == idx
)
1431 e
->priority
[role_index
] = idx
;
1433 if (entry_write(u
, (char *) devices
[i
]->device
, e
)) {
1441 pa_xfree(devices
[i
]->device
);
1442 pa_xfree(devices
[i
]);
1449 route_sink_inputs(u
, NULL
);
1451 route_source_outputs(u
, NULL
);
1457 case SUBCOMMAND_SUBSCRIBE
: {
1461 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1462 !pa_tagstruct_eof(t
))
1466 pa_idxset_put(u
->subscribed
, c
, NULL
);
1468 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1477 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1483 pa_tagstruct_free(reply
);
1488 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1493 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1497 struct prioritised_indexes
{
1502 int pa__init(pa_module
*m
) {
1503 pa_modargs
*ma
= NULL
;
1509 pa_bool_t do_routing
= FALSE
, on_hotplug
= TRUE
, on_rescue
= TRUE
;
1510 uint32_t total_devices
;
1514 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1515 pa_log("Failed to parse module arguments");
1519 if (pa_modargs_get_value_boolean(ma
, "do_routing", &do_routing
) < 0 ||
1520 pa_modargs_get_value_boolean(ma
, "on_hotplug", &on_hotplug
) < 0 ||
1521 pa_modargs_get_value_boolean(ma
, "on_rescue", &on_rescue
) < 0) {
1522 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1526 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1529 u
->do_routing
= do_routing
;
1530 u
->on_hotplug
= on_hotplug
;
1531 u
->on_rescue
= on_rescue
;
1532 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1534 u
->protocol
= pa_native_protocol_get(m
->core
);
1535 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1537 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
);
1539 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
);
1541 /* Used to handle device description management */
1542 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
);
1543 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
);
1545 /* The following slots are used to deal with routing */
1546 /* A little bit later than module-stream-restore, but before module-intended-roles */
1547 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
);
1548 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
);
1551 /* A little bit later than module-stream-restore, but before module-intended-roles */
1552 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
);
1553 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
);
1557 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1558 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
);
1559 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
);
1562 if (!(fname
= pa_state_path("device-manager", TRUE
)))
1565 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1566 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1571 pa_log_info("Successfully opened database file '%s'.", fname
);
1574 /* Attempt to inject the devices into the list in priority order */
1575 total_devices
= PA_MAX(pa_idxset_size(m
->core
->sinks
), pa_idxset_size(m
->core
->sources
));
1576 if (total_devices
> 0 && total_devices
< 128) {
1578 struct prioritised_indexes p_i
[128];
1580 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1582 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
) {
1583 pa_log_debug("Found sink index %u", sink
->index
);
1584 p_i
[i
].index
= sink
->index
;
1585 p_i
[i
++].priority
= sink
->priority
;
1587 /* Bubble sort it (only really useful for first time creation) */
1589 for (uint32_t j
= 0; j
< i
; ++j
)
1590 for (uint32_t k
= 0; k
< i
; ++k
)
1591 if (p_i
[j
].priority
> p_i
[k
].priority
) {
1592 struct prioritised_indexes tmp_pi
= p_i
[k
];
1597 for (uint32_t j
= 0; j
< i
; ++j
)
1598 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, p_i
[j
].index
, u
);
1601 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1603 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
) {
1604 p_i
[i
].index
= source
->index
;
1605 p_i
[i
++].priority
= source
->priority
;
1607 /* Bubble sort it (only really useful for first time creation) */
1609 for (uint32_t j
= 0; j
< i
; ++j
)
1610 for (uint32_t k
= 0; k
< i
; ++k
)
1611 if (p_i
[j
].priority
> p_i
[k
].priority
) {
1612 struct prioritised_indexes tmp_pi
= p_i
[k
];
1617 for (uint32_t j
= 0; j
< i
; ++j
)
1618 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, p_i
[j
].index
, u
);
1620 else if (total_devices
> 0) {
1621 /* This user has a *lot* of devices... */
1622 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1623 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1625 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1626 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1629 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1630 for (uint32_t i
= 0; i
< NUM_ROLES
; ++i
) {
1631 u
->preferred_sinks
[i
] = u
->preferred_sources
[i
] = PA_INVALID_INDEX
;
1634 route_sink_inputs(u
, NULL
);
1635 route_source_outputs(u
, NULL
);
1637 #ifdef DUMP_DATABASE
1641 pa_modargs_free(ma
);
1648 pa_modargs_free(ma
);
1653 void pa__done(pa_module
*m
) {
1658 if (!(u
= m
->userdata
))
1661 if (u
->subscription
)
1662 pa_subscription_free(u
->subscription
);
1664 if (u
->sink_new_hook_slot
)
1665 pa_hook_slot_free(u
->sink_new_hook_slot
);
1666 if (u
->source_new_hook_slot
)
1667 pa_hook_slot_free(u
->source_new_hook_slot
);
1669 if (u
->sink_input_new_hook_slot
)
1670 pa_hook_slot_free(u
->sink_input_new_hook_slot
);
1671 if (u
->source_output_new_hook_slot
)
1672 pa_hook_slot_free(u
->source_output_new_hook_slot
);
1674 if (u
->sink_put_hook_slot
)
1675 pa_hook_slot_free(u
->sink_put_hook_slot
);
1676 if (u
->source_put_hook_slot
)
1677 pa_hook_slot_free(u
->source_put_hook_slot
);
1679 if (u
->sink_unlink_hook_slot
)
1680 pa_hook_slot_free(u
->sink_unlink_hook_slot
);
1681 if (u
->source_unlink_hook_slot
)
1682 pa_hook_slot_free(u
->source_unlink_hook_slot
);
1684 if (u
->connection_unlink_hook_slot
)
1685 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1687 if (u
->save_time_event
)
1688 u
->core
->mainloop
->time_free(u
->save_time_event
);
1691 pa_database_close(u
->database
);
1694 pa_native_protocol_remove_ext(u
->protocol
, m
);
1695 pa_native_protocol_unref(u
->protocol
);
1699 pa_idxset_free(u
->subscribed
, NULL
, NULL
);