]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Keep track as to whether or not the user specifically renamed the...
[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 pa_bool_t user_set_description;
137 char icon[PA_NAME_MAX];
138 role_indexes_t priority;
139 } PA_GCC_PACKED;
140
141 enum {
142 SUBCOMMAND_TEST,
143 SUBCOMMAND_READ,
144 SUBCOMMAND_RENAME,
145 SUBCOMMAND_DELETE,
146 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
147 SUBCOMMAND_REORDER,
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 if (!memchr(e->icon, 0, sizeof(e->icon))) {
186 pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name);
187 goto fail;
188 }
189
190 return e;
191
192 fail:
193
194 pa_datum_free(&data);
195 return NULL;
196 }
197
198 #ifdef DUMP_DATABASE
199 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
200 pa_assert(u);
201 pa_assert(human);
202
203 if (sink_mode) {
204 pa_sink *s;
205 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
206 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
207 else
208 pa_log_debug(" %s No sink specified", human);
209 } else {
210 pa_source *s;
211 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
212 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
213 else
214 pa_log_debug(" %s No source specified", human);
215 }
216 }
217
218 static void dump_database(struct userdata *u) {
219 pa_datum key;
220 pa_bool_t done;
221
222 pa_assert(u);
223
224 done = !pa_database_first(u->database, &key, NULL);
225
226 pa_log_debug("Dumping database");
227 while (!done) {
228 char *name;
229 struct entry *e;
230 pa_datum next_key;
231
232 done = !pa_database_next(u->database, &key, &next_key, NULL);
233
234 name = pa_xstrndup(key.data, key.size);
235
236 if ((e = read_entry(u, name))) {
237 pa_log_debug(" Got entry: %s", name);
238 pa_log_debug(" Description: %s", e->description);
239 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
240 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
241 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
242 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
243 pa_xfree(e);
244 }
245
246 pa_xfree(name);
247
248 pa_datum_free(&key);
249 key = next_key;
250 }
251
252 if (u->do_routing) {
253 pa_log_debug(" Highest priority devices per-role:");
254
255 pa_log_debug(" Sinks:");
256 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
257 char name[13];
258 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
259 strncpy(name, role_names[role], len);
260 for (int i = len+1; i < 12; ++i) name[i] = ' ';
261 name[len] = ':'; name[0] -= 32; name[12] = '\0';
262 dump_database_helper(u, role, name, TRUE);
263 }
264
265 pa_log_debug(" Sources:");
266 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
267 char name[13];
268 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
269 strncpy(name, role_names[role], len);
270 for (int i = len+1; i < 12; ++i) name[i] = ' ';
271 name[len] = ':'; name[0] -= 32; name[12] = '\0';
272 dump_database_helper(u, role, name, FALSE);
273 }
274 }
275
276 pa_log_debug("Completed database dump");
277 }
278 #endif
279
280 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
281 struct userdata *u = userdata;
282
283 pa_assert(a);
284 pa_assert(e);
285 pa_assert(u);
286
287 pa_assert(e == u->save_time_event);
288 u->core->mainloop->time_free(u->save_time_event);
289 u->save_time_event = NULL;
290
291 pa_database_sync(u->database);
292 pa_log_info("Synced.");
293
294 #ifdef DUMP_DATABASE
295 dump_database(u);
296 #endif
297 }
298
299 static void notify_subscribers(struct userdata *u) {
300
301 pa_native_connection *c;
302 uint32_t idx;
303
304 pa_assert(u);
305
306 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
307 pa_tagstruct *t;
308
309 t = pa_tagstruct_new(NULL, 0);
310 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
311 pa_tagstruct_putu32(t, 0);
312 pa_tagstruct_putu32(t, u->module->index);
313 pa_tagstruct_puts(t, u->module->name);
314 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
315
316 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
317 }
318 }
319
320 static void trigger_save(struct userdata *u) {
321
322 pa_assert(u);
323
324 notify_subscribers(u);
325
326 if (u->save_time_event)
327 return;
328
329 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
330 }
331
332 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
333
334 pa_assert(a);
335 pa_assert(b);
336
337 if (strncmp(a->description, b->description, sizeof(a->description))
338 || a->user_set_description != b->user_set_description
339 || strncmp(a->icon, b->icon, sizeof(a->icon)))
340 return FALSE;
341
342 for (int i=0; i < NUM_ROLES; ++i)
343 if (a->priority[i] != b->priority[i])
344 return FALSE;
345
346 return TRUE;
347 }
348
349 static char *get_name(const char *key, const char *prefix) {
350 char *t;
351
352 if (strncmp(key, prefix, strlen(prefix)))
353 return NULL;
354
355 t = pa_xstrdup(key + strlen(prefix));
356 return t;
357 }
358
359 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
360 struct entry *old;
361
362 pa_assert(u);
363 pa_assert(entry);
364 pa_assert(name);
365 pa_assert(prefix);
366
367 if ((old = read_entry(u, name)))
368 *entry = *old;
369 else {
370 /* This is a new device, so make sure we write it's priority list correctly */
371 role_indexes_t max_priority;
372 pa_datum key;
373 pa_bool_t done;
374
375 pa_zero(max_priority);
376 done = !pa_database_first(u->database, &key, NULL);
377
378 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
379 while (!done) {
380 pa_datum next_key;
381
382 done = !pa_database_next(u->database, &key, &next_key, NULL);
383
384 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
385 char *name2;
386 struct entry *e;
387
388 name2 = pa_xstrndup(key.data, key.size);
389
390 if ((e = read_entry(u, name2))) {
391 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
392 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
393 }
394
395 pa_xfree(e);
396 }
397
398 pa_xfree(name2);
399 }
400 pa_datum_free(&key);
401 key = next_key;
402 }
403
404 /* Actually initialise our entry now we've calculated it */
405 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
406 entry->priority[i] = max_priority[i] + 1;
407 }
408 entry->user_set_description = FALSE;
409 }
410
411 return old;
412 }
413
414 static uint32_t get_role_index(const char* role) {
415 pa_assert(role);
416
417 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
418 if (strcmp(role, role_names[i]) == 0)
419 return i;
420
421 return PA_INVALID_INDEX;
422 }
423
424 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
425 role_indexes_t *indexes, highest_priority_available;
426 pa_datum key;
427 pa_bool_t done, sink_mode;
428
429 pa_assert(u);
430 pa_assert(prefix);
431
432 sink_mode = (strcmp(prefix, "sink:") == 0);
433
434 if (sink_mode)
435 indexes = &u->preferred_sinks;
436 else
437 indexes = &u->preferred_sources;
438
439 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
440 (*indexes)[i] = PA_INVALID_INDEX;
441 }
442 pa_zero(highest_priority_available);
443
444 done = !pa_database_first(u->database, &key, NULL);
445
446 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
447 while (!done) {
448 pa_datum next_key;
449
450 done = !pa_database_next(u->database, &key, &next_key, NULL);
451
452 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
453 char *name, *device_name;
454 struct entry *e;
455
456 name = pa_xstrndup(key.data, key.size);
457 device_name = get_name(name, prefix);
458
459 if ((e = read_entry(u, name))) {
460 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
461 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
462 /* We've found a device with a higher priority than that we've currently got,
463 so see if it is currently available or not and update our list */
464 uint32_t idx;
465 pa_bool_t found = FALSE;
466
467 if (sink_mode) {
468 pa_sink *sink;
469
470 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
471 if ((pa_sink*) ignore_device == sink)
472 continue;
473 if (strcmp(sink->name, device_name) == 0) {
474 found = TRUE;
475 idx = sink->index; /* Is this needed? */
476 break;
477 }
478 }
479 } else {
480 pa_source *source;
481
482 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
483 if ((pa_source*) ignore_device == source)
484 continue;
485 if (strcmp(source->name, device_name) == 0) {
486 found = TRUE;
487 idx = source->index; /* Is this needed? */
488 break;
489 }
490 }
491 }
492 if (found) {
493 highest_priority_available[i] = e->priority[i];
494 (*indexes)[i] = idx;
495 }
496
497 }
498 }
499
500 pa_xfree(e);
501 }
502
503 pa_xfree(name);
504 pa_xfree(device_name);
505 }
506
507 pa_datum_free(&key);
508 key = next_key;
509 }
510 }
511
512
513 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
514 const char *role;
515 uint32_t role_index, device_index;
516 pa_sink *sink;
517
518 pa_assert(u);
519 pa_assert(u->do_routing);
520
521 /* Skip this if it is already in the process of being moved anyway */
522 if (!si->sink)
523 return;
524
525 /* It might happen that a stream and a sink are set up at the
526 same time, in which case we want to make sure we don't
527 interfere with that */
528 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
529 return;
530
531 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
532 role_index = get_role_index("none");
533 else
534 role_index = get_role_index(role);
535
536 if (PA_INVALID_INDEX == role_index)
537 return;
538
539 device_index = u->preferred_sinks[role_index];
540 if (PA_INVALID_INDEX == device_index)
541 return;
542
543 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
544 return;
545
546 if (si->sink != sink)
547 pa_sink_input_move_to(si, sink, TRUE);
548 }
549
550 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
551 pa_sink_input *si;
552 uint32_t idx;
553
554 pa_assert(u);
555
556 if (!u->do_routing)
557 return PA_HOOK_OK;
558
559 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
560
561 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
562 route_sink_input(u, si);
563 }
564
565 return PA_HOOK_OK;
566 }
567
568 static void route_source_output(struct userdata *u, pa_source_output *so) {
569 const char *role;
570 uint32_t role_index, device_index;
571 pa_source *source;
572
573 pa_assert(u);
574 pa_assert(u->do_routing);
575
576 if (so->direct_on_input)
577 return;
578
579 /* Skip this if it is already in the process of being moved anyway */
580 if (!so->source)
581 return;
582
583 /* It might happen that a stream and a source are set up at the
584 same time, in which case we want to make sure we don't
585 interfere with that */
586 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
587 return;
588
589 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
590 role_index = get_role_index("none");
591 else
592 role_index = get_role_index(role);
593
594 if (PA_INVALID_INDEX == role_index)
595 return;
596
597 device_index = u->preferred_sources[role_index];
598 if (PA_INVALID_INDEX == device_index)
599 return;
600
601 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
602 return;
603
604 if (so->source != source)
605 pa_source_output_move_to(so, source, TRUE);
606 }
607
608 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
609 pa_source_output *so;
610 uint32_t idx;
611
612 pa_assert(u);
613
614 if (!u->do_routing)
615 return PA_HOOK_OK;
616
617 update_highest_priority_device_indexes(u, "source:", ignore_source);
618
619 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
620 route_source_output(u, so);
621 }
622
623 return PA_HOOK_OK;
624 }
625
626 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
627 struct userdata *u = userdata;
628 struct entry entry, *old = NULL;
629 char *name = NULL;
630 pa_datum key, data;
631
632 pa_assert(c);
633 pa_assert(u);
634
635 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
636 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
637 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
638 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
639
640 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
641 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
642 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
643 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
644 return;
645
646 pa_zero(entry);
647 entry.version = ENTRY_VERSION;
648
649 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
650 pa_sink_input *si;
651
652 if (!u->do_routing)
653 return;
654 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
655 return;
656
657 /* The role may change mid-stream, so we reroute */
658 route_sink_input(u, si);
659
660 return;
661 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
662 pa_source_output *so;
663
664 if (!u->do_routing)
665 return;
666 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
667 return;
668
669 /* The role may change mid-stream, so we reroute */
670 route_source_output(u, so);
671
672 return;
673 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
674 pa_sink *sink;
675
676 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
677 return;
678
679 name = pa_sprintf_malloc("sink:%s", sink->name);
680
681 old = load_or_initialize_entry(u, &entry, name, "sink:");
682
683 if (!entry.user_set_description)
684 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
685 else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
686 /* Warning: If two modules fight over the description, this could cause an infinite loop.
687 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
688 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
689 the description, this will fail... */
690 pa_sink_set_description(sink, entry.description);
691 }
692
693 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
694
695 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
696 pa_source *source;
697
698 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
699
700 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
701 return;
702
703 if (source->monitor_of)
704 return;
705
706 name = pa_sprintf_malloc("source:%s", source->name);
707
708 old = load_or_initialize_entry(u, &entry, name, "source:");
709
710 if (!entry.user_set_description)
711 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
712 else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
713 /* Warning: If two modules fight over the description, this could cause an infinite loop.
714 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
715 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
716 the description, this will fail... */
717 pa_source_set_description(source, entry.description);
718 }
719
720 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
721 }
722
723 pa_assert(name);
724
725 if (old) {
726
727 if (entries_equal(old, &entry)) {
728 pa_xfree(old);
729 pa_xfree(name);
730
731 return;
732 }
733
734 pa_xfree(old);
735 }
736
737 key.data = name;
738 key.size = strlen(name);
739
740 data.data = &entry;
741 data.size = sizeof(entry);
742
743 pa_log_info("Storing device %s.", name);
744
745 if (pa_database_set(u->database, &key, &data, TRUE) == 0)
746 trigger_save(u);
747 else
748 pa_log_warn("Could not save device");;
749
750 pa_xfree(name);
751 }
752
753 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
754 char *name;
755 struct entry *e;
756
757 pa_assert(c);
758 pa_assert(new_data);
759 pa_assert(u);
760
761 name = pa_sprintf_malloc("sink:%s", new_data->name);
762
763 if ((e = read_entry(u, name))) {
764 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
765 pa_log_info("Restoring description for sink %s.", new_data->name);
766 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
767 }
768
769 pa_xfree(e);
770 }
771
772 pa_xfree(name);
773
774 return PA_HOOK_OK;
775 }
776
777 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
778 char *name;
779 struct entry *e;
780
781 pa_assert(c);
782 pa_assert(new_data);
783 pa_assert(u);
784
785 name = pa_sprintf_malloc("source:%s", new_data->name);
786
787 if ((e = read_entry(u, name))) {
788 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
789 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
790 pa_log_info("Restoring description for source %s.", new_data->name);
791 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
792 }
793
794 pa_xfree(e);
795 }
796
797 pa_xfree(name);
798
799 return PA_HOOK_OK;
800 }
801
802 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
803 const char *role;
804 uint32_t role_index;
805
806 pa_assert(c);
807 pa_assert(new_data);
808 pa_assert(u);
809
810 if (!u->do_routing)
811 return PA_HOOK_OK;
812
813 if (new_data->sink)
814 pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
815
816 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
817 role_index = get_role_index("none");
818 else
819 role_index = get_role_index(role);
820
821 if (PA_INVALID_INDEX != role_index) {
822 uint32_t device_index;
823
824 device_index = u->preferred_sinks[role_index];
825 if (PA_INVALID_INDEX != device_index) {
826 pa_sink *sink;
827
828 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
829 new_data->sink = sink;
830 }
831 }
832 }
833
834 return PA_HOOK_OK;
835 }
836
837 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
838 const char *role;
839 uint32_t role_index;
840
841 pa_assert(c);
842 pa_assert(new_data);
843 pa_assert(u);
844
845 if (!u->do_routing)
846 return PA_HOOK_OK;
847
848 if (new_data->direct_on_input)
849 return PA_HOOK_OK;
850
851 if (new_data->source)
852 pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
853
854 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
855 role_index = get_role_index("none");
856 else
857 role_index = get_role_index(role);
858
859 if (PA_INVALID_INDEX != role_index) {
860 uint32_t device_index;
861
862 device_index = u->preferred_sources[role_index];
863 if (PA_INVALID_INDEX != device_index) {
864 pa_source *source;
865
866 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
867 new_data->source = source;
868 }
869 }
870 }
871
872 return PA_HOOK_OK;
873 }
874
875
876 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
877 pa_assert(c);
878 pa_assert(u);
879 pa_assert(u->core == c);
880 pa_assert(u->on_hotplug);
881
882 notify_subscribers(u);
883
884 return route_sink_inputs(u, NULL);
885 }
886
887 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
888 pa_assert(c);
889 pa_assert(u);
890 pa_assert(u->core == c);
891 pa_assert(u->on_hotplug);
892
893 notify_subscribers(u);
894
895 return route_source_outputs(u, NULL);
896 }
897
898 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
899 pa_assert(c);
900 pa_assert(sink);
901 pa_assert(u);
902 pa_assert(u->core == c);
903 pa_assert(u->on_rescue);
904
905 /* There's no point in doing anything if the core is shut down anyway */
906 if (c->state == PA_CORE_SHUTDOWN)
907 return PA_HOOK_OK;
908
909 notify_subscribers(u);
910
911 return route_sink_inputs(u, sink);
912 }
913
914 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
915 pa_assert(c);
916 pa_assert(source);
917 pa_assert(u);
918 pa_assert(u->core == c);
919 pa_assert(u->on_rescue);
920
921 /* There's no point in doing anything if the core is shut down anyway */
922 if (c->state == PA_CORE_SHUTDOWN)
923 return PA_HOOK_OK;
924
925 notify_subscribers(u);
926
927 return route_source_outputs(u, source);
928 }
929
930
931 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
932 pa_sink *sink;
933 pa_source *source;
934 uint32_t idx;
935 char *n;
936
937 pa_assert(u);
938 pa_assert(name);
939 pa_assert(e);
940
941 if (!e->user_set_description)
942 return;
943
944 if ((n = get_name(name, "sink:"))) {
945 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
946 if (!pa_streq(sink->name, n)) {
947 continue;
948 }
949
950 pa_log_info("Setting description for sink %s to '%s'", sink->name, e->description);
951 pa_sink_set_description(sink, e->description);
952 }
953 pa_xfree(n);
954 }
955 else if ((n = get_name(name, "source:"))) {
956 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
957 if (!pa_streq(source->name, n)) {
958 continue;
959 }
960
961 if (source->monitor_of) {
962 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
963 continue;
964 }
965
966 pa_log_info("Setting description for source %s to '%s'", source->name, e->description);
967 pa_source_set_description(source, e->description);
968 }
969 pa_xfree(n);
970 }
971 }
972
973
974 #define EXT_VERSION 1
975
976 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
977 struct userdata *u;
978 uint32_t command;
979 pa_tagstruct *reply = NULL;
980
981 pa_assert(p);
982 pa_assert(m);
983 pa_assert(c);
984 pa_assert(t);
985
986 u = m->userdata;
987
988 if (pa_tagstruct_getu32(t, &command) < 0)
989 goto fail;
990
991 reply = pa_tagstruct_new(NULL, 0);
992 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
993 pa_tagstruct_putu32(reply, tag);
994
995 switch (command) {
996 case SUBCOMMAND_TEST: {
997 if (!pa_tagstruct_eof(t))
998 goto fail;
999
1000 pa_tagstruct_putu32(reply, EXT_VERSION);
1001 break;
1002 }
1003
1004 case SUBCOMMAND_READ: {
1005 pa_datum key;
1006 pa_bool_t done;
1007
1008 if (!pa_tagstruct_eof(t))
1009 goto fail;
1010
1011 done = !pa_database_first(u->database, &key, NULL);
1012
1013 while (!done) {
1014 pa_datum next_key;
1015 struct entry *e;
1016 char *name;
1017
1018 done = !pa_database_next(u->database, &key, &next_key, NULL);
1019
1020 name = pa_xstrndup(key.data, key.size);
1021 pa_datum_free(&key);
1022
1023 if ((e = read_entry(u, name))) {
1024 uint32_t idx;
1025 char *devname;
1026 pa_bool_t available = FALSE;
1027
1028 if ((devname = get_name(name, "sink:"))) {
1029 pa_sink* s;
1030 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1031 if (strcmp(s->name, devname) == 0) {
1032 available = TRUE;
1033 break;
1034 }
1035 }
1036 pa_xfree(devname);
1037 } else if ((devname = get_name(name, "source:"))) {
1038 pa_source* s;
1039 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1040 if (strcmp(s->name, devname) == 0) {
1041 available = TRUE;
1042 break;
1043 }
1044 }
1045 pa_xfree(devname);
1046 }
1047
1048 pa_tagstruct_puts(reply, name);
1049 pa_tagstruct_puts(reply, e->description);
1050 pa_tagstruct_puts(reply, e->icon);
1051 pa_tagstruct_put_boolean(reply, available);
1052 pa_tagstruct_putu32(reply, NUM_ROLES);
1053
1054 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1055 pa_tagstruct_puts(reply, role_names[i]);
1056 pa_tagstruct_putu32(reply, e->priority[i]);
1057 }
1058
1059 pa_xfree(e);
1060 }
1061
1062 pa_xfree(name);
1063
1064 key = next_key;
1065 }
1066
1067 break;
1068 }
1069
1070 case SUBCOMMAND_RENAME: {
1071
1072 struct entry *e;
1073 const char *device, *description;
1074
1075 if (pa_tagstruct_gets(t, &device) < 0 ||
1076 pa_tagstruct_gets(t, &description) < 0)
1077 goto fail;
1078
1079 if (!device || !*device || !description || !*description)
1080 goto fail;
1081
1082 if ((e = read_entry(u, device))) {
1083 pa_datum key, data;
1084
1085 pa_strlcpy(e->description, description, sizeof(e->description));
1086 e->user_set_description = TRUE;
1087
1088 key.data = (char *) device;
1089 key.size = strlen(device);
1090
1091 data.data = e;
1092 data.size = sizeof(*e);
1093
1094 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1095 apply_entry(u, device, e);
1096
1097 trigger_save(u);
1098 }
1099 else
1100 pa_log_warn("Could not save device");
1101
1102 pa_xfree(e);
1103 }
1104 else
1105 pa_log_warn("Could not rename device %s, no entry in database", device);
1106
1107 break;
1108 }
1109
1110 case SUBCOMMAND_DELETE:
1111
1112 while (!pa_tagstruct_eof(t)) {
1113 const char *name;
1114 pa_datum key;
1115
1116 if (pa_tagstruct_gets(t, &name) < 0)
1117 goto fail;
1118
1119 key.data = (char*) name;
1120 key.size = strlen(name);
1121
1122 /** @todo: Reindex the priorities */
1123 pa_database_unset(u->database, &key);
1124 }
1125
1126 trigger_save(u);
1127
1128 break;
1129
1130 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1131
1132 pa_bool_t enable;
1133
1134 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1135 goto fail;
1136
1137 if ((u->do_routing = enable)) {
1138 /* Update our caches */
1139 update_highest_priority_device_indexes(u, "sink:", NULL);
1140 update_highest_priority_device_indexes(u, "source:", NULL);
1141 }
1142
1143 break;
1144 }
1145
1146 case SUBCOMMAND_REORDER: {
1147
1148 const char *role;
1149 struct entry *e;
1150 uint32_t role_index, n_devices;
1151 pa_datum key, data;
1152 pa_bool_t done, sink_mode = TRUE;
1153 struct device_t { uint32_t prio; char *device; };
1154 struct device_t *device;
1155 struct device_t **devices;
1156 uint32_t i, idx, offset;
1157 pa_hashmap *h;
1158 /*void *state;*/
1159 pa_bool_t first;
1160
1161 if (pa_tagstruct_gets(t, &role) < 0 ||
1162 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1163 n_devices < 1)
1164 goto fail;
1165
1166 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1167 goto fail;
1168
1169 /* Cycle through the devices given and make sure they exist */
1170 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1171 first = TRUE;
1172 idx = 0;
1173 for (i = 0; i < n_devices; ++i) {
1174 const char *s;
1175 if (pa_tagstruct_gets(t, &s) < 0) {
1176 while ((device = pa_hashmap_steal_first(h))) {
1177 pa_xfree(device->device);
1178 pa_xfree(device);
1179 }
1180
1181 pa_hashmap_free(h, NULL, NULL);
1182 pa_log_error("Protocol error on reorder");
1183 goto fail;
1184 }
1185
1186 /* Ensure this is a valid entry */
1187 if (!(e = read_entry(u, s))) {
1188 while ((device = pa_hashmap_steal_first(h))) {
1189 pa_xfree(device->device);
1190 pa_xfree(device);
1191 }
1192
1193 pa_hashmap_free(h, NULL, NULL);
1194 pa_log_error("Client specified an unknown device in it's reorder list.");
1195 goto fail;
1196 }
1197 pa_xfree(e);
1198
1199 if (first) {
1200 first = FALSE;
1201 sink_mode = (0 == strncmp("sink:", s, 5));
1202 } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
1203 || (!sink_mode && 0 != strncmp("source:", s, 7)))
1204 {
1205 while ((device = pa_hashmap_steal_first(h))) {
1206 pa_xfree(device->device);
1207 pa_xfree(device);
1208 }
1209
1210 pa_hashmap_free(h, NULL, NULL);
1211 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1212 goto fail;
1213 }
1214
1215 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1216 device = pa_xnew(struct device_t, 1);
1217 device->device = pa_xstrdup(s);
1218 if (pa_hashmap_put(h, device->device, device) == 0) {
1219 device->prio = idx;
1220 idx++;
1221 } else {
1222 pa_xfree(device->device);
1223 pa_xfree(device);
1224 }
1225 }
1226
1227 /*pa_log_debug("Hashmap contents (received from client)");
1228 PA_HASHMAP_FOREACH(device, h, state) {
1229 pa_log_debug(" - %s (%d)", device->device, device->prio);
1230 }*/
1231
1232 /* Now cycle through our list and add all the devices.
1233 This has the effect of addign in any in our DB,
1234 not specified in the device list (and thus will be
1235 tacked on at the end) */
1236 offset = idx;
1237 done = !pa_database_first(u->database, &key, NULL);
1238
1239 while (!done && idx < 256) {
1240 pa_datum next_key;
1241
1242 done = !pa_database_next(u->database, &key, &next_key, NULL);
1243
1244 device = pa_xnew(struct device_t, 1);
1245 device->device = pa_xstrndup(key.data, key.size);
1246 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1247 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1248
1249 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1250 if (pa_hashmap_put(h, device->device, device) == 0
1251 && (e = read_entry(u, device->device))) {
1252 /* We add offset on to the existing priorirty so that when we order, the
1253 existing entries are always lower priority than the new ones. */
1254 device->prio = (offset + e->priority[role_index]);
1255 pa_xfree(e);
1256 }
1257 else {
1258 pa_xfree(device->device);
1259 pa_xfree(device);
1260 }
1261 } else {
1262 pa_xfree(device->device);
1263 pa_xfree(device);
1264 }
1265
1266 pa_datum_free(&key);
1267
1268 key = next_key;
1269 }
1270
1271 /*pa_log_debug("Hashmap contents (combined with database)");
1272 PA_HASHMAP_FOREACH(device, h, state) {
1273 pa_log_debug(" - %s (%d)", device->device, device->prio);
1274 }*/
1275
1276 /* Now we put all the entries in a simple list for sorting it. */
1277 n_devices = pa_hashmap_size(h);
1278 devices = pa_xnew(struct device_t *, n_devices);
1279 idx = 0;
1280 while ((device = pa_hashmap_steal_first(h))) {
1281 devices[idx++] = device;
1282 }
1283 pa_hashmap_free(h, NULL, NULL);
1284
1285 /* Simple bubble sort */
1286 for (i = 0; i < n_devices; ++i) {
1287 for (uint32_t j = i; j < n_devices; ++j) {
1288 if (devices[i]->prio > devices[j]->prio) {
1289 struct device_t *tmp;
1290 tmp = devices[i];
1291 devices[i] = devices[j];
1292 devices[j] = tmp;
1293 }
1294 }
1295 }
1296
1297 /*pa_log_debug("Sorted device list");
1298 for (i = 0; i < n_devices; ++i) {
1299 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1300 }*/
1301
1302 /* Go through in order and write the new entry and cleanup our own list */
1303 idx = 1;
1304 first = TRUE;
1305 for (i = 0; i < n_devices; ++i) {
1306 if ((e = read_entry(u, devices[i]->device))) {
1307 if (e->priority[role_index] == idx)
1308 idx++;
1309 else {
1310 e->priority[role_index] = idx;
1311
1312 key.data = (char *) devices[i]->device;
1313 key.size = strlen(devices[i]->device);
1314
1315 data.data = e;
1316 data.size = sizeof(*e);
1317
1318 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1319 first = FALSE;
1320 idx++;
1321 }
1322 }
1323
1324 pa_xfree(e);
1325 }
1326 pa_xfree(devices[i]->device);
1327 pa_xfree(devices[i]);
1328 }
1329
1330 if (!first) {
1331 trigger_save(u);
1332
1333 if (sink_mode)
1334 route_sink_inputs(u, NULL);
1335 else
1336 route_source_outputs(u, NULL);
1337 }
1338
1339 break;
1340 }
1341
1342 case SUBCOMMAND_SUBSCRIBE: {
1343
1344 pa_bool_t enabled;
1345
1346 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1347 !pa_tagstruct_eof(t))
1348 goto fail;
1349
1350 if (enabled)
1351 pa_idxset_put(u->subscribed, c, NULL);
1352 else
1353 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1354
1355 break;
1356 }
1357
1358 default:
1359 goto fail;
1360 }
1361
1362 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1363 return 0;
1364
1365 fail:
1366
1367 if (reply)
1368 pa_tagstruct_free(reply);
1369
1370 return -1;
1371 }
1372
1373 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1374 pa_assert(p);
1375 pa_assert(c);
1376 pa_assert(u);
1377
1378 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1379 return PA_HOOK_OK;
1380 }
1381
1382 int pa__init(pa_module*m) {
1383 pa_modargs *ma = NULL;
1384 struct userdata *u;
1385 char *fname;
1386 pa_sink *sink;
1387 pa_source *source;
1388 uint32_t idx;
1389 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1390
1391 pa_assert(m);
1392
1393 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1394 pa_log("Failed to parse module arguments");
1395 goto fail;
1396 }
1397
1398 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1399 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1400 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1401 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1402 goto fail;
1403 }
1404
1405 m->userdata = u = pa_xnew0(struct userdata, 1);
1406 u->core = m->core;
1407 u->module = m;
1408 u->do_routing = do_routing;
1409 u->on_hotplug = on_hotplug;
1410 u->on_rescue = on_rescue;
1411 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1412
1413 u->protocol = pa_native_protocol_get(m->core);
1414 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1415
1416 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);
1417
1418 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);
1419
1420 /* Used to handle device description management */
1421 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);
1422 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);
1423
1424 /* The following slots are used to deal with routing */
1425 /* A little bit later than module-stream-restore, module-intended-roles */
1426 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);
1427 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);
1428
1429 if (on_hotplug) {
1430 /* A little bit later than module-stream-restore, module-intended-roles */
1431 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);
1432 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);
1433 }
1434
1435 if (on_rescue) {
1436 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1437 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);
1438 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);
1439 }
1440
1441 if (!(fname = pa_state_path("device-manager", TRUE)))
1442 goto fail;
1443
1444 if (!(u->database = pa_database_open(fname, TRUE))) {
1445 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1446 pa_xfree(fname);
1447 goto fail;
1448 }
1449
1450 pa_log_info("Sucessfully opened database file '%s'.", fname);
1451 pa_xfree(fname);
1452
1453 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1454 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1455 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1456
1457 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1458 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1459
1460 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1461 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1462 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1463 }
1464
1465 route_sink_inputs(u, NULL);
1466 route_source_outputs(u, NULL);
1467
1468 #ifdef DUMP_DATABASE
1469 dump_database(u);
1470 #endif
1471
1472 pa_modargs_free(ma);
1473 return 0;
1474
1475 fail:
1476 pa__done(m);
1477
1478 if (ma)
1479 pa_modargs_free(ma);
1480
1481 return -1;
1482 }
1483
1484 void pa__done(pa_module*m) {
1485 struct userdata* u;
1486
1487 pa_assert(m);
1488
1489 if (!(u = m->userdata))
1490 return;
1491
1492 if (u->subscription)
1493 pa_subscription_free(u->subscription);
1494
1495 if (u->sink_new_hook_slot)
1496 pa_hook_slot_free(u->sink_new_hook_slot);
1497 if (u->source_new_hook_slot)
1498 pa_hook_slot_free(u->source_new_hook_slot);
1499
1500 if (u->sink_input_new_hook_slot)
1501 pa_hook_slot_free(u->sink_input_new_hook_slot);
1502 if (u->source_output_new_hook_slot)
1503 pa_hook_slot_free(u->source_output_new_hook_slot);
1504
1505 if (u->sink_put_hook_slot)
1506 pa_hook_slot_free(u->sink_put_hook_slot);
1507 if (u->source_put_hook_slot)
1508 pa_hook_slot_free(u->source_put_hook_slot);
1509
1510 if (u->sink_unlink_hook_slot)
1511 pa_hook_slot_free(u->sink_unlink_hook_slot);
1512 if (u->source_unlink_hook_slot)
1513 pa_hook_slot_free(u->source_unlink_hook_slot);
1514
1515 if (u->save_time_event)
1516 u->core->mainloop->time_free(u->save_time_event);
1517
1518 if (u->database)
1519 pa_database_close(u->database);
1520
1521 if (u->protocol) {
1522 pa_native_protocol_remove_ext(u->protocol, m);
1523 pa_native_protocol_unref(u->protocol);
1524 }
1525
1526 if (u->subscribed)
1527 pa_idxset_free(u->subscribed, NULL, NULL);
1528
1529 pa_xfree(u);
1530 }