]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
capture: Add the passthrough format negotiation to capture streams.
[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 if (si->save_sink)
522 return;
523
524 /* Skip this if it is already in the process of being moved anyway */
525 if (!si->sink)
526 return;
527
528 /* It might happen that a stream and a sink are set up at the
529 same time, in which case we want to make sure we don't
530 interfere with that */
531 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
532 return;
533
534 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
535 role_index = get_role_index("none");
536 else
537 role_index = get_role_index(role);
538
539 if (PA_INVALID_INDEX == role_index)
540 return;
541
542 device_index = u->preferred_sinks[role_index];
543 if (PA_INVALID_INDEX == device_index)
544 return;
545
546 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
547 return;
548
549 if (si->sink != sink)
550 pa_sink_input_move_to(si, sink, FALSE);
551 }
552
553 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
554 pa_sink_input *si;
555 uint32_t idx;
556
557 pa_assert(u);
558
559 if (!u->do_routing)
560 return PA_HOOK_OK;
561
562 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
563
564 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
565 route_sink_input(u, si);
566 }
567
568 return PA_HOOK_OK;
569 }
570
571 static void route_source_output(struct userdata *u, pa_source_output *so) {
572 const char *role;
573 uint32_t role_index, device_index;
574 pa_source *source;
575
576 pa_assert(u);
577 pa_assert(u->do_routing);
578
579 if (so->save_source)
580 return;
581
582 if (so->direct_on_input)
583 return;
584
585 /* Skip this if it is already in the process of being moved anyway */
586 if (!so->source)
587 return;
588
589 /* It might happen that a stream and a source are set up at the
590 same time, in which case we want to make sure we don't
591 interfere with that */
592 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
593 return;
594
595 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
596 role_index = get_role_index("none");
597 else
598 role_index = get_role_index(role);
599
600 if (PA_INVALID_INDEX == role_index)
601 return;
602
603 device_index = u->preferred_sources[role_index];
604 if (PA_INVALID_INDEX == device_index)
605 return;
606
607 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
608 return;
609
610 if (so->source != source)
611 pa_source_output_move_to(so, source, FALSE);
612 }
613
614 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
615 pa_source_output *so;
616 uint32_t idx;
617
618 pa_assert(u);
619
620 if (!u->do_routing)
621 return PA_HOOK_OK;
622
623 update_highest_priority_device_indexes(u, "source:", ignore_source);
624
625 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
626 route_source_output(u, so);
627 }
628
629 return PA_HOOK_OK;
630 }
631
632 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
633 struct userdata *u = userdata;
634 struct entry entry, *old = NULL;
635 char *name = NULL;
636 pa_datum key, data;
637
638 pa_assert(c);
639 pa_assert(u);
640
641 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
642 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
643 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
644 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
645
646 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
647 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
648 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
649 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
650 return;
651
652 pa_zero(entry);
653 entry.version = ENTRY_VERSION;
654
655 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
656 pa_sink_input *si;
657
658 if (!u->do_routing)
659 return;
660 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
661 return;
662
663 /* The role may change mid-stream, so we reroute */
664 route_sink_input(u, si);
665
666 return;
667 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
668 pa_source_output *so;
669
670 if (!u->do_routing)
671 return;
672 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
673 return;
674
675 /* The role may change mid-stream, so we reroute */
676 route_source_output(u, so);
677
678 return;
679 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
680 pa_sink *sink;
681
682 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
683 return;
684
685 name = pa_sprintf_malloc("sink:%s", sink->name);
686
687 old = load_or_initialize_entry(u, &entry, name, "sink:");
688
689 if (!entry.user_set_description)
690 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
691 else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
692 /* Warning: If two modules fight over the description, this could cause an infinite loop.
693 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
694 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
695 the description, this will fail... */
696 pa_sink_set_description(sink, entry.description);
697 }
698
699 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
700
701 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
702 pa_source *source;
703
704 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
705
706 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
707 return;
708
709 if (source->monitor_of)
710 return;
711
712 name = pa_sprintf_malloc("source:%s", source->name);
713
714 old = load_or_initialize_entry(u, &entry, name, "source:");
715
716 if (!entry.user_set_description)
717 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
718 else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
719 /* Warning: If two modules fight over the description, this could cause an infinite loop.
720 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
721 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
722 the description, this will fail... */
723 pa_source_set_description(source, entry.description);
724 }
725
726 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
727 }
728
729 pa_assert(name);
730
731 if (old) {
732
733 if (entries_equal(old, &entry)) {
734 pa_xfree(old);
735 pa_xfree(name);
736
737 return;
738 }
739
740 pa_xfree(old);
741 }
742
743 key.data = name;
744 key.size = strlen(name);
745
746 data.data = &entry;
747 data.size = sizeof(entry);
748
749 pa_log_info("Storing device %s.", name);
750
751 if (pa_database_set(u->database, &key, &data, TRUE) == 0)
752 trigger_save(u);
753 else
754 pa_log_warn("Could not save device");;
755
756 pa_xfree(name);
757 }
758
759 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
760 char *name;
761 struct entry *e;
762
763 pa_assert(c);
764 pa_assert(new_data);
765 pa_assert(u);
766
767 name = pa_sprintf_malloc("sink:%s", new_data->name);
768
769 if ((e = read_entry(u, name))) {
770 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
771 pa_log_info("Restoring description for sink %s.", new_data->name);
772 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
773 }
774
775 pa_xfree(e);
776 }
777
778 pa_xfree(name);
779
780 return PA_HOOK_OK;
781 }
782
783 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
784 char *name;
785 struct entry *e;
786
787 pa_assert(c);
788 pa_assert(new_data);
789 pa_assert(u);
790
791 name = pa_sprintf_malloc("source:%s", new_data->name);
792
793 if ((e = read_entry(u, name))) {
794 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
795 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
796 pa_log_info("Restoring description for source %s.", new_data->name);
797 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
798 }
799
800 pa_xfree(e);
801 }
802
803 pa_xfree(name);
804
805 return PA_HOOK_OK;
806 }
807
808 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
809 const char *role;
810 uint32_t role_index;
811
812 pa_assert(c);
813 pa_assert(new_data);
814 pa_assert(u);
815
816 if (!u->do_routing)
817 return PA_HOOK_OK;
818
819 if (new_data->sink)
820 pa_log_debug("Not restoring device for stream because already set.");
821 else {
822 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
823 role_index = get_role_index("none");
824 else
825 role_index = get_role_index(role);
826
827 if (PA_INVALID_INDEX != role_index) {
828 uint32_t device_index;
829
830 device_index = u->preferred_sinks[role_index];
831 if (PA_INVALID_INDEX != device_index) {
832 pa_sink *sink;
833
834 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
835 if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
836 pa_log_debug("Not restoring device for stream because no supported format was found");
837 }
838 }
839 }
840 }
841
842 return PA_HOOK_OK;
843 }
844
845 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
846 const char *role;
847 uint32_t role_index;
848
849 pa_assert(c);
850 pa_assert(new_data);
851 pa_assert(u);
852
853 if (!u->do_routing)
854 return PA_HOOK_OK;
855
856 if (new_data->direct_on_input)
857 return PA_HOOK_OK;
858
859 if (new_data->source)
860 pa_log_debug("Not restoring device for stream because already set.");
861 else {
862 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
863 role_index = get_role_index("none");
864 else
865 role_index = get_role_index(role);
866
867 if (PA_INVALID_INDEX != role_index) {
868 uint32_t device_index;
869
870 device_index = u->preferred_sources[role_index];
871 if (PA_INVALID_INDEX != device_index) {
872 pa_source *source;
873
874 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
875 if (!pa_source_output_new_data_set_source(new_data, source, FALSE))
876 pa_log_debug("Not restoring device for stream because no supported format was found");
877 }
878 }
879 }
880
881 return PA_HOOK_OK;
882 }
883
884
885 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
886 pa_assert(c);
887 pa_assert(u);
888 pa_assert(u->core == c);
889 pa_assert(u->on_hotplug);
890
891 notify_subscribers(u);
892
893 return route_sink_inputs(u, NULL);
894 }
895
896 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
897 pa_assert(c);
898 pa_assert(u);
899 pa_assert(u->core == c);
900 pa_assert(u->on_hotplug);
901
902 notify_subscribers(u);
903
904 return route_source_outputs(u, NULL);
905 }
906
907 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
908 pa_assert(c);
909 pa_assert(sink);
910 pa_assert(u);
911 pa_assert(u->core == c);
912 pa_assert(u->on_rescue);
913
914 /* There's no point in doing anything if the core is shut down anyway */
915 if (c->state == PA_CORE_SHUTDOWN)
916 return PA_HOOK_OK;
917
918 notify_subscribers(u);
919
920 return route_sink_inputs(u, sink);
921 }
922
923 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
924 pa_assert(c);
925 pa_assert(source);
926 pa_assert(u);
927 pa_assert(u->core == c);
928 pa_assert(u->on_rescue);
929
930 /* There's no point in doing anything if the core is shut down anyway */
931 if (c->state == PA_CORE_SHUTDOWN)
932 return PA_HOOK_OK;
933
934 notify_subscribers(u);
935
936 return route_source_outputs(u, source);
937 }
938
939
940 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
941 uint32_t idx;
942 char *n;
943
944 pa_assert(u);
945 pa_assert(name);
946 pa_assert(e);
947
948 if (!e->user_set_description)
949 return;
950
951 if ((n = get_name(name, "sink:"))) {
952 pa_sink *s;
953 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
954 if (!pa_streq(s->name, n)) {
955 continue;
956 }
957
958 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
959 pa_sink_set_description(s, e->description);
960 }
961 pa_xfree(n);
962 }
963 else if ((n = get_name(name, "source:"))) {
964 pa_source *s;
965 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
966 if (!pa_streq(s->name, n)) {
967 continue;
968 }
969
970 if (s->monitor_of) {
971 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
972 continue;
973 }
974
975 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
976 pa_source_set_description(s, e->description);
977 }
978 pa_xfree(n);
979 }
980 }
981
982
983 #define EXT_VERSION 1
984
985 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
986 struct userdata *u;
987 uint32_t command;
988 pa_tagstruct *reply = NULL;
989
990 pa_assert(p);
991 pa_assert(m);
992 pa_assert(c);
993 pa_assert(t);
994
995 u = m->userdata;
996
997 if (pa_tagstruct_getu32(t, &command) < 0)
998 goto fail;
999
1000 reply = pa_tagstruct_new(NULL, 0);
1001 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1002 pa_tagstruct_putu32(reply, tag);
1003
1004 switch (command) {
1005 case SUBCOMMAND_TEST: {
1006 if (!pa_tagstruct_eof(t))
1007 goto fail;
1008
1009 pa_tagstruct_putu32(reply, EXT_VERSION);
1010 break;
1011 }
1012
1013 case SUBCOMMAND_READ: {
1014 pa_datum key;
1015 pa_bool_t done;
1016
1017 if (!pa_tagstruct_eof(t))
1018 goto fail;
1019
1020 done = !pa_database_first(u->database, &key, NULL);
1021
1022 while (!done) {
1023 pa_datum next_key;
1024 struct entry *e;
1025 char *name;
1026
1027 done = !pa_database_next(u->database, &key, &next_key, NULL);
1028
1029 name = pa_xstrndup(key.data, key.size);
1030 pa_datum_free(&key);
1031
1032 if ((e = read_entry(u, name))) {
1033 uint32_t idx;
1034 char *device_name;
1035 uint32_t found_index = PA_INVALID_INDEX;
1036
1037 if ((device_name = get_name(name, "sink:"))) {
1038 pa_sink* s;
1039 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1040 if (strcmp(s->name, device_name) == 0) {
1041 found_index = s->index;
1042 break;
1043 }
1044 }
1045 pa_xfree(device_name);
1046 } else if ((device_name = get_name(name, "source:"))) {
1047 pa_source* s;
1048 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1049 if (strcmp(s->name, device_name) == 0) {
1050 found_index = s->index;
1051 break;
1052 }
1053 }
1054 pa_xfree(device_name);
1055 }
1056
1057 pa_tagstruct_puts(reply, name);
1058 pa_tagstruct_puts(reply, e->description);
1059 pa_tagstruct_puts(reply, e->icon);
1060 pa_tagstruct_putu32(reply, found_index);
1061 pa_tagstruct_putu32(reply, NUM_ROLES);
1062
1063 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1064 pa_tagstruct_puts(reply, role_names[i]);
1065 pa_tagstruct_putu32(reply, e->priority[i]);
1066 }
1067
1068 pa_xfree(e);
1069 }
1070
1071 pa_xfree(name);
1072
1073 key = next_key;
1074 }
1075
1076 break;
1077 }
1078
1079 case SUBCOMMAND_RENAME: {
1080
1081 struct entry *e;
1082 const char *device, *description;
1083
1084 if (pa_tagstruct_gets(t, &device) < 0 ||
1085 pa_tagstruct_gets(t, &description) < 0)
1086 goto fail;
1087
1088 if (!device || !*device || !description || !*description)
1089 goto fail;
1090
1091 if ((e = read_entry(u, device))) {
1092 pa_datum key, data;
1093
1094 pa_strlcpy(e->description, description, sizeof(e->description));
1095 e->user_set_description = TRUE;
1096
1097 key.data = (char *) device;
1098 key.size = strlen(device);
1099
1100 data.data = e;
1101 data.size = sizeof(*e);
1102
1103 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1104 apply_entry(u, device, e);
1105
1106 trigger_save(u);
1107 }
1108 else
1109 pa_log_warn("Could not save device");
1110
1111 pa_xfree(e);
1112 }
1113 else
1114 pa_log_warn("Could not rename device %s, no entry in database", device);
1115
1116 break;
1117 }
1118
1119 case SUBCOMMAND_DELETE:
1120
1121 while (!pa_tagstruct_eof(t)) {
1122 const char *name;
1123 pa_datum key;
1124
1125 if (pa_tagstruct_gets(t, &name) < 0)
1126 goto fail;
1127
1128 key.data = (char*) name;
1129 key.size = strlen(name);
1130
1131 /** @todo: Reindex the priorities */
1132 pa_database_unset(u->database, &key);
1133 }
1134
1135 trigger_save(u);
1136
1137 break;
1138
1139 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1140
1141 pa_bool_t enable;
1142
1143 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1144 goto fail;
1145
1146 if ((u->do_routing = enable)) {
1147 /* Update our caches */
1148 update_highest_priority_device_indexes(u, "sink:", NULL);
1149 update_highest_priority_device_indexes(u, "source:", NULL);
1150 }
1151
1152 break;
1153 }
1154
1155 case SUBCOMMAND_REORDER: {
1156
1157 const char *role;
1158 struct entry *e;
1159 uint32_t role_index, n_devices;
1160 pa_datum key, data;
1161 pa_bool_t done, sink_mode = TRUE;
1162 struct device_t { uint32_t prio; char *device; };
1163 struct device_t *device;
1164 struct device_t **devices;
1165 uint32_t i, idx, offset;
1166 pa_hashmap *h;
1167 /*void *state;*/
1168 pa_bool_t first;
1169
1170 if (pa_tagstruct_gets(t, &role) < 0 ||
1171 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1172 n_devices < 1)
1173 goto fail;
1174
1175 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1176 goto fail;
1177
1178 /* Cycle through the devices given and make sure they exist */
1179 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1180 first = TRUE;
1181 idx = 0;
1182 for (i = 0; i < n_devices; ++i) {
1183 const char *s;
1184 if (pa_tagstruct_gets(t, &s) < 0) {
1185 while ((device = pa_hashmap_steal_first(h))) {
1186 pa_xfree(device->device);
1187 pa_xfree(device);
1188 }
1189
1190 pa_hashmap_free(h, NULL, NULL);
1191 pa_log_error("Protocol error on reorder");
1192 goto fail;
1193 }
1194
1195 /* Ensure this is a valid entry */
1196 if (!(e = read_entry(u, s))) {
1197 while ((device = pa_hashmap_steal_first(h))) {
1198 pa_xfree(device->device);
1199 pa_xfree(device);
1200 }
1201
1202 pa_hashmap_free(h, NULL, NULL);
1203 pa_log_error("Client specified an unknown device in it's reorder list.");
1204 goto fail;
1205 }
1206 pa_xfree(e);
1207
1208 if (first) {
1209 first = FALSE;
1210 sink_mode = (0 == strncmp("sink:", s, 5));
1211 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1212 while ((device = pa_hashmap_steal_first(h))) {
1213 pa_xfree(device->device);
1214 pa_xfree(device);
1215 }
1216
1217 pa_hashmap_free(h, NULL, NULL);
1218 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1219 goto fail;
1220 }
1221
1222 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1223 device = pa_xnew(struct device_t, 1);
1224 device->device = pa_xstrdup(s);
1225 if (pa_hashmap_put(h, device->device, device) == 0) {
1226 device->prio = idx;
1227 idx++;
1228 } else {
1229 pa_xfree(device->device);
1230 pa_xfree(device);
1231 }
1232 }
1233
1234 /*pa_log_debug("Hashmap contents (received from client)");
1235 PA_HASHMAP_FOREACH(device, h, state) {
1236 pa_log_debug(" - %s (%d)", device->device, device->prio);
1237 }*/
1238
1239 /* Now cycle through our list and add all the devices.
1240 This has the effect of addign in any in our DB,
1241 not specified in the device list (and thus will be
1242 tacked on at the end) */
1243 offset = idx;
1244 done = !pa_database_first(u->database, &key, NULL);
1245
1246 while (!done && idx < 256) {
1247 pa_datum next_key;
1248
1249 done = !pa_database_next(u->database, &key, &next_key, NULL);
1250
1251 device = pa_xnew(struct device_t, 1);
1252 device->device = pa_xstrndup(key.data, key.size);
1253 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1254 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1255
1256 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1257 if (pa_hashmap_put(h, device->device, device) == 0
1258 && (e = read_entry(u, device->device))) {
1259 /* We add offset on to the existing priorirty so that when we order, the
1260 existing entries are always lower priority than the new ones. */
1261 device->prio = (offset + e->priority[role_index]);
1262 pa_xfree(e);
1263 }
1264 else {
1265 pa_xfree(device->device);
1266 pa_xfree(device);
1267 }
1268 } else {
1269 pa_xfree(device->device);
1270 pa_xfree(device);
1271 }
1272
1273 pa_datum_free(&key);
1274
1275 key = next_key;
1276 }
1277
1278 /*pa_log_debug("Hashmap contents (combined with database)");
1279 PA_HASHMAP_FOREACH(device, h, state) {
1280 pa_log_debug(" - %s (%d)", device->device, device->prio);
1281 }*/
1282
1283 /* Now we put all the entries in a simple list for sorting it. */
1284 n_devices = pa_hashmap_size(h);
1285 devices = pa_xnew(struct device_t *, n_devices);
1286 idx = 0;
1287 while ((device = pa_hashmap_steal_first(h))) {
1288 devices[idx++] = device;
1289 }
1290 pa_hashmap_free(h, NULL, NULL);
1291
1292 /* Simple bubble sort */
1293 for (i = 0; i < n_devices; ++i) {
1294 for (uint32_t j = i; j < n_devices; ++j) {
1295 if (devices[i]->prio > devices[j]->prio) {
1296 struct device_t *tmp;
1297 tmp = devices[i];
1298 devices[i] = devices[j];
1299 devices[j] = tmp;
1300 }
1301 }
1302 }
1303
1304 /*pa_log_debug("Sorted device list");
1305 for (i = 0; i < n_devices; ++i) {
1306 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1307 }*/
1308
1309 /* Go through in order and write the new entry and cleanup our own list */
1310 idx = 1;
1311 first = TRUE;
1312 for (i = 0; i < n_devices; ++i) {
1313 if ((e = read_entry(u, devices[i]->device))) {
1314 if (e->priority[role_index] == idx)
1315 idx++;
1316 else {
1317 e->priority[role_index] = idx;
1318
1319 key.data = (char *) devices[i]->device;
1320 key.size = strlen(devices[i]->device);
1321
1322 data.data = e;
1323 data.size = sizeof(*e);
1324
1325 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1326 first = FALSE;
1327 idx++;
1328 }
1329 }
1330
1331 pa_xfree(e);
1332 }
1333 pa_xfree(devices[i]->device);
1334 pa_xfree(devices[i]);
1335 }
1336
1337 if (!first) {
1338 trigger_save(u);
1339
1340 if (sink_mode)
1341 route_sink_inputs(u, NULL);
1342 else
1343 route_source_outputs(u, NULL);
1344 }
1345
1346 break;
1347 }
1348
1349 case SUBCOMMAND_SUBSCRIBE: {
1350
1351 pa_bool_t enabled;
1352
1353 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1354 !pa_tagstruct_eof(t))
1355 goto fail;
1356
1357 if (enabled)
1358 pa_idxset_put(u->subscribed, c, NULL);
1359 else
1360 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1361
1362 break;
1363 }
1364
1365 default:
1366 goto fail;
1367 }
1368
1369 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1370 return 0;
1371
1372 fail:
1373
1374 if (reply)
1375 pa_tagstruct_free(reply);
1376
1377 return -1;
1378 }
1379
1380 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1381 pa_assert(p);
1382 pa_assert(c);
1383 pa_assert(u);
1384
1385 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1386 return PA_HOOK_OK;
1387 }
1388
1389 struct prioritised_indexes {
1390 uint32_t index;
1391 int32_t priority;
1392 };
1393
1394 int pa__init(pa_module*m) {
1395 pa_modargs *ma = NULL;
1396 struct userdata *u;
1397 char *fname;
1398 pa_sink *sink;
1399 pa_source *source;
1400 uint32_t idx;
1401 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1402 uint32_t total_devices;
1403
1404 pa_assert(m);
1405
1406 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1407 pa_log("Failed to parse module arguments");
1408 goto fail;
1409 }
1410
1411 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1412 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1413 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1414 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1415 goto fail;
1416 }
1417
1418 m->userdata = u = pa_xnew0(struct userdata, 1);
1419 u->core = m->core;
1420 u->module = m;
1421 u->do_routing = do_routing;
1422 u->on_hotplug = on_hotplug;
1423 u->on_rescue = on_rescue;
1424 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1425
1426 u->protocol = pa_native_protocol_get(m->core);
1427 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1428
1429 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);
1430
1431 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);
1432
1433 /* Used to handle device description management */
1434 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);
1435 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);
1436
1437 /* The following slots are used to deal with routing */
1438 /* A little bit later than module-stream-restore, but before module-intended-roles */
1439 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);
1440 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);
1441
1442 if (on_hotplug) {
1443 /* A little bit later than module-stream-restore, but before module-intended-roles */
1444 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);
1445 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);
1446 }
1447
1448 if (on_rescue) {
1449 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1450 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);
1451 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);
1452 }
1453
1454 if (!(fname = pa_state_path("device-manager", TRUE)))
1455 goto fail;
1456
1457 if (!(u->database = pa_database_open(fname, TRUE))) {
1458 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1459 pa_xfree(fname);
1460 goto fail;
1461 }
1462
1463 pa_log_info("Successfully opened database file '%s'.", fname);
1464 pa_xfree(fname);
1465
1466 /* Attempt to inject the devices into the list in priority order */
1467 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1468 if (total_devices > 0 && total_devices < 128) {
1469 uint32_t i;
1470 struct prioritised_indexes p_i[128];
1471
1472 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1473 i = 0;
1474 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1475 pa_log_debug("Found sink index %u", sink->index);
1476 p_i[i ].index = sink->index;
1477 p_i[i++].priority = sink->priority;
1478 }
1479 /* Bubble sort it (only really useful for first time creation) */
1480 if (i > 1)
1481 for (uint32_t j = 0; j < i; ++j)
1482 for (uint32_t k = 0; k < i; ++k)
1483 if (p_i[j].priority > p_i[k].priority) {
1484 struct prioritised_indexes tmp_pi = p_i[k];
1485 p_i[k] = p_i[j];
1486 p_i[j] = tmp_pi;
1487 }
1488 /* Register it */
1489 for (uint32_t j = 0; j < i; ++j)
1490 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1491
1492
1493 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1494 i = 0;
1495 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1496 p_i[i ].index = source->index;
1497 p_i[i++].priority = source->priority;
1498 }
1499 /* Bubble sort it (only really useful for first time creation) */
1500 if (i > 1)
1501 for (uint32_t j = 0; j < i; ++j)
1502 for (uint32_t k = 0; k < i; ++k)
1503 if (p_i[j].priority > p_i[k].priority) {
1504 struct prioritised_indexes tmp_pi = p_i[k];
1505 p_i[k] = p_i[j];
1506 p_i[j] = tmp_pi;
1507 }
1508 /* Register it */
1509 for (uint32_t j = 0; j < i; ++j)
1510 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1511 }
1512 else if (total_devices > 0) {
1513 /* This user has a *lot* of devices... */
1514 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1515 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1516
1517 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1518 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1519 }
1520
1521 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1522 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1523 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1524 }
1525
1526 route_sink_inputs(u, NULL);
1527 route_source_outputs(u, NULL);
1528
1529 #ifdef DUMP_DATABASE
1530 dump_database(u);
1531 #endif
1532
1533 pa_modargs_free(ma);
1534 return 0;
1535
1536 fail:
1537 pa__done(m);
1538
1539 if (ma)
1540 pa_modargs_free(ma);
1541
1542 return -1;
1543 }
1544
1545 void pa__done(pa_module*m) {
1546 struct userdata* u;
1547
1548 pa_assert(m);
1549
1550 if (!(u = m->userdata))
1551 return;
1552
1553 if (u->subscription)
1554 pa_subscription_free(u->subscription);
1555
1556 if (u->sink_new_hook_slot)
1557 pa_hook_slot_free(u->sink_new_hook_slot);
1558 if (u->source_new_hook_slot)
1559 pa_hook_slot_free(u->source_new_hook_slot);
1560
1561 if (u->sink_input_new_hook_slot)
1562 pa_hook_slot_free(u->sink_input_new_hook_slot);
1563 if (u->source_output_new_hook_slot)
1564 pa_hook_slot_free(u->source_output_new_hook_slot);
1565
1566 if (u->sink_put_hook_slot)
1567 pa_hook_slot_free(u->sink_put_hook_slot);
1568 if (u->source_put_hook_slot)
1569 pa_hook_slot_free(u->source_put_hook_slot);
1570
1571 if (u->sink_unlink_hook_slot)
1572 pa_hook_slot_free(u->sink_unlink_hook_slot);
1573 if (u->source_unlink_hook_slot)
1574 pa_hook_slot_free(u->source_unlink_hook_slot);
1575
1576 if (u->connection_unlink_hook_slot)
1577 pa_hook_slot_free(u->connection_unlink_hook_slot);
1578
1579 if (u->save_time_event)
1580 u->core->mainloop->time_free(u->save_time_event);
1581
1582 if (u->database)
1583 pa_database_close(u->database);
1584
1585 if (u->protocol) {
1586 pa_native_protocol_remove_ext(u->protocol, m);
1587 pa_native_protocol_unref(u->protocol);
1588 }
1589
1590 if (u->subscribed)
1591 pa_idxset_free(u->subscribed, NULL, NULL);
1592
1593 pa_xfree(u);
1594 }