]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: When a new device is encountered, initialise the priority list to...
[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 description for %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
370 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
371 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
372 pa_log_info("Restoring description for sink %s.", new_data->name);
373 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
374 }
375
376 pa_xfree(e);
377 }
378
379 pa_xfree(name);
380
381 return PA_HOOK_OK;
382 }
383
384 static char *get_name(const char *key, const char *prefix) {
385 char *t;
386
387 if (strncmp(key, prefix, strlen(prefix)))
388 return NULL;
389
390 t = pa_xstrdup(key + strlen(prefix));
391 return t;
392 }
393
394 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
395 pa_sink *sink;
396 pa_source *source;
397 uint32_t idx;
398 char *n;
399
400 pa_assert(u);
401 pa_assert(name);
402 pa_assert(e);
403
404 if ((n = get_name(name, "sink:"))) {
405 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
406 if (!pa_streq(sink->name, n)) {
407 continue;
408 }
409
410 pa_log_info("Setting description for sink %s.", sink->name);
411 pa_sink_set_description(sink, e->description);
412 }
413 pa_xfree(n);
414 }
415 else if ((n = get_name(name, "source:"))) {
416 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
417 if (!pa_streq(source->name, n)) {
418 continue;
419 }
420
421 if (source->monitor_of) {
422 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
423 continue;
424 }
425
426 pa_log_info("Setting description for source %s.", source->name);
427 pa_source_set_description(source, e->description);
428 }
429 pa_xfree(n);
430 }
431 }
432
433
434 static uint32_t get_role_index(const char* role) {
435 pa_assert(role);
436
437 if (strcmp(role, "") == 0)
438 return ROLE_NONE;
439 if (strcmp(role, "video") == 0)
440 return ROLE_VIDEO;
441 if (strcmp(role, "music") == 0)
442 return ROLE_MUSIC;
443 if (strcmp(role, "game") == 0)
444 return ROLE_GAME;
445 if (strcmp(role, "event") == 0)
446 return ROLE_EVENT;
447 if (strcmp(role, "phone") == 0)
448 return ROLE_PHONE;
449 if (strcmp(role, "animation") == 0)
450 return ROLE_ANIMATION;
451 if (strcmp(role, "production") == 0)
452 return ROLE_PRODUCTION;
453 if (strcmp(role, "a11y") == 0)
454 return ROLE_A11Y;
455 return PA_INVALID_INDEX;
456 }
457
458 #define EXT_VERSION 1
459
460 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
461 struct userdata *u;
462 uint32_t command;
463 pa_tagstruct *reply = NULL;
464
465 pa_assert(p);
466 pa_assert(m);
467 pa_assert(c);
468 pa_assert(t);
469
470 u = m->userdata;
471
472 if (pa_tagstruct_getu32(t, &command) < 0)
473 goto fail;
474
475 reply = pa_tagstruct_new(NULL, 0);
476 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
477 pa_tagstruct_putu32(reply, tag);
478
479 switch (command) {
480 case SUBCOMMAND_TEST: {
481 if (!pa_tagstruct_eof(t))
482 goto fail;
483
484 pa_tagstruct_putu32(reply, EXT_VERSION);
485 break;
486 }
487
488 case SUBCOMMAND_READ: {
489 pa_datum key;
490 pa_bool_t done;
491
492 if (!pa_tagstruct_eof(t))
493 goto fail;
494
495 done = !pa_database_first(u->database, &key, NULL);
496
497 while (!done) {
498 pa_datum next_key;
499 struct entry *e;
500 char *name;
501
502 done = !pa_database_next(u->database, &key, &next_key, NULL);
503
504 name = pa_xstrndup(key.data, key.size);
505 pa_datum_free(&key);
506
507 if ((e = read_entry(u, name))) {
508 pa_tagstruct_puts(reply, name);
509 pa_tagstruct_puts(reply, e->description);
510
511 pa_xfree(e);
512 }
513
514 pa_xfree(name);
515
516 key = next_key;
517 }
518
519 break;
520 }
521
522 case SUBCOMMAND_RENAME: {
523
524 struct entry *e;
525 const char *device, *description;
526
527 if (pa_tagstruct_gets(t, &device) < 0 ||
528 pa_tagstruct_gets(t, &description) < 0)
529 goto fail;
530
531 if (!device || !*device || !description || !*description)
532 goto fail;
533
534 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
535 pa_datum key, data;
536
537 pa_strlcpy(e->description, description, sizeof(e->description));
538
539 key.data = (char *) device;
540 key.size = strlen(device);
541
542 data.data = e;
543 data.size = sizeof(*e);
544
545 if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
546 apply_entry(u, device, e);
547
548 trigger_save(u);
549 }
550 else
551 pa_log_warn("Could not save device");
552
553 pa_xfree(e);
554 }
555 else
556 pa_log_warn("Could not rename device %s, no entry in database", device);
557
558 break;
559 }
560
561 case SUBCOMMAND_DELETE:
562
563 while (!pa_tagstruct_eof(t)) {
564 const char *name;
565 pa_datum key;
566
567 if (pa_tagstruct_gets(t, &name) < 0)
568 goto fail;
569
570 key.data = (char*) name;
571 key.size = strlen(name);
572
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;
648 pa_datum data;
649 pa_bool_t done;
650 char* prefix;
651 uint32_t priority;
652 pa_bool_t haschanged = FALSE;
653
654 if (strncmp(device, "sink:", 5) == 0)
655 prefix = pa_xstrdup("sink:");
656 else
657 prefix = pa_xstrdup("source:");
658
659 priority = e->priority[role_index];
660
661 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
662
663 done = !pa_database_first(u->database, &key, NULL);
664
665 while (!done && !haschanged) {
666 pa_datum next_key;
667
668 done = !pa_database_next(u->database, &key, &next_key, NULL);
669
670 /* Only read devices with the right prefix */
671 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
672 char *name;
673 struct entry *e2;
674
675 name = pa_xstrndup(key.data, key.size);
676 pa_datum_free(&key);
677
678 if ((e2 = read_entry(u, name))) {
679 if (SUBCOMMAND_PREFER_DEVICE == command) {
680 /* PREFER */
681 if (e2->priority[role_index] == (priority - 1)) {
682 e2->priority[role_index]++;
683 haschanged = TRUE;
684 }
685 } else {
686 /* DEFER */
687 if (e2->priority[role_index] == (priority + 1)) {
688 e2->priority[role_index]--;
689 haschanged = TRUE;
690 }
691 }
692
693 if (haschanged) {
694 data.data = e2;
695 data.size = sizeof(*e2);
696
697 if (pa_database_set(u->database, &key, &data, FALSE))
698 pa_log_warn("Could not save device");
699 }
700 pa_xfree(e2);
701 }
702
703 pa_xfree(name);
704 }
705
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 }