]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Add a function to get a list of the highest priority device indexes...
[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(
62 "on_hotplug=<When new device becomes available, recheck streams?> "
63 "on_rescue=<When device becomes unavailable, recheck streams?>");
64
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
66
67 static const char* const valid_modargs[] = {
68 "on_hotplug",
69 "on_rescue",
70 NULL
71 };
72
73 struct userdata {
74 pa_core *core;
75 pa_module *module;
76 pa_subscription *subscription;
77 pa_hook_slot
78 *sink_new_hook_slot,
79 *source_new_hook_slot,
80 *sink_input_new_hook_slot,
81 *source_output_new_hook_slot,
82 *sink_put_hook_slot,
83 *source_put_hook_slot,
84 *sink_unlink_hook_slot,
85 *source_unlink_hook_slot,
86 *connection_unlink_hook_slot;
87 pa_time_event *save_time_event;
88 pa_database *database;
89
90 pa_native_protocol *protocol;
91 pa_idxset *subscribed;
92
93 pa_bool_t on_hotplug;
94 pa_bool_t on_rescue;
95 pa_bool_t role_device_priority_routing;
96 };
97
98 #define ENTRY_VERSION 1
99
100 #define NUM_ROLES 9
101 enum {
102 ROLE_NONE,
103 ROLE_VIDEO,
104 ROLE_MUSIC,
105 ROLE_GAME,
106 ROLE_EVENT,
107 ROLE_PHONE,
108 ROLE_ANIMATION,
109 ROLE_PRODUCTION,
110 ROLE_A11Y,
111 };
112
113 typedef uint32_t role_indexes_t[NUM_ROLES];
114
115 struct entry {
116 uint8_t version;
117 char description[PA_NAME_MAX];
118 role_indexes_t priority;
119 } PA_GCC_PACKED;
120
121 enum {
122 SUBCOMMAND_TEST,
123 SUBCOMMAND_READ,
124 SUBCOMMAND_RENAME,
125 SUBCOMMAND_DELETE,
126 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
127 SUBCOMMAND_PREFER_DEVICE,
128 SUBCOMMAND_DEFER_DEVICE,
129 SUBCOMMAND_SUBSCRIBE,
130 SUBCOMMAND_EVENT
131 };
132
133 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
134 struct userdata *u = userdata;
135
136 pa_assert(a);
137 pa_assert(e);
138 pa_assert(u);
139
140 pa_assert(e == u->save_time_event);
141 u->core->mainloop->time_free(u->save_time_event);
142 u->save_time_event = NULL;
143
144 pa_database_sync(u->database);
145 pa_log_info("Synced.");
146 }
147
148 static struct entry* read_entry(struct userdata *u, const char *name) {
149 pa_datum key, data;
150 struct entry *e;
151
152 pa_assert(u);
153 pa_assert(name);
154
155 key.data = (char*) name;
156 key.size = strlen(name);
157
158 pa_zero(data);
159
160 if (!pa_database_get(u->database, &key, &data))
161 goto fail;
162
163 if (data.size != sizeof(struct entry)) {
164 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));
165 goto fail;
166 }
167
168 e = (struct entry*) data.data;
169
170 if (e->version != ENTRY_VERSION) {
171 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
172 goto fail;
173 }
174
175 if (!memchr(e->description, 0, sizeof(e->description))) {
176 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
177 goto fail;
178 }
179
180 return e;
181
182 fail:
183
184 pa_datum_free(&data);
185 return NULL;
186 }
187
188 static void trigger_save(struct userdata *u) {
189 pa_native_connection *c;
190 uint32_t idx;
191
192 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
193 pa_tagstruct *t;
194
195 t = pa_tagstruct_new(NULL, 0);
196 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
197 pa_tagstruct_putu32(t, 0);
198 pa_tagstruct_putu32(t, u->module->index);
199 pa_tagstruct_puts(t, u->module->name);
200 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
201
202 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
203 }
204
205 if (u->save_time_event)
206 return;
207
208 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
209 }
210
211 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
212 if (strncmp(a->description, b->description, sizeof(a->description)))
213 return FALSE;
214
215 return TRUE;
216 }
217
218 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
219 struct entry *old;
220
221 pa_assert(u);
222 pa_assert(entry);
223 pa_assert(name);
224 pa_assert(prefix);
225
226 if ((old = read_entry(u, name)))
227 *entry = *old;
228 else {
229 /* This is a new device, so make sure we write it's priority list correctly */
230 role_indexes_t max_priority;
231 pa_datum key;
232 pa_bool_t done;
233
234 pa_zero(max_priority);
235 done = !pa_database_first(u->database, &key, NULL);
236
237 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
238 while (!done) {
239 pa_datum next_key;
240
241 done = !pa_database_next(u->database, &key, &next_key, NULL);
242
243 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
244 char *name2;
245 struct entry *e;
246
247 name2 = pa_xstrndup(key.data, key.size);
248
249 if ((e = read_entry(u, name2))) {
250 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
251 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
252 }
253
254 pa_xfree(e);
255 }
256
257 pa_xfree(name2);
258 }
259 pa_datum_free(&key);
260 key = next_key;
261 }
262
263 /* Actually initialise our entry now we've calculated it */
264 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
265 entry->priority[i] = max_priority[i] + 1;
266 }
267 }
268
269 return old;
270 }
271
272 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
273 struct userdata *u = userdata;
274 struct entry entry, *old = NULL;
275 char *name = NULL;
276 pa_datum key, data;
277
278 pa_assert(c);
279 pa_assert(u);
280
281 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
282 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
283 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
284 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
285 return;
286
287 pa_zero(entry);
288 entry.version = ENTRY_VERSION;
289
290 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
291 pa_sink *sink;
292
293 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
294 return;
295
296 name = pa_sprintf_malloc("sink:%s", sink->name);
297
298 old = load_or_initialize_entry(u, &entry, name, "sink:");
299
300 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
301
302 } else {
303 pa_source *source;
304
305 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
306
307 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
308 return;
309
310 if (source->monitor_of)
311 return;
312
313 name = pa_sprintf_malloc("source:%s", source->name);
314
315 old = load_or_initialize_entry(u, &entry, name, "source:");
316
317 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
318 }
319
320 if (old) {
321
322 if (entries_equal(old, &entry)) {
323 pa_xfree(old);
324 pa_xfree(name);
325 return;
326 }
327
328 pa_xfree(old);
329 }
330
331 key.data = name;
332 key.size = strlen(name);
333
334 data.data = &entry;
335 data.size = sizeof(entry);
336
337 pa_log_info("Storing device %s.", name);
338
339 pa_database_set(u->database, &key, &data, TRUE);
340
341 pa_xfree(name);
342
343 trigger_save(u);
344 }
345
346 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
347 char *name;
348 struct entry *e;
349
350 pa_assert(c);
351 pa_assert(new_data);
352 pa_assert(u);
353
354 name = pa_sprintf_malloc("sink:%s", new_data->name);
355
356 if ((e = read_entry(u, name))) {
357 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
358 pa_log_info("Restoring description for sink %s.", new_data->name);
359 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
360 }
361
362 pa_xfree(e);
363 }
364
365 pa_xfree(name);
366
367 return PA_HOOK_OK;
368 }
369
370 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
371 char *name;
372 struct entry *e;
373
374 pa_assert(c);
375 pa_assert(new_data);
376 pa_assert(u);
377
378 name = pa_sprintf_malloc("source:%s", new_data->name);
379
380 if ((e = read_entry(u, name))) {
381 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
382 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
383 pa_log_info("Restoring description for source %s.", new_data->name);
384 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
385 }
386
387 pa_xfree(e);
388 }
389
390 pa_xfree(name);
391
392 return PA_HOOK_OK;
393 }
394
395 static char *get_name(const char *key, const char *prefix) {
396 char *t;
397
398 if (strncmp(key, prefix, strlen(prefix)))
399 return NULL;
400
401 t = pa_xstrdup(key + strlen(prefix));
402 return t;
403 }
404
405 static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) {
406 role_indexes_t *indexes, highest_priority_available;
407 pa_datum key;
408 pa_bool_t done;
409
410 pa_assert(u);
411 pa_assert(prefix);
412
413 indexes = pa_xnew(role_indexes_t, 1);
414 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
415 *indexes[i] = PA_INVALID_INDEX;
416 }
417 pa_zero(highest_priority_available);
418
419 done = !pa_database_first(u->database, &key, NULL);
420
421 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
422 while (!done) {
423 pa_datum next_key;
424
425 done = !pa_database_next(u->database, &key, &next_key, NULL);
426
427 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
428 char *name;
429 struct entry *e;
430
431 name = pa_xstrndup(key.data, key.size);
432
433 if ((e = read_entry(u, name))) {
434 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
435 if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
436 /* We've found a device with a higher priority than that we've currently got,
437 so see if it is currently available or not and update our list */
438 uint32_t idx;
439 pa_bool_t found = FALSE;
440 char *device_name = get_name(name, prefix);
441
442 if (strcmp(prefix, "sink:") == 0) {
443 pa_sink *sink;
444
445 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
446 if (strcmp(sink->name, device_name) == 0) {
447 found = TRUE;
448 idx = sink->index; /* Is this needed? */
449 break;
450 }
451 }
452 } else {
453 pa_source *source;
454
455 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
456 if (strcmp(source->name, device_name) == 0) {
457 found = TRUE;
458 idx = source->index; /* Is this needed? */
459 break;
460 }
461 }
462 }
463 if (found) {
464 highest_priority_available[i] = e->priority[i];
465 *indexes[i] = idx;
466 }
467
468 pa_xfree(device_name);
469 }
470 }
471
472 pa_xfree(e);
473 }
474
475 pa_xfree(name);
476 }
477
478 pa_datum_free(&key);
479 key = next_key;
480 }
481
482 return indexes;
483 }
484
485
486 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
487 char *name;
488 struct entry *e;
489
490 pa_assert(c);
491 pa_assert(new_data);
492 pa_assert(u);
493
494 if (!u->role_device_priority_routing)
495 return PA_HOOK_OK;
496
497 /*if (!(name = get_name(new_data->proplist, "sink-input")))
498 return PA_HOOK_OK;
499
500 if (new_data->sink)
501 pa_log_debug("Not restoring device for stream %s, because already set.", name);
502 else if ((e = read_entry(u, name))) {
503
504 pa_xfree(e);
505 }
506
507 pa_xfree(name);*/
508
509 return PA_HOOK_OK;
510 }
511
512 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
513 char *name;
514 struct entry *e;
515
516 pa_assert(c);
517 pa_assert(new_data);
518 pa_assert(u);
519
520 if (!u->role_device_priority_routing)
521 return PA_HOOK_OK;
522
523 if (new_data->direct_on_input)
524 return PA_HOOK_OK;
525
526 /*if (!(name = get_name(new_data->proplist, "source-output")))
527 return PA_HOOK_OK;
528
529 if (new_data->source)
530 pa_log_debug("Not restoring device for stream %s, because already set", name);
531 else if ((e = read_entry(u, name))) {
532
533 pa_xfree(e);
534 }
535
536 pa_xfree(name);*/
537
538 return PA_HOOK_OK;
539 }
540
541 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
542 pa_sink_input *si;
543 uint32_t idx;
544
545 pa_assert(c);
546 pa_assert(sink);
547 pa_assert(u);
548 pa_assert(u->on_hotplug);
549
550 if (!u->role_device_priority_routing)
551 return PA_HOOK_OK;
552
553 /** @todo Ensure redo the routing based on the priorities */
554
555 return PA_HOOK_OK;
556 }
557
558 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
559 pa_source_output *so;
560 uint32_t idx;
561
562 pa_assert(c);
563 pa_assert(source);
564 pa_assert(u);
565 pa_assert(u->on_hotplug);
566
567 if (!u->role_device_priority_routing)
568 return PA_HOOK_OK;
569
570 /** @todo Ensure redo the routing based on the priorities */
571
572 return PA_HOOK_OK;
573 }
574
575 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
576 pa_sink_input *si;
577 uint32_t idx;
578
579 pa_assert(c);
580 pa_assert(sink);
581 pa_assert(u);
582 pa_assert(u->on_rescue);
583
584 /* There's no point in doing anything if the core is shut down anyway */
585 if (c->state == PA_CORE_SHUTDOWN)
586 return PA_HOOK_OK;
587
588 if (!u->role_device_priority_routing)
589 return PA_HOOK_OK;
590
591 /** @todo Ensure redo the routing based on the priorities */
592
593 return PA_HOOK_OK;
594 }
595
596 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
597 pa_source_output *so;
598 uint32_t idx;
599
600 pa_assert(c);
601 pa_assert(source);
602 pa_assert(u);
603 pa_assert(u->on_rescue);
604
605 /* There's no point in doing anything if the core is shut down anyway */
606 if (c->state == PA_CORE_SHUTDOWN)
607 return PA_HOOK_OK;
608
609 if (!u->role_device_priority_routing)
610 return PA_HOOK_OK;
611
612 /** @todo Ensure redo the routing based on the priorities */
613
614 return PA_HOOK_OK;
615 }
616
617
618 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
619 pa_sink *sink;
620 pa_source *source;
621 uint32_t idx;
622 char *n;
623
624 pa_assert(u);
625 pa_assert(name);
626 pa_assert(e);
627
628 if ((n = get_name(name, "sink:"))) {
629 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
630 if (!pa_streq(sink->name, n)) {
631 continue;
632 }
633
634 pa_log_info("Setting description for sink %s.", sink->name);
635 pa_sink_set_description(sink, e->description);
636 }
637 pa_xfree(n);
638 }
639 else if ((n = get_name(name, "source:"))) {
640 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
641 if (!pa_streq(source->name, n)) {
642 continue;
643 }
644
645 if (source->monitor_of) {
646 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
647 continue;
648 }
649
650 pa_log_info("Setting description for source %s.", source->name);
651 pa_source_set_description(source, e->description);
652 }
653 pa_xfree(n);
654 }
655 }
656
657
658 static uint32_t get_role_index(const char* role) {
659 pa_assert(role);
660
661 if (strcmp(role, "") == 0)
662 return ROLE_NONE;
663 if (strcmp(role, "video") == 0)
664 return ROLE_VIDEO;
665 if (strcmp(role, "music") == 0)
666 return ROLE_MUSIC;
667 if (strcmp(role, "game") == 0)
668 return ROLE_GAME;
669 if (strcmp(role, "event") == 0)
670 return ROLE_EVENT;
671 if (strcmp(role, "phone") == 0)
672 return ROLE_PHONE;
673 if (strcmp(role, "animation") == 0)
674 return ROLE_ANIMATION;
675 if (strcmp(role, "production") == 0)
676 return ROLE_PRODUCTION;
677 if (strcmp(role, "a11y") == 0)
678 return ROLE_A11Y;
679 return PA_INVALID_INDEX;
680 }
681
682 #define EXT_VERSION 1
683
684 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
685 struct userdata *u;
686 uint32_t command;
687 pa_tagstruct *reply = NULL;
688
689 pa_assert(p);
690 pa_assert(m);
691 pa_assert(c);
692 pa_assert(t);
693
694 u = m->userdata;
695
696 if (pa_tagstruct_getu32(t, &command) < 0)
697 goto fail;
698
699 reply = pa_tagstruct_new(NULL, 0);
700 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
701 pa_tagstruct_putu32(reply, tag);
702
703 switch (command) {
704 case SUBCOMMAND_TEST: {
705 if (!pa_tagstruct_eof(t))
706 goto fail;
707
708 pa_tagstruct_putu32(reply, EXT_VERSION);
709 break;
710 }
711
712 case SUBCOMMAND_READ: {
713 pa_datum key;
714 pa_bool_t done;
715
716 if (!pa_tagstruct_eof(t))
717 goto fail;
718
719 done = !pa_database_first(u->database, &key, NULL);
720
721 while (!done) {
722 pa_datum next_key;
723 struct entry *e;
724 char *name;
725
726 done = !pa_database_next(u->database, &key, &next_key, NULL);
727
728 name = pa_xstrndup(key.data, key.size);
729 pa_datum_free(&key);
730
731 if ((e = read_entry(u, name))) {
732 pa_tagstruct_puts(reply, name);
733 pa_tagstruct_puts(reply, e->description);
734
735 pa_xfree(e);
736 }
737
738 pa_xfree(name);
739
740 key = next_key;
741 }
742
743 break;
744 }
745
746 case SUBCOMMAND_RENAME: {
747
748 struct entry *e;
749 const char *device, *description;
750
751 if (pa_tagstruct_gets(t, &device) < 0 ||
752 pa_tagstruct_gets(t, &description) < 0)
753 goto fail;
754
755 if (!device || !*device || !description || !*description)
756 goto fail;
757
758 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
759 pa_datum key, data;
760
761 pa_strlcpy(e->description, description, sizeof(e->description));
762
763 key.data = (char *) device;
764 key.size = strlen(device);
765
766 data.data = e;
767 data.size = sizeof(*e);
768
769 if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
770 apply_entry(u, device, e);
771
772 trigger_save(u);
773 }
774 else
775 pa_log_warn("Could not save device");
776
777 pa_xfree(e);
778 }
779 else
780 pa_log_warn("Could not rename device %s, no entry in database", device);
781
782 break;
783 }
784
785 case SUBCOMMAND_DELETE:
786
787 while (!pa_tagstruct_eof(t)) {
788 const char *name;
789 pa_datum key;
790
791 if (pa_tagstruct_gets(t, &name) < 0)
792 goto fail;
793
794 key.data = (char*) name;
795 key.size = strlen(name);
796
797 /** @todo: Reindex the priorities */
798 pa_database_unset(u->database, &key);
799 }
800
801 trigger_save(u);
802
803 break;
804
805 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
806
807 pa_bool_t enable;
808 uint32_t sridx = PA_INVALID_INDEX;
809 uint32_t idx;
810 pa_module *module;
811
812 if (pa_tagstruct_get_boolean(t, &enable) < 0)
813 goto fail;
814
815 u->role_device_priority_routing = enable;
816
817 break;
818 }
819
820 case SUBCOMMAND_PREFER_DEVICE:
821 case SUBCOMMAND_DEFER_DEVICE: {
822
823 const char *role, *device;
824 struct entry *e;
825 uint32_t role_index;
826
827 if (pa_tagstruct_gets(t, &role) < 0 ||
828 pa_tagstruct_gets(t, &device) < 0)
829 goto fail;
830
831 if (!role || !device || !*device)
832 goto fail;
833
834 role_index = get_role_index(role);
835 if (PA_INVALID_INDEX == role_index)
836 goto fail;
837
838 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
839 pa_datum key, data;
840 pa_bool_t done;
841 char* prefix;
842 uint32_t priority;
843 pa_bool_t haschanged = FALSE;
844
845 if (strncmp(device, "sink:", 5) == 0)
846 prefix = pa_xstrdup("sink:");
847 else
848 prefix = pa_xstrdup("source:");
849
850 priority = e->priority[role_index];
851
852 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
853
854 done = !pa_database_first(u->database, &key, NULL);
855
856 while (!done && !haschanged) {
857 pa_datum next_key;
858
859 done = !pa_database_next(u->database, &key, &next_key, NULL);
860
861 /* Only read devices with the right prefix */
862 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
863 char *name;
864 struct entry *e2;
865
866 name = pa_xstrndup(key.data, key.size);
867
868 if ((e2 = read_entry(u, name))) {
869 if (SUBCOMMAND_PREFER_DEVICE == command) {
870 /* PREFER */
871 if (e2->priority[role_index] == (priority - 1)) {
872 e2->priority[role_index]++;
873 haschanged = TRUE;
874 }
875 } else {
876 /* DEFER */
877 if (e2->priority[role_index] == (priority + 1)) {
878 e2->priority[role_index]--;
879 haschanged = TRUE;
880 }
881 }
882
883 if (haschanged) {
884 data.data = e2;
885 data.size = sizeof(*e2);
886
887 if (pa_database_set(u->database, &key, &data, FALSE))
888 pa_log_warn("Could not save device");
889 }
890
891 pa_xfree(e2);
892 }
893
894 pa_xfree(name);
895 }
896
897 pa_datum_free(&key);
898 key = next_key;
899 }
900
901 /* Now write out our actual entry */
902 if (haschanged) {
903 if (SUBCOMMAND_PREFER_DEVICE == command)
904 e->priority[role_index]--;
905 else
906 e->priority[role_index]++;
907
908 key.data = (char *) device;
909 key.size = strlen(device);
910
911 data.data = e;
912 data.size = sizeof(*e);
913
914 if (pa_database_set(u->database, &key, &data, FALSE))
915 pa_log_warn("Could not save device");
916
917 trigger_save(u);
918 }
919
920 pa_xfree(e);
921
922 pa_xfree(prefix);
923 }
924 else
925 pa_log_warn("Could not reorder device %s, no entry in database", device);
926
927 break;
928 }
929
930 case SUBCOMMAND_SUBSCRIBE: {
931
932 pa_bool_t enabled;
933
934 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
935 !pa_tagstruct_eof(t))
936 goto fail;
937
938 if (enabled)
939 pa_idxset_put(u->subscribed, c, NULL);
940 else
941 pa_idxset_remove_by_data(u->subscribed, c, NULL);
942
943 break;
944 }
945
946 default:
947 goto fail;
948 }
949
950 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
951 return 0;
952
953 fail:
954
955 if (reply)
956 pa_tagstruct_free(reply);
957
958 return -1;
959 }
960
961 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
962 pa_assert(p);
963 pa_assert(c);
964 pa_assert(u);
965
966 pa_idxset_remove_by_data(u->subscribed, c, NULL);
967 return PA_HOOK_OK;
968 }
969
970 int pa__init(pa_module*m) {
971 pa_modargs *ma = NULL;
972 struct userdata *u;
973 char *fname;
974 pa_sink *sink;
975 pa_source *source;
976 pa_sink_input *si;
977 pa_source_output *so;
978 uint32_t idx;
979 pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
980
981 pa_assert(m);
982
983 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
984 pa_log("Failed to parse module arguments");
985 goto fail;
986 }
987
988 if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
989 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
990 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
991 goto fail;
992 }
993
994 m->userdata = u = pa_xnew0(struct userdata, 1);
995 u->core = m->core;
996 u->module = m;
997 u->on_hotplug = on_hotplug;
998 u->on_rescue = on_rescue;
999 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1000
1001 u->protocol = pa_native_protocol_get(m->core);
1002 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1003
1004 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);
1005
1006 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
1007
1008 /* Used to handle device description management */
1009 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);
1010 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);
1011
1012 /* The following slots are used to deal with routing */
1013 /* A little bit later than module-stream-restore, module-intended-roles */
1014 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
1015 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
1016
1017 if (on_hotplug) {
1018 /* A little bit later than module-stream-restore, module-intended-roles */
1019 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
1020 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
1021 }
1022
1023 if (on_rescue) {
1024 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1025 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
1026 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
1027 }
1028
1029 if (!(fname = pa_state_path("device-manager", TRUE)))
1030 goto fail;
1031
1032 if (!(u->database = pa_database_open(fname, TRUE))) {
1033 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1034 pa_xfree(fname);
1035 goto fail;
1036 }
1037
1038 pa_log_info("Sucessfully opened database file '%s'.", fname);
1039 pa_xfree(fname);
1040
1041 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1042 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1043
1044 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1045 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1046
1047 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
1048 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
1049
1050 PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
1051 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
1052
1053 pa_modargs_free(ma);
1054 return 0;
1055
1056 fail:
1057 pa__done(m);
1058
1059 if (ma)
1060 pa_modargs_free(ma);
1061
1062 return -1;
1063 }
1064
1065 void pa__done(pa_module*m) {
1066 struct userdata* u;
1067
1068 pa_assert(m);
1069
1070 if (!(u = m->userdata))
1071 return;
1072
1073 if (u->subscription)
1074 pa_subscription_free(u->subscription);
1075
1076 if (u->sink_new_hook_slot)
1077 pa_hook_slot_free(u->sink_new_hook_slot);
1078 if (u->source_new_hook_slot)
1079 pa_hook_slot_free(u->source_new_hook_slot);
1080
1081 if (u->sink_input_new_hook_slot)
1082 pa_hook_slot_free(u->sink_input_new_hook_slot);
1083 if (u->source_output_new_hook_slot)
1084 pa_hook_slot_free(u->source_output_new_hook_slot);
1085
1086 if (u->sink_put_hook_slot)
1087 pa_hook_slot_free(u->sink_put_hook_slot);
1088 if (u->source_put_hook_slot)
1089 pa_hook_slot_free(u->source_put_hook_slot);
1090
1091 if (u->sink_unlink_hook_slot)
1092 pa_hook_slot_free(u->sink_unlink_hook_slot);
1093 if (u->source_unlink_hook_slot)
1094 pa_hook_slot_free(u->source_unlink_hook_slot);
1095
1096 if (u->save_time_event)
1097 u->core->mainloop->time_free(u->save_time_event);
1098
1099 if (u->database)
1100 pa_database_close(u->database);
1101
1102 if (u->protocol) {
1103 pa_native_protocol_remove_ext(u->protocol, m);
1104 pa_native_protocol_unref(u->protocol);
1105 }
1106
1107 if (u->subscribed)
1108 pa_idxset_free(u->subscribed, NULL, NULL);
1109
1110 pa_xfree(u);
1111 }