]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: debug and comments
[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");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE("This module does not take any arguments");
62
63 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
64
65 static const char* const valid_modargs[] = {
66 NULL
67 };
68
69 struct userdata {
70 pa_core *core;
71 pa_module *module;
72 pa_subscription *subscription;
73 pa_hook_slot
74 *sink_new_hook_slot,
75 *source_new_hook_slot,
76 *connection_unlink_hook_slot;
77 pa_time_event *save_time_event;
78 pa_database *database;
79
80 pa_native_protocol *protocol;
81 pa_idxset *subscribed;
82
83 pa_bool_t role_device_priority_routing;
84 pa_bool_t stream_restore_used;
85 pa_bool_t checked_stream_restore;
86 };
87
88 #define ENTRY_VERSION 1
89
90 #define NUM_ROLES 9
91 enum {
92 ROLE_NONE,
93 ROLE_VIDEO,
94 ROLE_MUSIC,
95 ROLE_GAME,
96 ROLE_EVENT,
97 ROLE_PHONE,
98 ROLE_ANIMATION,
99 ROLE_PRODUCTION,
100 ROLE_A11Y,
101 };
102
103 struct entry {
104 uint8_t version;
105 char description[PA_NAME_MAX];
106 uint32_t priority[NUM_ROLES];
107 } PA_GCC_PACKED;
108
109 enum {
110 SUBCOMMAND_TEST,
111 SUBCOMMAND_READ,
112 SUBCOMMAND_RENAME,
113 SUBCOMMAND_DELETE,
114 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
115 SUBCOMMAND_PREFER_DEVICE,
116 SUBCOMMAND_DEFER_DEVICE,
117 SUBCOMMAND_SUBSCRIBE,
118 SUBCOMMAND_EVENT
119 };
120
121 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
122 struct userdata *u = userdata;
123
124 pa_assert(a);
125 pa_assert(e);
126 pa_assert(u);
127
128 pa_assert(e == u->save_time_event);
129 u->core->mainloop->time_free(u->save_time_event);
130 u->save_time_event = NULL;
131
132 pa_database_sync(u->database);
133 pa_log_info("Synced.");
134 }
135
136 static struct entry* read_entry(struct userdata *u, const char *name) {
137 pa_datum key, data;
138 struct entry *e;
139
140 pa_assert(u);
141 pa_assert(name);
142
143 key.data = (char*) name;
144 key.size = strlen(name);
145
146 pa_zero(data);
147
148 if (!pa_database_get(u->database, &key, &data))
149 goto fail;
150
151 if (data.size != sizeof(struct entry)) {
152 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));
153 goto fail;
154 }
155
156 e = (struct entry*) data.data;
157
158 if (e->version != ENTRY_VERSION) {
159 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
160 goto fail;
161 }
162
163 if (!memchr(e->description, 0, sizeof(e->description))) {
164 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
165 goto fail;
166 }
167
168 return e;
169
170 fail:
171
172 pa_datum_free(&data);
173 return NULL;
174 }
175
176 static void trigger_save(struct userdata *u) {
177 pa_native_connection *c;
178 uint32_t idx;
179
180 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
181 pa_tagstruct *t;
182
183 t = pa_tagstruct_new(NULL, 0);
184 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
185 pa_tagstruct_putu32(t, 0);
186 pa_tagstruct_putu32(t, u->module->index);
187 pa_tagstruct_puts(t, u->module->name);
188 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
189
190 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
191 }
192
193 if (u->save_time_event)
194 return;
195
196 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
197 }
198
199 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
200 if (strncmp(a->description, b->description, sizeof(a->description)))
201 return FALSE;
202
203 return TRUE;
204 }
205
206 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
207 struct entry *old;
208
209 pa_assert(u);
210 pa_assert(entry);
211 pa_assert(name);
212 pa_assert(prefix);
213
214 if ((old = read_entry(u, name)))
215 *entry = *old;
216 else {
217 /* This is a new device, so make sure we write it's priority list correctly */
218 uint32_t max_priority[NUM_ROLES];
219 pa_datum key;
220 pa_bool_t done;
221
222 pa_zero(max_priority);
223 done = !pa_database_first(u->database, &key, NULL);
224
225 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
226 while (!done) {
227 pa_datum next_key;
228
229 done = !pa_database_next(u->database, &key, &next_key, NULL);
230
231 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
232 char *name2;
233 struct entry *e;
234
235 name2 = pa_xstrndup(key.data, key.size);
236
237 if ((e = read_entry(u, name2))) {
238 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
239 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
240 }
241
242 pa_xfree(e);
243 }
244
245 pa_xfree(name2);
246 }
247 pa_datum_free(&key);
248 key = next_key;
249 }
250
251 /* Actually initialise our entry now we've calculated it */
252 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
253 entry->priority[i] = max_priority[i] + 1;
254 }
255 }
256
257 return old;
258 }
259
260 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
261 struct userdata *u = userdata;
262 struct entry entry, *old = NULL;
263 char *name = NULL;
264 pa_datum key, data;
265
266 pa_assert(c);
267 pa_assert(u);
268
269 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
270 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
271 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
272 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
273 return;
274
275 pa_zero(entry);
276 entry.version = ENTRY_VERSION;
277
278 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
279 pa_sink *sink;
280
281 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
282 return;
283
284 name = pa_sprintf_malloc("sink:%s", sink->name);
285
286 old = load_or_initialize_entry(u, &entry, name, "sink:");
287
288 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
289
290 } else {
291 pa_source *source;
292
293 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
294
295 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
296 return;
297
298 if (source->monitor_of)
299 return;
300
301 name = pa_sprintf_malloc("source:%s", source->name);
302
303 old = load_or_initialize_entry(u, &entry, name, "source:");
304
305 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
306 }
307
308 if (old) {
309
310 if (entries_equal(old, &entry)) {
311 pa_xfree(old);
312 pa_xfree(name);
313 return;
314 }
315
316 pa_xfree(old);
317 }
318
319 key.data = name;
320 key.size = strlen(name);
321
322 data.data = &entry;
323 data.size = sizeof(entry);
324
325 pa_log_info("Storing device %s.", name);
326
327 pa_database_set(u->database, &key, &data, TRUE);
328
329 pa_xfree(name);
330
331 trigger_save(u);
332 }
333
334 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
335 char *name;
336 struct entry *e;
337
338 pa_assert(c);
339 pa_assert(new_data);
340 pa_assert(u);
341
342 name = pa_sprintf_malloc("sink:%s", new_data->name);
343
344 if ((e = read_entry(u, name))) {
345 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
346 pa_log_info("Restoring description for sink %s.", new_data->name);
347 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
348 }
349
350 pa_xfree(e);
351 }
352
353 pa_xfree(name);
354
355 return PA_HOOK_OK;
356 }
357
358 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
359 char *name;
360 struct entry *e;
361
362 pa_assert(c);
363 pa_assert(new_data);
364 pa_assert(u);
365
366 name = pa_sprintf_malloc("source:%s", new_data->name);
367
368 if ((e = read_entry(u, name))) {
369 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
370 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
371 pa_log_info("Restoring description for source %s.", new_data->name);
372 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
373 }
374
375 pa_xfree(e);
376 }
377
378 pa_xfree(name);
379
380 return PA_HOOK_OK;
381 }
382
383 static char *get_name(const char *key, const char *prefix) {
384 char *t;
385
386 if (strncmp(key, prefix, strlen(prefix)))
387 return NULL;
388
389 t = pa_xstrdup(key + strlen(prefix));
390 return t;
391 }
392
393 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
394 pa_sink *sink;
395 pa_source *source;
396 uint32_t idx;
397 char *n;
398
399 pa_assert(u);
400 pa_assert(name);
401 pa_assert(e);
402
403 if ((n = get_name(name, "sink:"))) {
404 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
405 if (!pa_streq(sink->name, n)) {
406 continue;
407 }
408
409 pa_log_info("Setting description for sink %s.", sink->name);
410 pa_sink_set_description(sink, e->description);
411 }
412 pa_xfree(n);
413 }
414 else if ((n = get_name(name, "source:"))) {
415 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
416 if (!pa_streq(source->name, n)) {
417 continue;
418 }
419
420 if (source->monitor_of) {
421 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
422 continue;
423 }
424
425 pa_log_info("Setting description for source %s.", source->name);
426 pa_source_set_description(source, e->description);
427 }
428 pa_xfree(n);
429 }
430 }
431
432
433 static uint32_t get_role_index(const char* role) {
434 pa_assert(role);
435
436 if (strcmp(role, "") == 0)
437 return ROLE_NONE;
438 if (strcmp(role, "video") == 0)
439 return ROLE_VIDEO;
440 if (strcmp(role, "music") == 0)
441 return ROLE_MUSIC;
442 if (strcmp(role, "game") == 0)
443 return ROLE_GAME;
444 if (strcmp(role, "event") == 0)
445 return ROLE_EVENT;
446 if (strcmp(role, "phone") == 0)
447 return ROLE_PHONE;
448 if (strcmp(role, "animation") == 0)
449 return ROLE_ANIMATION;
450 if (strcmp(role, "production") == 0)
451 return ROLE_PRODUCTION;
452 if (strcmp(role, "a11y") == 0)
453 return ROLE_A11Y;
454 return PA_INVALID_INDEX;
455 }
456
457 #define EXT_VERSION 1
458
459 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
460 struct userdata *u;
461 uint32_t command;
462 pa_tagstruct *reply = NULL;
463
464 pa_assert(p);
465 pa_assert(m);
466 pa_assert(c);
467 pa_assert(t);
468
469 u = m->userdata;
470
471 if (pa_tagstruct_getu32(t, &command) < 0)
472 goto fail;
473
474 reply = pa_tagstruct_new(NULL, 0);
475 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
476 pa_tagstruct_putu32(reply, tag);
477
478 switch (command) {
479 case SUBCOMMAND_TEST: {
480 if (!pa_tagstruct_eof(t))
481 goto fail;
482
483 pa_tagstruct_putu32(reply, EXT_VERSION);
484 break;
485 }
486
487 case SUBCOMMAND_READ: {
488 pa_datum key;
489 pa_bool_t done;
490
491 if (!pa_tagstruct_eof(t))
492 goto fail;
493
494 done = !pa_database_first(u->database, &key, NULL);
495
496 while (!done) {
497 pa_datum next_key;
498 struct entry *e;
499 char *name;
500
501 done = !pa_database_next(u->database, &key, &next_key, NULL);
502
503 name = pa_xstrndup(key.data, key.size);
504 pa_datum_free(&key);
505
506 if ((e = read_entry(u, name))) {
507 pa_tagstruct_puts(reply, name);
508 pa_tagstruct_puts(reply, e->description);
509
510 pa_xfree(e);
511 }
512
513 pa_xfree(name);
514
515 key = next_key;
516 }
517
518 break;
519 }
520
521 case SUBCOMMAND_RENAME: {
522
523 struct entry *e;
524 const char *device, *description;
525
526 if (pa_tagstruct_gets(t, &device) < 0 ||
527 pa_tagstruct_gets(t, &description) < 0)
528 goto fail;
529
530 if (!device || !*device || !description || !*description)
531 goto fail;
532
533 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
534 pa_datum key, data;
535
536 pa_strlcpy(e->description, description, sizeof(e->description));
537
538 key.data = (char *) device;
539 key.size = strlen(device);
540
541 data.data = e;
542 data.size = sizeof(*e);
543
544 if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
545 apply_entry(u, device, e);
546
547 trigger_save(u);
548 }
549 else
550 pa_log_warn("Could not save device");
551
552 pa_xfree(e);
553 }
554 else
555 pa_log_warn("Could not rename device %s, no entry in database", device);
556
557 break;
558 }
559
560 case SUBCOMMAND_DELETE:
561
562 while (!pa_tagstruct_eof(t)) {
563 const char *name;
564 pa_datum key;
565
566 if (pa_tagstruct_gets(t, &name) < 0)
567 goto fail;
568
569 key.data = (char*) name;
570 key.size = strlen(name);
571
572 /** @todo: Reindex the priorities */
573 pa_database_unset(u->database, &key);
574 }
575
576 trigger_save(u);
577
578 break;
579
580 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
581
582 pa_bool_t enable;
583 uint32_t sridx = PA_INVALID_INDEX;
584 uint32_t idx;
585 pa_module *module;
586
587 if (pa_tagstruct_get_boolean(t, &enable) < 0)
588 goto fail;
589
590 /* If this is the first run, check for stream restore module */
591 if (!u->checked_stream_restore) {
592 u->checked_stream_restore = TRUE;
593
594 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
595 if (strcmp(module->name, "module-stream-restore") == 0) {
596 pa_log_debug("Detected module-stream-restore is currently in use");
597 u->stream_restore_used = TRUE;
598 sridx = module->index;
599 }
600 }
601 }
602
603 u->role_device_priority_routing = enable;
604 if (enable) {
605 if (u->stream_restore_used) {
606 if (PA_INVALID_INDEX == sridx) {
607 /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
608 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
609 if (strcmp(module->name, "module-stream-restore") == 0) {
610 sridx = module->index;
611 }
612 }
613 }
614 if (PA_INVALID_INDEX != sridx) {
615 pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
616 pa_module_unload_request_by_index(u->core, sridx, TRUE);
617 }
618 }
619 } else if (u->stream_restore_used) {
620 /* We want to reload module-stream-restore */
621 if (!pa_module_load(u->core, "module-stream-restore", ""))
622 pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
623 }
624
625 break;
626 }
627
628 case SUBCOMMAND_PREFER_DEVICE:
629 case SUBCOMMAND_DEFER_DEVICE: {
630
631 const char *role, *device;
632 struct entry *e;
633 uint32_t role_index;
634
635 if (pa_tagstruct_gets(t, &role) < 0 ||
636 pa_tagstruct_gets(t, &device) < 0)
637 goto fail;
638
639 if (!role || !device || !*device)
640 goto fail;
641
642 role_index = get_role_index(role);
643 if (PA_INVALID_INDEX == role_index)
644 goto fail;
645
646 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
647 pa_datum key, data;
648 pa_bool_t done;
649 char* prefix;
650 uint32_t priority;
651 pa_bool_t haschanged = FALSE;
652
653 if (strncmp(device, "sink:", 5) == 0)
654 prefix = pa_xstrdup("sink:");
655 else
656 prefix = pa_xstrdup("source:");
657
658 priority = e->priority[role_index];
659
660 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
661
662 done = !pa_database_first(u->database, &key, NULL);
663
664 while (!done && !haschanged) {
665 pa_datum next_key;
666
667 done = !pa_database_next(u->database, &key, &next_key, NULL);
668
669 /* Only read devices with the right prefix */
670 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
671 char *name;
672 struct entry *e2;
673
674 name = pa_xstrndup(key.data, key.size);
675
676 if ((e2 = read_entry(u, name))) {
677 if (SUBCOMMAND_PREFER_DEVICE == command) {
678 /* PREFER */
679 if (e2->priority[role_index] == (priority - 1)) {
680 e2->priority[role_index]++;
681 haschanged = TRUE;
682 }
683 } else {
684 /* DEFER */
685 if (e2->priority[role_index] == (priority + 1)) {
686 e2->priority[role_index]--;
687 haschanged = TRUE;
688 }
689 }
690
691 if (haschanged) {
692 data.data = e2;
693 data.size = sizeof(*e2);
694
695 if (pa_database_set(u->database, &key, &data, FALSE))
696 pa_log_warn("Could not save device");
697 }
698
699 pa_xfree(e2);
700 }
701
702 pa_xfree(name);
703 }
704
705 pa_datum_free(&key);
706 key = next_key;
707 }
708
709 /* Now write out our actual entry */
710 if (haschanged) {
711 if (SUBCOMMAND_PREFER_DEVICE == command)
712 e->priority[role_index]--;
713 else
714 e->priority[role_index]++;
715
716 key.data = (char *) device;
717 key.size = strlen(device);
718
719 data.data = e;
720 data.size = sizeof(*e);
721
722 if (pa_database_set(u->database, &key, &data, FALSE))
723 pa_log_warn("Could not save device");
724
725 trigger_save(u);
726 }
727
728 pa_xfree(e);
729
730 pa_xfree(prefix);
731 }
732 else
733 pa_log_warn("Could not reorder device %s, no entry in database", device);
734
735 break;
736 }
737
738 case SUBCOMMAND_SUBSCRIBE: {
739
740 pa_bool_t enabled;
741
742 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
743 !pa_tagstruct_eof(t))
744 goto fail;
745
746 if (enabled)
747 pa_idxset_put(u->subscribed, c, NULL);
748 else
749 pa_idxset_remove_by_data(u->subscribed, c, NULL);
750
751 break;
752 }
753
754 default:
755 goto fail;
756 }
757
758 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
759 return 0;
760
761 fail:
762
763 if (reply)
764 pa_tagstruct_free(reply);
765
766 return -1;
767 }
768
769 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
770 pa_assert(p);
771 pa_assert(c);
772 pa_assert(u);
773
774 pa_idxset_remove_by_data(u->subscribed, c, NULL);
775 return PA_HOOK_OK;
776 }
777
778 int pa__init(pa_module*m) {
779 pa_modargs *ma = NULL;
780 struct userdata *u;
781 char *fname;
782 pa_sink *sink;
783 pa_source *source;
784 uint32_t idx;
785
786 pa_assert(m);
787
788 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
789 pa_log("Failed to parse module arguments");
790 goto fail;
791 }
792
793 m->userdata = u = pa_xnew0(struct userdata, 1);
794 u->core = m->core;
795 u->module = m;
796 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
797
798 u->protocol = pa_native_protocol_get(m->core);
799 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
800
801 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);
802
803 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
804
805 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);
806 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);
807
808 if (!(fname = pa_state_path("device-manager", TRUE)))
809 goto fail;
810
811 if (!(u->database = pa_database_open(fname, TRUE))) {
812 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
813 pa_xfree(fname);
814 goto fail;
815 }
816
817 pa_log_info("Sucessfully opened database file '%s'.", fname);
818 pa_xfree(fname);
819
820 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
821 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
822
823 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
824 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
825
826 pa_modargs_free(ma);
827 return 0;
828
829 fail:
830 pa__done(m);
831
832 if (ma)
833 pa_modargs_free(ma);
834
835 return -1;
836 }
837
838 void pa__done(pa_module*m) {
839 struct userdata* u;
840
841 pa_assert(m);
842
843 if (!(u = m->userdata))
844 return;
845
846 if (u->subscription)
847 pa_subscription_free(u->subscription);
848
849 if (u->sink_new_hook_slot)
850 pa_hook_slot_free(u->sink_new_hook_slot);
851 if (u->source_new_hook_slot)
852 pa_hook_slot_free(u->source_new_hook_slot);
853
854 if (u->save_time_event)
855 u->core->mainloop->time_free(u->save_time_event);
856
857 if (u->database)
858 pa_database_close(u->database);
859
860 if (u->protocol) {
861 pa_native_protocol_remove_ext(u->protocol, m);
862 pa_native_protocol_unref(u->protocol);
863 }
864
865 if (u->subscribed)
866 pa_idxset_free(u->subscribed, NULL, NULL);
867
868 pa_xfree(u);
869 }