]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Save icon and report current availability over protocol.
[pulseaudio] / src / modules / module-device-manager.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
6
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.
11
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.
16
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
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
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>
40
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
55 #include "module-device-manager-symdef.h"
56
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
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?>");
65
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 #define DUMP_DATABASE
68
69 static const char* const valid_modargs[] = {
70 "do_routing",
71 "on_hotplug",
72 "on_rescue",
73 NULL
74 };
75
76 #define NUM_ROLES 9
77 enum {
78 ROLE_NONE,
79 ROLE_VIDEO,
80 ROLE_MUSIC,
81 ROLE_GAME,
82 ROLE_EVENT,
83 ROLE_PHONE,
84 ROLE_ANIMATION,
85 ROLE_PRODUCTION,
86 ROLE_A11Y,
87 };
88
89 typedef uint32_t role_indexes_t[NUM_ROLES];
90
91 static const char* role_names[NUM_ROLES] = {
92 "none",
93 "video",
94 "music",
95 "game",
96 "event",
97 "phone",
98 "animation",
99 "production",
100 "a11y",
101 };
102
103 struct userdata {
104 pa_core *core;
105 pa_module *module;
106 pa_subscription *subscription;
107 pa_hook_slot
108 *sink_new_hook_slot,
109 *source_new_hook_slot,
110 *sink_input_new_hook_slot,
111 *source_output_new_hook_slot,
112 *sink_put_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;
119
120 pa_native_protocol *protocol;
121 pa_idxset *subscribed;
122
123 pa_bool_t on_hotplug;
124 pa_bool_t on_rescue;
125 pa_bool_t do_routing;
126
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
129 };
130
131 #define ENTRY_VERSION 1
132
133 struct entry {
134 uint8_t version;
135 char description[PA_NAME_MAX];
136 char icon[PA_NAME_MAX];
137 role_indexes_t priority;
138 } PA_GCC_PACKED;
139
140 enum {
141 SUBCOMMAND_TEST,
142 SUBCOMMAND_READ,
143 SUBCOMMAND_RENAME,
144 SUBCOMMAND_DELETE,
145 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
146 SUBCOMMAND_PREFER_DEVICE,
147 SUBCOMMAND_DEFER_DEVICE,
148 SUBCOMMAND_SUBSCRIBE,
149 SUBCOMMAND_EVENT
150 };
151
152
153 static struct entry* read_entry(struct userdata *u, const char *name) {
154 pa_datum key, data;
155 struct entry *e;
156
157 pa_assert(u);
158 pa_assert(name);
159
160 key.data = (char*) name;
161 key.size = strlen(name);
162
163 pa_zero(data);
164
165 if (!pa_database_get(u->database, &key, &data))
166 goto fail;
167
168 if (data.size != sizeof(struct entry)) {
169 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
170 goto fail;
171 }
172
173 e = (struct entry*) data.data;
174
175 if (e->version != ENTRY_VERSION) {
176 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
177 goto fail;
178 }
179
180 if (!memchr(e->description, 0, sizeof(e->description))) {
181 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
182 goto fail;
183 }
184
185 return e;
186
187 fail:
188
189 pa_datum_free(&data);
190 return NULL;
191 }
192
193 #ifdef DUMP_DATABASE
194 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
195 pa_assert(u);
196 pa_assert(human);
197
198 if (sink_mode) {
199 pa_sink *s;
200 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
201 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
202 else
203 pa_log_debug(" %s No sink specified", human);
204 } else {
205 pa_source *s;
206 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
207 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
208 else
209 pa_log_debug(" %s No source specified", human);
210 }
211 }
212
213 static void dump_database(struct userdata *u) {
214 pa_datum key;
215 pa_bool_t done;
216
217 pa_assert(u);
218
219 done = !pa_database_first(u->database, &key, NULL);
220
221 pa_log_debug("Dumping database");
222 while (!done) {
223 char *name;
224 struct entry *e;
225 pa_datum next_key;
226
227 done = !pa_database_next(u->database, &key, &next_key, NULL);
228
229 name = pa_xstrndup(key.data, key.size);
230
231 if ((e = read_entry(u, name))) {
232 pa_log_debug(" Got entry: %s", name);
233 pa_log_debug(" Description: %s", e->description);
234 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
235 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
236 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
237 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
238 pa_xfree(e);
239 }
240
241 pa_xfree(name);
242
243 pa_datum_free(&key);
244 key = next_key;
245 }
246
247 pa_log_debug(" Highest priority devices per-role:");
248
249 pa_log_debug(" Sinks:");
250 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
251 char name[13];
252 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
253 strncpy(name, role_names[role], len);
254 for (int i = len+1; i < 12; ++i) name[i] = ' ';
255 name[len] = ':'; name[0] -= 32; name[12] = '\0';
256 dump_database_helper(u, role, name, TRUE);
257 }
258
259 pa_log_debug(" Sources:");
260 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
261 char name[13];
262 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
263 strncpy(name, role_names[role], len);
264 for (int i = len+1; i < 12; ++i) name[i] = ' ';
265 name[len] = ':'; name[0] -= 32; name[12] = '\0';
266 dump_database_helper(u, role, name, FALSE);
267 }
268
269 pa_log_debug("Completed database dump");
270 }
271 #endif
272
273 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
274 struct userdata *u = userdata;
275
276 pa_assert(a);
277 pa_assert(e);
278 pa_assert(u);
279
280 pa_assert(e == u->save_time_event);
281 u->core->mainloop->time_free(u->save_time_event);
282 u->save_time_event = NULL;
283
284 pa_database_sync(u->database);
285 pa_log_info("Synced.");
286
287 #ifdef DUMP_DATABASE
288 dump_database(u);
289 #endif
290 }
291
292 static void notify_subscribers(struct userdata *u) {
293
294 pa_native_connection *c;
295 uint32_t idx;
296
297 pa_assert(u);
298
299 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
300 pa_tagstruct *t;
301
302 t = pa_tagstruct_new(NULL, 0);
303 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
304 pa_tagstruct_putu32(t, 0);
305 pa_tagstruct_putu32(t, u->module->index);
306 pa_tagstruct_puts(t, u->module->name);
307 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
308
309 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
310 }
311 }
312
313 static void trigger_save(struct userdata *u) {
314
315 pa_assert(u);
316
317 notify_subscribers(u);
318
319 if (u->save_time_event)
320 return;
321
322 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
323 }
324
325 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
326 /** @todo: Compare the priority lists too */
327 if (strncmp(a->description, b->description, sizeof(a->description)))
328 return FALSE;
329
330 return TRUE;
331 }
332
333 static char *get_name(const char *key, const char *prefix) {
334 char *t;
335
336 if (strncmp(key, prefix, strlen(prefix)))
337 return NULL;
338
339 t = pa_xstrdup(key + strlen(prefix));
340 return t;
341 }
342
343 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
344 struct entry *old;
345
346 pa_assert(u);
347 pa_assert(entry);
348 pa_assert(name);
349 pa_assert(prefix);
350
351 if ((old = read_entry(u, name)))
352 *entry = *old;
353 else {
354 /* This is a new device, so make sure we write it's priority list correctly */
355 role_indexes_t max_priority;
356 pa_datum key;
357 pa_bool_t done;
358
359 pa_zero(max_priority);
360 done = !pa_database_first(u->database, &key, NULL);
361
362 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
363 while (!done) {
364 pa_datum next_key;
365
366 done = !pa_database_next(u->database, &key, &next_key, NULL);
367
368 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
369 char *name2;
370 struct entry *e;
371
372 name2 = pa_xstrndup(key.data, key.size);
373
374 if ((e = read_entry(u, name2))) {
375 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
376 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
377 }
378
379 pa_xfree(e);
380 }
381
382 pa_xfree(name2);
383 }
384 pa_datum_free(&key);
385 key = next_key;
386 }
387
388 /* Actually initialise our entry now we've calculated it */
389 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
390 entry->priority[i] = max_priority[i] + 1;
391 }
392 }
393
394 return old;
395 }
396
397 static uint32_t get_role_index(const char* role) {
398 pa_assert(role);
399
400 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
401 if (strcmp(role, role_names[i]) == 0)
402 return i;
403
404 return PA_INVALID_INDEX;
405 }
406
407 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
408 role_indexes_t *indexes, highest_priority_available;
409 pa_datum key;
410 pa_bool_t done, sink_mode;
411
412 pa_assert(u);
413 pa_assert(prefix);
414
415 sink_mode = (strcmp(prefix, "sink:") == 0);
416
417 if (sink_mode)
418 indexes = &u->preferred_sinks;
419 else
420 indexes = &u->preferred_sources;
421
422 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
423 *indexes[i] = PA_INVALID_INDEX;
424 }
425 pa_zero(highest_priority_available);
426
427 done = !pa_database_first(u->database, &key, NULL);
428
429 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
430 while (!done) {
431 pa_datum next_key;
432
433 done = !pa_database_next(u->database, &key, &next_key, NULL);
434
435 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
436 char *name;
437 struct entry *e;
438
439 name = pa_xstrndup(key.data, key.size);
440
441 if ((e = read_entry(u, name))) {
442 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
443 if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
444 /* We've found a device with a higher priority than that we've currently got,
445 so see if it is currently available or not and update our list */
446 uint32_t idx;
447 pa_bool_t found = FALSE;
448 char *device_name = get_name(name, prefix);
449
450 if (sink_mode) {
451 pa_sink *sink;
452
453 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
454 if ((pa_sink*) ignore_device == sink)
455 continue;
456 if (strcmp(sink->name, device_name) == 0) {
457 found = TRUE;
458 idx = sink->index; /* Is this needed? */
459 break;
460 }
461 }
462 } else {
463 pa_source *source;
464
465 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
466 if ((pa_source*) ignore_device == source)
467 continue;
468 if (strcmp(source->name, device_name) == 0) {
469 found = TRUE;
470 idx = source->index; /* Is this needed? */
471 break;
472 }
473 }
474 }
475 if (found) {
476 highest_priority_available[i] = e->priority[i];
477 *indexes[i] = idx;
478 }
479
480 pa_xfree(device_name);
481 }
482 }
483
484 pa_xfree(e);
485 }
486
487 pa_xfree(name);
488 }
489
490 pa_datum_free(&key);
491 key = next_key;
492 }
493 }
494
495
496 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
497 const char *role;
498 uint32_t role_index, device_index;
499 pa_sink *sink;
500
501 pa_assert(u);
502 pa_assert(u->do_routing);
503
504 if (si->save_sink)
505 return;
506
507 /* Skip this if it is already in the process of being moved anyway */
508 if (!si->sink)
509 return;
510
511 /* It might happen that a stream and a sink are set up at the
512 same time, in which case we want to make sure we don't
513 interfere with that */
514 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
515 return;
516
517 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
518 role_index = get_role_index("none");
519 else
520 role_index = get_role_index(role);
521
522 if (PA_INVALID_INDEX == role_index)
523 return;
524
525 device_index = u->preferred_sinks[role_index];
526 if (PA_INVALID_INDEX == device_index)
527 return;
528
529 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
530 return;
531
532 if (si->sink != sink)
533 pa_sink_input_move_to(si, sink, TRUE);
534 }
535
536 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
537 pa_sink_input *si;
538 uint32_t idx;
539
540 pa_assert(u);
541
542 if (!u->do_routing)
543 return PA_HOOK_OK;
544
545 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
546
547 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
548 route_sink_input(u, si);
549 }
550
551 return PA_HOOK_OK;
552 }
553
554 static void route_source_output(struct userdata *u, pa_source_output *so) {
555 const char *role;
556 uint32_t role_index, device_index;
557 pa_source *source;
558
559 pa_assert(u);
560 pa_assert(u->do_routing);
561
562 if (so->save_source)
563 return;
564
565 if (so->direct_on_input)
566 return;
567
568 /* Skip this if it is already in the process of being moved anyway */
569 if (!so->source)
570 return;
571
572 /* It might happen that a stream and a source are set up at the
573 same time, in which case we want to make sure we don't
574 interfere with that */
575 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
576 return;
577
578 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
579 role_index = get_role_index("none");
580 else
581 role_index = get_role_index(role);
582
583 if (PA_INVALID_INDEX == role_index)
584 return;
585
586 device_index = u->preferred_sources[role_index];
587 if (PA_INVALID_INDEX == device_index)
588 return;
589
590 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
591 return;
592
593 if (so->source != source)
594 pa_source_output_move_to(so, source, TRUE);
595 }
596
597 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
598 pa_source_output *so;
599 uint32_t idx;
600
601 pa_assert(u);
602
603 if (!u->do_routing)
604 return PA_HOOK_OK;
605
606 update_highest_priority_device_indexes(u, "source:", ignore_source);
607
608 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
609 route_source_output(u, so);
610 }
611
612 return PA_HOOK_OK;
613 }
614
615 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
616 struct userdata *u = userdata;
617 struct entry entry, *old = NULL;
618 char *name = NULL;
619 pa_datum key, data;
620
621 pa_assert(c);
622 pa_assert(u);
623
624 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
625 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
626 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
627 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
628
629 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
630 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
631 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
632 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
633 return;
634
635 pa_zero(entry);
636 entry.version = ENTRY_VERSION;
637
638 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
639 pa_sink_input *si;
640
641 if (!u->do_routing)
642 return;
643 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
644 return;
645
646 /* The role may change mid-stream, so we reroute */
647 route_sink_input(u, si);
648
649 return;
650 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
651 pa_source_output *so;
652
653 if (!u->do_routing)
654 return;
655 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
656 return;
657
658 /* The role may change mid-stream, so we reroute */
659 route_source_output(u, so);
660
661 return;
662 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
663 pa_sink *sink;
664
665 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
666 return;
667
668 name = pa_sprintf_malloc("sink:%s", sink->name);
669
670 old = load_or_initialize_entry(u, &entry, name, "sink:");
671
672 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
673 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
674
675 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
676 pa_source *source;
677
678 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
679
680 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
681 return;
682
683 if (source->monitor_of)
684 return;
685
686 name = pa_sprintf_malloc("source:%s", source->name);
687
688 old = load_or_initialize_entry(u, &entry, name, "source:");
689
690 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
691 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
692 }
693
694 pa_assert(name);
695
696 if (old) {
697
698 if (entries_equal(old, &entry)) {
699 pa_xfree(old);
700 pa_xfree(name);
701
702 /* Even if the entries are equal, the availability or otherwise
703 of the sink/source may have changed so we notify clients all the same */
704 notify_subscribers(u);
705
706 return;
707 }
708
709 pa_xfree(old);
710 }
711
712 key.data = name;
713 key.size = strlen(name);
714
715 data.data = &entry;
716 data.size = sizeof(entry);
717
718 pa_log_info("Storing device %s.", name);
719
720 pa_database_set(u->database, &key, &data, TRUE);
721
722 pa_xfree(name);
723
724 trigger_save(u);
725 }
726
727 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
728 char *name;
729 struct entry *e;
730
731 pa_assert(c);
732 pa_assert(new_data);
733 pa_assert(u);
734
735 name = pa_sprintf_malloc("sink:%s", new_data->name);
736
737 if ((e = read_entry(u, name))) {
738 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
739 pa_log_info("Restoring description for sink %s.", new_data->name);
740 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
741 }
742
743 pa_xfree(e);
744 }
745
746 pa_xfree(name);
747
748 return PA_HOOK_OK;
749 }
750
751 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
752 char *name;
753 struct entry *e;
754
755 pa_assert(c);
756 pa_assert(new_data);
757 pa_assert(u);
758
759 name = pa_sprintf_malloc("source:%s", new_data->name);
760
761 if ((e = read_entry(u, name))) {
762 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
763 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
764 pa_log_info("Restoring description for source %s.", new_data->name);
765 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
766 }
767
768 pa_xfree(e);
769 }
770
771 pa_xfree(name);
772
773 return PA_HOOK_OK;
774 }
775
776 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
777 pa_assert(c);
778 pa_assert(new_data);
779 pa_assert(u);
780
781 if (!u->do_routing)
782 return PA_HOOK_OK;
783
784 if (new_data->sink)
785 pa_log_debug("Not restoring device for stream, because already set.");
786 else {
787 const char *role;
788 uint32_t role_index;
789
790 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
791 role_index = get_role_index("");
792 else
793 role_index = get_role_index(role);
794
795 if (PA_INVALID_INDEX != role_index) {
796 uint32_t device_index;
797
798 device_index = u->preferred_sinks[role_index];
799 if (PA_INVALID_INDEX != device_index) {
800 pa_sink *sink;
801
802 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
803 new_data->sink = sink;
804 new_data->save_sink = TRUE;
805 }
806 }
807 }
808 }
809
810 return PA_HOOK_OK;
811 }
812
813 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
814 pa_assert(c);
815 pa_assert(new_data);
816 pa_assert(u);
817
818 if (!u->do_routing)
819 return PA_HOOK_OK;
820
821 if (new_data->direct_on_input)
822 return PA_HOOK_OK;
823
824 if (new_data->source)
825 pa_log_debug("Not restoring device for stream, because already set");
826 else {
827 const char *role;
828 uint32_t role_index;
829
830 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
831 role_index = get_role_index("");
832 else
833 role_index = get_role_index(role);
834
835 if (PA_INVALID_INDEX != role_index) {
836 uint32_t device_index;
837
838 device_index = u->preferred_sources[role_index];
839 if (PA_INVALID_INDEX != device_index) {
840 pa_source *source;
841
842 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
843 new_data->source = source;
844 new_data->save_source = TRUE;
845 }
846 }
847 }
848 }
849
850 return PA_HOOK_OK;
851 }
852
853
854 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
855 pa_assert(c);
856 pa_assert(u);
857 pa_assert(u->core == c);
858 pa_assert(u->on_hotplug);
859
860 return route_sink_inputs(u, NULL);
861 }
862
863 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
864 pa_assert(c);
865 pa_assert(u);
866 pa_assert(u->core == c);
867 pa_assert(u->on_hotplug);
868
869 return route_source_outputs(u, NULL);
870 }
871
872 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
873 pa_assert(c);
874 pa_assert(sink);
875 pa_assert(u);
876 pa_assert(u->core == c);
877 pa_assert(u->on_rescue);
878
879 /* There's no point in doing anything if the core is shut down anyway */
880 if (c->state == PA_CORE_SHUTDOWN)
881 return PA_HOOK_OK;
882
883 return route_sink_inputs(u, sink);
884 }
885
886 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
887 pa_assert(c);
888 pa_assert(source);
889 pa_assert(u);
890 pa_assert(u->core == c);
891 pa_assert(u->on_rescue);
892
893 /* There's no point in doing anything if the core is shut down anyway */
894 if (c->state == PA_CORE_SHUTDOWN)
895 return PA_HOOK_OK;
896
897 return route_source_outputs(u, source);
898 }
899
900
901 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
902 pa_sink *sink;
903 pa_source *source;
904 uint32_t idx;
905 char *n;
906
907 pa_assert(u);
908 pa_assert(name);
909 pa_assert(e);
910
911 if ((n = get_name(name, "sink:"))) {
912 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
913 if (!pa_streq(sink->name, n)) {
914 continue;
915 }
916
917 pa_log_info("Setting description for sink %s.", sink->name);
918 pa_sink_set_description(sink, e->description);
919 }
920 pa_xfree(n);
921 }
922 else if ((n = get_name(name, "source:"))) {
923 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
924 if (!pa_streq(source->name, n)) {
925 continue;
926 }
927
928 if (source->monitor_of) {
929 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
930 continue;
931 }
932
933 pa_log_info("Setting description for source %s.", source->name);
934 pa_source_set_description(source, e->description);
935 }
936 pa_xfree(n);
937 }
938 }
939
940
941 #define EXT_VERSION 1
942
943 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
944 struct userdata *u;
945 uint32_t command;
946 pa_tagstruct *reply = NULL;
947
948 pa_assert(p);
949 pa_assert(m);
950 pa_assert(c);
951 pa_assert(t);
952
953 u = m->userdata;
954
955 if (pa_tagstruct_getu32(t, &command) < 0)
956 goto fail;
957
958 reply = pa_tagstruct_new(NULL, 0);
959 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
960 pa_tagstruct_putu32(reply, tag);
961
962 switch (command) {
963 case SUBCOMMAND_TEST: {
964 if (!pa_tagstruct_eof(t))
965 goto fail;
966
967 pa_tagstruct_putu32(reply, EXT_VERSION);
968 break;
969 }
970
971 case SUBCOMMAND_READ: {
972 pa_datum key;
973 pa_bool_t done;
974
975 if (!pa_tagstruct_eof(t))
976 goto fail;
977
978 done = !pa_database_first(u->database, &key, NULL);
979
980 while (!done) {
981 pa_datum next_key;
982 struct entry *e;
983 char *name;
984
985 done = !pa_database_next(u->database, &key, &next_key, NULL);
986
987 name = pa_xstrndup(key.data, key.size);
988 pa_datum_free(&key);
989
990 if ((e = read_entry(u, name))) {
991 uint32_t idx;
992 char *devname;
993 pa_bool_t available = FALSE;
994
995 if ((devname = get_name(name, "sink:"))) {
996 pa_sink* s;
997 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
998 if (strcmp(s->name, devname) == 0) {
999 available = TRUE;
1000 break;
1001 }
1002 }
1003 pa_xfree(devname);
1004 } else if ((devname = get_name(name, "source:"))) {
1005 pa_source* s;
1006 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1007 if (strcmp(s->name, devname) == 0) {
1008 available = TRUE;
1009 break;
1010 }
1011 }
1012 pa_xfree(devname);
1013 }
1014
1015 pa_tagstruct_puts(reply, name);
1016 pa_tagstruct_puts(reply, e->description);
1017 pa_tagstruct_puts(reply, e->icon);
1018 pa_tagstruct_put_boolean(reply, available);
1019 pa_tagstruct_putu32(reply, NUM_ROLES);
1020
1021 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1022 pa_tagstruct_puts(reply, role_names[i]);
1023 pa_tagstruct_putu32(reply, e->priority[i]);
1024 }
1025
1026 pa_xfree(e);
1027 }
1028
1029 pa_xfree(name);
1030
1031 key = next_key;
1032 }
1033
1034 break;
1035 }
1036
1037 case SUBCOMMAND_RENAME: {
1038
1039 struct entry *e;
1040 const char *device, *description;
1041
1042 if (pa_tagstruct_gets(t, &device) < 0 ||
1043 pa_tagstruct_gets(t, &description) < 0)
1044 goto fail;
1045
1046 if (!device || !*device || !description || !*description)
1047 goto fail;
1048
1049 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1050 pa_datum key, data;
1051
1052 pa_strlcpy(e->description, description, sizeof(e->description));
1053
1054 key.data = (char *) device;
1055 key.size = strlen(device);
1056
1057 data.data = e;
1058 data.size = sizeof(*e);
1059
1060 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1061 apply_entry(u, device, e);
1062
1063 trigger_save(u);
1064 }
1065 else
1066 pa_log_warn("Could not save device");
1067
1068 pa_xfree(e);
1069 }
1070 else
1071 pa_log_warn("Could not rename device %s, no entry in database", device);
1072
1073 break;
1074 }
1075
1076 case SUBCOMMAND_DELETE:
1077
1078 while (!pa_tagstruct_eof(t)) {
1079 const char *name;
1080 pa_datum key;
1081
1082 if (pa_tagstruct_gets(t, &name) < 0)
1083 goto fail;
1084
1085 key.data = (char*) name;
1086 key.size = strlen(name);
1087
1088 /** @todo: Reindex the priorities */
1089 pa_database_unset(u->database, &key);
1090 }
1091
1092 trigger_save(u);
1093
1094 break;
1095
1096 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1097
1098 pa_bool_t enable;
1099
1100 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1101 goto fail;
1102
1103 if ((u->do_routing = enable)) {
1104 /* Update our caches */
1105 update_highest_priority_device_indexes(u, "sink:", NULL);
1106 update_highest_priority_device_indexes(u, "source:", NULL);
1107 }
1108
1109 break;
1110 }
1111
1112 case SUBCOMMAND_PREFER_DEVICE:
1113 case SUBCOMMAND_DEFER_DEVICE: {
1114
1115 const char *role, *device;
1116 struct entry *e;
1117 uint32_t role_index;
1118
1119 if (pa_tagstruct_gets(t, &role) < 0 ||
1120 pa_tagstruct_gets(t, &device) < 0)
1121 goto fail;
1122
1123 if (!role || !device || !*device)
1124 goto fail;
1125
1126 role_index = get_role_index(role);
1127 if (PA_INVALID_INDEX == role_index)
1128 goto fail;
1129
1130 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1131 pa_datum key, data;
1132 pa_bool_t done;
1133 char* prefix = NULL;
1134 uint32_t priority;
1135 pa_bool_t haschanged = FALSE;
1136
1137 if (strncmp(device, "sink:", 5) == 0)
1138 prefix = pa_xstrdup("sink:");
1139 else if (strncmp(device, "source:", 7) == 0)
1140 prefix = pa_xstrdup("source:");
1141
1142 if (!prefix)
1143 goto fail;
1144
1145 priority = e->priority[role_index];
1146
1147 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
1148
1149 done = !pa_database_first(u->database, &key, NULL);
1150
1151 while (!done && !haschanged) {
1152 pa_datum next_key;
1153
1154 done = !pa_database_next(u->database, &key, &next_key, NULL);
1155
1156 /* Only read devices with the right prefix */
1157 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
1158 char *name;
1159 struct entry *e2;
1160
1161 name = pa_xstrndup(key.data, key.size);
1162
1163 if ((e2 = read_entry(u, name))) {
1164 if (SUBCOMMAND_PREFER_DEVICE == command) {
1165 /* PREFER */
1166 if (e2->priority[role_index] == (priority - 1)) {
1167 e2->priority[role_index]++;
1168 haschanged = TRUE;
1169 }
1170 } else {
1171 /* DEFER */
1172 if (e2->priority[role_index] == (priority + 1)) {
1173 e2->priority[role_index]--;
1174 haschanged = TRUE;
1175 }
1176 }
1177
1178 if (haschanged) {
1179 data.data = e2;
1180 data.size = sizeof(*e2);
1181
1182 if (pa_database_set(u->database, &key, &data, TRUE))
1183 pa_log_warn("Could not save device");
1184 }
1185
1186 pa_xfree(e2);
1187 }
1188
1189 pa_xfree(name);
1190 }
1191
1192 pa_datum_free(&key);
1193 key = next_key;
1194 }
1195
1196 /* Now write out our actual entry */
1197 if (haschanged) {
1198 if (SUBCOMMAND_PREFER_DEVICE == command)
1199 e->priority[role_index]--;
1200 else
1201 e->priority[role_index]++;
1202
1203 key.data = (char *) device;
1204 key.size = strlen(device);
1205
1206 data.data = e;
1207 data.size = sizeof(*e);
1208
1209 if (pa_database_set(u->database, &key, &data, TRUE))
1210 pa_log_warn("Could not save device");
1211
1212 trigger_save(u);
1213 }
1214
1215 pa_xfree(e);
1216
1217 pa_xfree(prefix);
1218 }
1219 else
1220 pa_log_warn("Could not reorder device %s, no entry in database", device);
1221
1222 break;
1223 }
1224
1225 case SUBCOMMAND_SUBSCRIBE: {
1226
1227 pa_bool_t enabled;
1228
1229 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1230 !pa_tagstruct_eof(t))
1231 goto fail;
1232
1233 if (enabled)
1234 pa_idxset_put(u->subscribed, c, NULL);
1235 else
1236 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1237
1238 break;
1239 }
1240
1241 default:
1242 goto fail;
1243 }
1244
1245 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1246 return 0;
1247
1248 fail:
1249
1250 if (reply)
1251 pa_tagstruct_free(reply);
1252
1253 return -1;
1254 }
1255
1256 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1257 pa_assert(p);
1258 pa_assert(c);
1259 pa_assert(u);
1260
1261 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1262 return PA_HOOK_OK;
1263 }
1264
1265 int pa__init(pa_module*m) {
1266 pa_modargs *ma = NULL;
1267 struct userdata *u;
1268 char *fname;
1269 pa_sink *sink;
1270 pa_source *source;
1271 uint32_t idx;
1272 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1273
1274 pa_assert(m);
1275
1276 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1277 pa_log("Failed to parse module arguments");
1278 goto fail;
1279 }
1280
1281 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1282 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1283 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1284 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1285 goto fail;
1286 }
1287
1288 m->userdata = u = pa_xnew0(struct userdata, 1);
1289 u->core = m->core;
1290 u->module = m;
1291 u->do_routing = do_routing;
1292 u->on_hotplug = on_hotplug;
1293 u->on_rescue = on_rescue;
1294 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1295
1296 u->protocol = pa_native_protocol_get(m->core);
1297 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1298
1299 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);
1300
1301 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);
1302
1303 /* Used to handle device description management */
1304 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);
1305 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);
1306
1307 /* The following slots are used to deal with routing */
1308 /* A little bit later than module-stream-restore, module-intended-roles */
1309 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);
1310 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);
1311
1312 if (on_hotplug) {
1313 /* A little bit later than module-stream-restore, module-intended-roles */
1314 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);
1315 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);
1316 }
1317
1318 if (on_rescue) {
1319 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1320 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);
1321 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);
1322 }
1323
1324 if (!(fname = pa_state_path("device-manager", TRUE)))
1325 goto fail;
1326
1327 if (!(u->database = pa_database_open(fname, TRUE))) {
1328 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1329 pa_xfree(fname);
1330 goto fail;
1331 }
1332
1333 pa_log_info("Sucessfully opened database file '%s'.", fname);
1334 pa_xfree(fname);
1335
1336 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1337 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1338 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1339
1340 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1341 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1342
1343 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1344 route_sink_inputs(u, NULL);
1345 route_source_outputs(u, NULL);
1346
1347 #ifdef DUMP_DATABASE
1348 dump_database(u);
1349 #endif
1350
1351 pa_modargs_free(ma);
1352 return 0;
1353
1354 fail:
1355 pa__done(m);
1356
1357 if (ma)
1358 pa_modargs_free(ma);
1359
1360 return -1;
1361 }
1362
1363 void pa__done(pa_module*m) {
1364 struct userdata* u;
1365
1366 pa_assert(m);
1367
1368 if (!(u = m->userdata))
1369 return;
1370
1371 if (u->subscription)
1372 pa_subscription_free(u->subscription);
1373
1374 if (u->sink_new_hook_slot)
1375 pa_hook_slot_free(u->sink_new_hook_slot);
1376 if (u->source_new_hook_slot)
1377 pa_hook_slot_free(u->source_new_hook_slot);
1378
1379 if (u->sink_input_new_hook_slot)
1380 pa_hook_slot_free(u->sink_input_new_hook_slot);
1381 if (u->source_output_new_hook_slot)
1382 pa_hook_slot_free(u->source_output_new_hook_slot);
1383
1384 if (u->sink_put_hook_slot)
1385 pa_hook_slot_free(u->sink_put_hook_slot);
1386 if (u->source_put_hook_slot)
1387 pa_hook_slot_free(u->source_put_hook_slot);
1388
1389 if (u->sink_unlink_hook_slot)
1390 pa_hook_slot_free(u->sink_unlink_hook_slot);
1391 if (u->source_unlink_hook_slot)
1392 pa_hook_slot_free(u->source_unlink_hook_slot);
1393
1394 if (u->save_time_event)
1395 u->core->mainloop->time_free(u->save_time_event);
1396
1397 if (u->database)
1398 pa_database_close(u->database);
1399
1400 if (u->protocol) {
1401 pa_native_protocol_remove_ext(u->protocol, m);
1402 pa_native_protocol_unref(u->protocol);
1403 }
1404
1405 if (u->subscribed)
1406 pa_idxset_free(u->subscribed, NULL, NULL);
1407
1408 pa_xfree(u);
1409 }