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