device-manager: Expose the priority lists in the protocol extension.
[profile/ivi/pulseaudio.git] / src / modules / module-device-manager.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2006-2008 Lennart Poettering
5   Copyright 2009 Colin Guthrie
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
54
55 #include "module-device-manager-symdef.h"
56
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
62     "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
63     "on_hotplug=<When new device becomes available, recheck streams?> "
64     "on_rescue=<When device becomes unavailable, recheck streams?>");
65
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 #define DUMP_DATABASE
68
69 static const char* const valid_modargs[] = {
70     "do_routing",
71     "on_hotplug",
72     "on_rescue",
73     NULL
74 };
75
76 #define NUM_ROLES 9
77 enum {
78     ROLE_NONE,
79     ROLE_VIDEO,
80     ROLE_MUSIC,
81     ROLE_GAME,
82     ROLE_EVENT,
83     ROLE_PHONE,
84     ROLE_ANIMATION,
85     ROLE_PRODUCTION,
86     ROLE_A11Y,
87 };
88
89 typedef uint32_t role_indexes_t[NUM_ROLES];
90
91 static const char* role_names[NUM_ROLES] = {
92     "none",
93     "video",
94     "music",
95     "game",
96     "event",
97     "phone",
98     "animation",
99     "production",
100     "a11y",
101 };
102
103 struct userdata {
104     pa_core *core;
105     pa_module *module;
106     pa_subscription *subscription;
107     pa_hook_slot
108         *sink_new_hook_slot,
109         *source_new_hook_slot,
110         *sink_input_new_hook_slot,
111         *source_output_new_hook_slot,
112         *sink_put_hook_slot,
113         *source_put_hook_slot,
114         *sink_unlink_hook_slot,
115         *source_unlink_hook_slot,
116         *connection_unlink_hook_slot;
117     pa_time_event *save_time_event;
118     pa_database *database;
119
120     pa_native_protocol *protocol;
121     pa_idxset *subscribed;
122
123     pa_bool_t on_hotplug;
124     pa_bool_t on_rescue;
125     pa_bool_t do_routing;
126
127     role_indexes_t preferred_sinks;
128     role_indexes_t preferred_sources;
129 };
130
131 #define ENTRY_VERSION 1
132
133 struct entry {
134     uint8_t version;
135     char description[PA_NAME_MAX];
136     role_indexes_t priority;
137 } PA_GCC_PACKED;
138
139 enum {
140     SUBCOMMAND_TEST,
141     SUBCOMMAND_READ,
142     SUBCOMMAND_RENAME,
143     SUBCOMMAND_DELETE,
144     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
145     SUBCOMMAND_PREFER_DEVICE,
146     SUBCOMMAND_DEFER_DEVICE,
147     SUBCOMMAND_SUBSCRIBE,
148     SUBCOMMAND_EVENT
149 };
150
151
152 static struct entry* read_entry(struct userdata *u, const char *name) {
153     pa_datum key, data;
154     struct entry *e;
155
156     pa_assert(u);
157     pa_assert(name);
158
159     key.data = (char*) name;
160     key.size = strlen(name);
161
162     pa_zero(data);
163
164     if (!pa_database_get(u->database, &key, &data))
165         goto fail;
166
167     if (data.size != sizeof(struct entry)) {
168         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));
169         goto fail;
170     }
171
172     e = (struct entry*) data.data;
173
174     if (e->version != ENTRY_VERSION) {
175         pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
176         goto fail;
177     }
178
179     if (!memchr(e->description, 0, sizeof(e->description))) {
180         pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
181         goto fail;
182     }
183
184     return e;
185
186 fail:
187
188     pa_datum_free(&data);
189     return NULL;
190 }
191
192 #ifdef DUMP_DATABASE
193 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
194     pa_assert(u);
195     pa_assert(human);
196
197     if (sink_mode) {
198         pa_sink *s;
199         if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
200             pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
201         else
202             pa_log_debug("   %s No sink specified", human);
203     } else {
204         pa_source *s;
205         if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
206             pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
207         else
208             pa_log_debug("   %s No source specified", human);
209     }
210 }
211
212 static void dump_database(struct userdata *u) {
213     pa_datum key;
214     pa_bool_t done;
215
216     pa_assert(u);
217
218     done = !pa_database_first(u->database, &key, NULL);
219
220     pa_log_debug("Dumping database");
221     while (!done) {
222         char *name;
223         struct entry *e;
224         pa_datum next_key;
225
226         done = !pa_database_next(u->database, &key, &next_key, NULL);
227
228         name = pa_xstrndup(key.data, key.size);
229
230         if ((e = read_entry(u, name))) {
231             pa_log_debug(" Got entry: %s", name);
232             pa_log_debug("  Description: %s", e->description);
233             pa_log_debug("  Priorities: None:   %3u, Video: %3u, Music:  %3u, Game: %3u, Event: %3u",
234                          e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
235             pa_log_debug("              Phone:  %3u, Anim:  %3u, Prodtn: %3u, A11y: %3u",
236                          e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
237             pa_xfree(e);
238         }
239
240         pa_xfree(name);
241
242         pa_datum_free(&key);
243         key = next_key;
244     }
245
246     pa_log_debug(" Highest priority devices per-role:");
247
248     pa_log_debug("  Sinks:");
249     for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
250         char name[13];
251         uint32_t len = PA_MAX(12u, strlen(role_names[role]));
252         for (int i = 0; i < 12; ++i) name[i] = ' ';
253         strncpy(name, role_names[role], len);
254         name[len] = ':';
255         name[0] -= 32;
256         name[12] = '\0';
257         dump_database_helper(u, role, name, TRUE);
258     }
259
260     pa_log_debug("  Sources:");
261     for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
262         char name[13];
263         uint32_t len = PA_MAX(12u, strlen(role_names[role]));
264         for (int i = 0; i < 12; ++i) name[i] = ' ';
265         strncpy(name, role_names[role], len);
266         name[len] = ':';
267         name[0] -= 32;
268         name[12] = '\0';
269         dump_database_helper(u, role, name, FALSE);
270     }
271
272     pa_log_debug("Completed database dump");
273 }
274 #endif
275
276 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
277     struct userdata *u = userdata;
278
279     pa_assert(a);
280     pa_assert(e);
281     pa_assert(u);
282
283     pa_assert(e == u->save_time_event);
284     u->core->mainloop->time_free(u->save_time_event);
285     u->save_time_event = NULL;
286
287     pa_database_sync(u->database);
288     pa_log_info("Synced.");
289
290 #ifdef DUMP_DATABASE
291     dump_database(u);
292 #endif
293 }
294
295 static void trigger_save(struct userdata *u) {
296     pa_native_connection *c;
297     uint32_t idx;
298
299     for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
300         pa_tagstruct *t;
301
302         t = pa_tagstruct_new(NULL, 0);
303         pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
304         pa_tagstruct_putu32(t, 0);
305         pa_tagstruct_putu32(t, u->module->index);
306         pa_tagstruct_puts(t, u->module->name);
307         pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
308
309         pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
310     }
311
312     if (u->save_time_event)
313         return;
314
315     u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
316 }
317
318 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
319     /** @todo: Compare the priority lists too */
320     if (strncmp(a->description, b->description, sizeof(a->description)))
321         return FALSE;
322
323     return TRUE;
324 }
325
326 static char *get_name(const char *key, const char *prefix) {
327     char *t;
328
329     if (strncmp(key, prefix, strlen(prefix)))
330         return NULL;
331
332     t = pa_xstrdup(key + strlen(prefix));
333     return t;
334 }
335
336 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
337     struct entry *old;
338
339     pa_assert(u);
340     pa_assert(entry);
341     pa_assert(name);
342     pa_assert(prefix);
343
344     if ((old = read_entry(u, name)))
345         *entry = *old;
346     else {
347         /* This is a new device, so make sure we write it's priority list correctly */
348         role_indexes_t max_priority;
349         pa_datum key;
350         pa_bool_t done;
351
352         pa_zero(max_priority);
353         done = !pa_database_first(u->database, &key, NULL);
354
355         /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
356         while (!done) {
357             pa_datum next_key;
358
359             done = !pa_database_next(u->database, &key, &next_key, NULL);
360
361             if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
362                 char *name2;
363                 struct entry *e;
364
365                 name2 = pa_xstrndup(key.data, key.size);
366
367                 if ((e = read_entry(u, name2))) {
368                     for (uint32_t i = 0; i < NUM_ROLES; ++i) {
369                         max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
370                     }
371
372                     pa_xfree(e);
373                 }
374
375                 pa_xfree(name2);
376             }
377             pa_datum_free(&key);
378             key = next_key;
379         }
380
381         /* Actually initialise our entry now we've calculated it */
382         for (uint32_t i = 0; i < NUM_ROLES; ++i) {
383             entry->priority[i] = max_priority[i] + 1;
384         }
385     }
386
387     return old;
388 }
389
390 static uint32_t get_role_index(const char* role) {
391     pa_assert(role);
392
393     if (strcmp(role, "") == 0)
394         return ROLE_NONE;
395     for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
396         if (strcmp(role, role_names[i]) == 0)
397             return i;
398
399     return PA_INVALID_INDEX;
400 }
401
402 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
403     role_indexes_t *indexes, highest_priority_available;
404     pa_datum key;
405     pa_bool_t done, sink_mode;
406
407     pa_assert(u);
408     pa_assert(prefix);
409
410     sink_mode = (strcmp(prefix, "sink:") == 0);
411
412     if (sink_mode)
413         indexes = &u->preferred_sinks;
414     else
415         indexes = &u->preferred_sources;
416
417     for (uint32_t i = 0; i < NUM_ROLES; ++i) {
418         *indexes[i] = PA_INVALID_INDEX;
419     }
420     pa_zero(highest_priority_available);
421
422     done = !pa_database_first(u->database, &key, NULL);
423
424     /* Find all existing devices with the same prefix so we find the highest priority device for each role */
425     while (!done) {
426         pa_datum next_key;
427
428         done = !pa_database_next(u->database, &key, &next_key, NULL);
429
430         if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
431             char *name;
432             struct entry *e;
433
434             name = pa_xstrndup(key.data, key.size);
435
436             if ((e = read_entry(u, name))) {
437                 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
438                     if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
439                         /* We've found a device with a higher priority than that we've currently got,
440                            so see if it is currently available or not and update our list */
441                         uint32_t idx;
442                         pa_bool_t found = FALSE;
443                         char *device_name = get_name(name, prefix);
444
445                         if (sink_mode) {
446                             pa_sink *sink;
447
448                             PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
449                                 if ((pa_sink*) ignore_device == sink)
450                                     continue;
451                                 if (strcmp(sink->name, device_name) == 0) {
452                                     found = TRUE;
453                                     idx = sink->index; /* Is this needed? */
454                                     break;
455                                 }
456                             }
457                         } else {
458                             pa_source *source;
459
460                             PA_IDXSET_FOREACH(source, u->core->sources, idx) {
461                                 if ((pa_source*) ignore_device == source)
462                                     continue;
463                                 if (strcmp(source->name, device_name) == 0) {
464                                     found = TRUE;
465                                     idx = source->index; /* Is this needed? */
466                                     break;
467                                 }
468                             }
469                         }
470                         if (found) {
471                             highest_priority_available[i] = e->priority[i];
472                             *indexes[i] = idx;
473                         }
474
475                         pa_xfree(device_name);
476                     }
477                 }
478
479                 pa_xfree(e);
480             }
481
482             pa_xfree(name);
483         }
484
485         pa_datum_free(&key);
486         key = next_key;
487     }
488 }
489
490
491 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
492     const char *role;
493     uint32_t role_index, device_index;
494     pa_sink *sink;
495
496     pa_assert(u);
497     pa_assert(u->do_routing);
498
499     if (si->save_sink)
500         return;
501
502     /* Skip this if it is already in the process of being moved anyway */
503     if (!si->sink)
504         return;
505
506     /* It might happen that a stream and a sink are set up at the
507     same time, in which case we want to make sure we don't
508     interfere with that */
509     if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
510         return;
511
512     if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
513         role_index = get_role_index("");
514     else
515         role_index = get_role_index(role);
516
517     if (PA_INVALID_INDEX == role_index)
518         return;
519
520     device_index = u->preferred_sinks[role_index];
521     if (PA_INVALID_INDEX == device_index)
522         return;
523
524     if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
525         return;
526
527     if (si->sink != sink)
528         pa_sink_input_move_to(si, sink, TRUE);
529 }
530
531 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
532     pa_sink_input *si;
533     uint32_t idx;
534
535     pa_assert(u);
536
537     if (!u->do_routing)
538         return PA_HOOK_OK;
539
540     update_highest_priority_device_indexes(u, "sink:", ignore_sink);
541
542     PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
543         route_sink_input(u, si);
544     }
545
546     return PA_HOOK_OK;
547 }
548
549 static void route_source_output(struct userdata *u, pa_source_output *so) {
550     const char *role;
551     uint32_t role_index, device_index;
552     pa_source *source;
553
554     pa_assert(u);
555     pa_assert(u->do_routing);
556
557     if (so->save_source)
558         return;
559
560     if (so->direct_on_input)
561         return;
562
563     /* Skip this if it is already in the process of being moved anyway */
564     if (!so->source)
565         return;
566
567     /* It might happen that a stream and a source are set up at the
568     same time, in which case we want to make sure we don't
569     interfere with that */
570     if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
571         return;
572
573     if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
574         role_index = get_role_index("");
575     else
576         role_index = get_role_index(role);
577
578     if (PA_INVALID_INDEX == role_index)
579         return;
580
581     device_index = u->preferred_sources[role_index];
582     if (PA_INVALID_INDEX == device_index)
583         return;
584
585     if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
586         return;
587
588     if (so->source != source)
589         pa_source_output_move_to(so, source, TRUE);
590 }
591
592 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
593     pa_source_output *so;
594     uint32_t idx;
595
596     pa_assert(u);
597
598     if (!u->do_routing)
599         return PA_HOOK_OK;
600
601     update_highest_priority_device_indexes(u, "source:", ignore_source);
602
603     PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
604         route_source_output(u, so);
605     }
606
607     return PA_HOOK_OK;
608 }
609
610 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
611     struct userdata *u = userdata;
612     struct entry entry, *old = NULL;
613     char *name = NULL;
614     pa_datum key, data;
615
616     pa_assert(c);
617     pa_assert(u);
618
619     if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
620         t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
621         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
622         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
623
624         /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
625         t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
626         /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
627         t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
628         return;
629
630     pa_zero(entry);
631     entry.version = ENTRY_VERSION;
632
633     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
634         pa_sink_input *si;
635
636         if (!u->do_routing)
637             return;
638         if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
639             return;
640
641         /* The role may change mid-stream, so we reroute */
642         route_sink_input(u, si);
643
644         return;
645     } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
646         pa_source_output *so;
647
648         if (!u->do_routing)
649             return;
650         if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
651             return;
652
653         /* The role may change mid-stream, so we reroute */
654         route_source_output(u, so);
655
656         return;
657     } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
658         pa_sink *sink;
659
660         if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
661             return;
662
663         name = pa_sprintf_malloc("sink:%s", sink->name);
664
665         old = load_or_initialize_entry(u, &entry, name, "sink:");
666
667         pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
668
669     } else  if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
670         pa_source *source;
671
672         pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
673
674         if (!(source = pa_idxset_get_by_index(c->sources, idx)))
675             return;
676
677         if (source->monitor_of)
678             return;
679
680         name = pa_sprintf_malloc("source:%s", source->name);
681
682         old = load_or_initialize_entry(u, &entry, name, "source:");
683
684         pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
685     }
686
687     pa_assert(name);
688
689     if (old) {
690
691         if (entries_equal(old, &entry)) {
692             pa_xfree(old);
693             pa_xfree(name);
694             return;
695         }
696
697         pa_xfree(old);
698     }
699
700     key.data = name;
701     key.size = strlen(name);
702
703     data.data = &entry;
704     data.size = sizeof(entry);
705
706     pa_log_info("Storing device %s.", name);
707
708     pa_database_set(u->database, &key, &data, TRUE);
709
710     pa_xfree(name);
711
712     trigger_save(u);
713 }
714
715 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
716     char *name;
717     struct entry *e;
718
719     pa_assert(c);
720     pa_assert(new_data);
721     pa_assert(u);
722
723     name = pa_sprintf_malloc("sink:%s", new_data->name);
724
725     if ((e = read_entry(u, name))) {
726         if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
727             pa_log_info("Restoring description for sink %s.", new_data->name);
728             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
729         }
730
731         pa_xfree(e);
732     }
733
734     pa_xfree(name);
735
736     return PA_HOOK_OK;
737 }
738
739 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
740     char *name;
741     struct entry *e;
742
743     pa_assert(c);
744     pa_assert(new_data);
745     pa_assert(u);
746
747     name = pa_sprintf_malloc("source:%s", new_data->name);
748
749     if ((e = read_entry(u, name))) {
750         if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
751             /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
752             pa_log_info("Restoring description for source %s.", new_data->name);
753             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
754         }
755
756         pa_xfree(e);
757     }
758
759     pa_xfree(name);
760
761     return PA_HOOK_OK;
762 }
763
764 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
765     pa_assert(c);
766     pa_assert(new_data);
767     pa_assert(u);
768
769     if (!u->do_routing)
770         return PA_HOOK_OK;
771
772     if (new_data->sink)
773         pa_log_debug("Not restoring device for stream, because already set.");
774     else {
775         const char *role;
776         uint32_t role_index;
777
778         if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
779             role_index = get_role_index("");
780         else
781             role_index = get_role_index(role);
782
783         if (PA_INVALID_INDEX != role_index) {
784             uint32_t device_index;
785
786             device_index = u->preferred_sinks[role_index];
787             if (PA_INVALID_INDEX != device_index) {
788                 pa_sink *sink;
789
790                 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
791                     new_data->sink = sink;
792                     new_data->save_sink = TRUE;
793                 }
794             }
795         }
796     }
797
798     return PA_HOOK_OK;
799 }
800
801 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
802     pa_assert(c);
803     pa_assert(new_data);
804     pa_assert(u);
805
806     if (!u->do_routing)
807         return PA_HOOK_OK;
808
809     if (new_data->direct_on_input)
810         return PA_HOOK_OK;
811
812     if (new_data->source)
813         pa_log_debug("Not restoring device for stream, because already set");
814     else {
815         const char *role;
816         uint32_t role_index;
817
818         if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
819             role_index = get_role_index("");
820         else
821             role_index = get_role_index(role);
822
823         if (PA_INVALID_INDEX != role_index) {
824             uint32_t device_index;
825
826             device_index = u->preferred_sources[role_index];
827             if (PA_INVALID_INDEX != device_index) {
828                 pa_source *source;
829
830                 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
831                     new_data->source = source;
832                     new_data->save_source = TRUE;
833                 }
834             }
835         }
836     }
837
838     return PA_HOOK_OK;
839 }
840
841
842 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
843     pa_assert(c);
844     pa_assert(u);
845     pa_assert(u->core == c);
846     pa_assert(u->on_hotplug);
847
848     return route_sink_inputs(u, NULL);
849 }
850
851 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
852     pa_assert(c);
853     pa_assert(u);
854     pa_assert(u->core == c);
855     pa_assert(u->on_hotplug);
856
857     return route_source_outputs(u, NULL);
858 }
859
860 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
861     pa_assert(c);
862     pa_assert(sink);
863     pa_assert(u);
864     pa_assert(u->core == c);
865     pa_assert(u->on_rescue);
866
867     /* There's no point in doing anything if the core is shut down anyway */
868     if (c->state == PA_CORE_SHUTDOWN)
869         return PA_HOOK_OK;
870
871     return route_sink_inputs(u, sink);
872 }
873
874 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
875     pa_assert(c);
876     pa_assert(source);
877     pa_assert(u);
878     pa_assert(u->core == c);
879     pa_assert(u->on_rescue);
880
881     /* There's no point in doing anything if the core is shut down anyway */
882     if (c->state == PA_CORE_SHUTDOWN)
883         return PA_HOOK_OK;
884
885     return route_source_outputs(u, source);
886 }
887
888
889 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
890     pa_sink *sink;
891     pa_source *source;
892     uint32_t idx;
893     char *n;
894
895     pa_assert(u);
896     pa_assert(name);
897     pa_assert(e);
898
899     if ((n = get_name(name, "sink:"))) {
900         for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
901             if (!pa_streq(sink->name, n)) {
902                 continue;
903             }
904
905             pa_log_info("Setting description for sink %s.", sink->name);
906             pa_sink_set_description(sink, e->description);
907         }
908         pa_xfree(n);
909     }
910     else if ((n = get_name(name, "source:"))) {
911         for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
912             if (!pa_streq(source->name, n)) {
913                 continue;
914             }
915
916             if (source->monitor_of) {
917                 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
918                 continue;
919             }
920
921             pa_log_info("Setting description for source %s.", source->name);
922             pa_source_set_description(source, e->description);
923         }
924         pa_xfree(n);
925     }
926 }
927
928
929 #define EXT_VERSION 1
930
931 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
932   struct userdata *u;
933   uint32_t command;
934   pa_tagstruct *reply = NULL;
935
936   pa_assert(p);
937   pa_assert(m);
938   pa_assert(c);
939   pa_assert(t);
940
941   u = m->userdata;
942
943   if (pa_tagstruct_getu32(t, &command) < 0)
944     goto fail;
945
946   reply = pa_tagstruct_new(NULL, 0);
947   pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
948   pa_tagstruct_putu32(reply, tag);
949
950   switch (command) {
951     case SUBCOMMAND_TEST: {
952       if (!pa_tagstruct_eof(t))
953         goto fail;
954
955       pa_tagstruct_putu32(reply, EXT_VERSION);
956       break;
957     }
958
959     case SUBCOMMAND_READ: {
960       pa_datum key;
961       pa_bool_t done;
962
963       if (!pa_tagstruct_eof(t))
964         goto fail;
965
966       done = !pa_database_first(u->database, &key, NULL);
967
968       while (!done) {
969         pa_datum next_key;
970         struct entry *e;
971         char *name;
972
973         done = !pa_database_next(u->database, &key, &next_key, NULL);
974
975         name = pa_xstrndup(key.data, key.size);
976         pa_datum_free(&key);
977
978         if ((e = read_entry(u, name))) {
979             pa_tagstruct_puts(reply, name);
980             pa_tagstruct_puts(reply, e->description);
981             pa_tagstruct_puts(reply, "audio-card"); /** @todo store the icon */
982             pa_tagstruct_put_boolean(reply, TRUE); /** @todo show current available */
983             pa_tagstruct_putu32(reply, NUM_ROLES);
984
985             for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
986                 pa_tagstruct_puts(reply, role_names[i]);
987                 pa_tagstruct_putu32(reply, e->priority[i]);
988             }
989
990             pa_xfree(e);
991         }
992
993         pa_xfree(name);
994
995         key = next_key;
996       }
997
998       break;
999     }
1000
1001     case SUBCOMMAND_RENAME: {
1002
1003         struct entry *e;
1004         const char *device, *description;
1005
1006         if (pa_tagstruct_gets(t, &device) < 0 ||
1007           pa_tagstruct_gets(t, &description) < 0)
1008           goto fail;
1009
1010         if (!device || !*device || !description || !*description)
1011           goto fail;
1012
1013         if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1014             pa_datum key, data;
1015
1016             pa_strlcpy(e->description, description, sizeof(e->description));
1017
1018             key.data = (char *) device;
1019             key.size = strlen(device);
1020
1021             data.data = e;
1022             data.size = sizeof(*e);
1023
1024             if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1025                 apply_entry(u, device, e);
1026
1027                 trigger_save(u);
1028             }
1029             else
1030                 pa_log_warn("Could not save device");
1031
1032             pa_xfree(e);
1033         }
1034         else
1035             pa_log_warn("Could not rename device %s, no entry in database", device);
1036
1037       break;
1038     }
1039
1040     case SUBCOMMAND_DELETE:
1041
1042       while (!pa_tagstruct_eof(t)) {
1043         const char *name;
1044         pa_datum key;
1045
1046         if (pa_tagstruct_gets(t, &name) < 0)
1047           goto fail;
1048
1049         key.data = (char*) name;
1050         key.size = strlen(name);
1051
1052         /** @todo: Reindex the priorities */
1053         pa_database_unset(u->database, &key);
1054       }
1055
1056       trigger_save(u);
1057
1058       break;
1059
1060     case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1061
1062         pa_bool_t enable;
1063
1064         if (pa_tagstruct_get_boolean(t, &enable) < 0)
1065             goto fail;
1066
1067         if ((u->do_routing = enable)) {
1068             /* Update our caches */
1069             update_highest_priority_device_indexes(u, "sink:", NULL);
1070             update_highest_priority_device_indexes(u, "source:", NULL);
1071         }
1072
1073         break;
1074     }
1075
1076     case SUBCOMMAND_PREFER_DEVICE:
1077     case SUBCOMMAND_DEFER_DEVICE: {
1078
1079         const char *role, *device;
1080         struct entry *e;
1081         uint32_t role_index;
1082
1083         if (pa_tagstruct_gets(t, &role) < 0 ||
1084             pa_tagstruct_gets(t, &device) < 0)
1085             goto fail;
1086
1087         if (!role || !device || !*device)
1088             goto fail;
1089
1090         role_index = get_role_index(role);
1091         if (PA_INVALID_INDEX == role_index)
1092             goto fail;
1093
1094         if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1095             pa_datum key, data;
1096             pa_bool_t done;
1097             char* prefix = NULL;
1098             uint32_t priority;
1099             pa_bool_t haschanged = FALSE;
1100
1101             if (strncmp(device, "sink:", 5) == 0)
1102                 prefix = pa_xstrdup("sink:");
1103             else if (strncmp(device, "source:", 7) == 0)
1104                 prefix = pa_xstrdup("source:");
1105
1106             if (!prefix)
1107                 goto fail;
1108
1109             priority = e->priority[role_index];
1110
1111             /* Now we need to load up all the other entries of this type and shuffle the priroities around */
1112
1113             done = !pa_database_first(u->database, &key, NULL);
1114
1115             while (!done && !haschanged) {
1116                 pa_datum next_key;
1117
1118                 done = !pa_database_next(u->database, &key, &next_key, NULL);
1119
1120                 /* Only read devices with the right prefix */
1121                 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
1122                     char *name;
1123                     struct entry *e2;
1124
1125                     name = pa_xstrndup(key.data, key.size);
1126
1127                     if ((e2 = read_entry(u, name))) {
1128                         if (SUBCOMMAND_PREFER_DEVICE == command) {
1129                             /* PREFER */
1130                             if (e2->priority[role_index] == (priority - 1)) {
1131                                 e2->priority[role_index]++;
1132                                 haschanged = TRUE;
1133                             }
1134                         } else {
1135                             /* DEFER */
1136                             if (e2->priority[role_index] == (priority + 1)) {
1137                                 e2->priority[role_index]--;
1138                                 haschanged = TRUE;
1139                             }
1140                         }
1141
1142                         if (haschanged) {
1143                             data.data = e2;
1144                             data.size = sizeof(*e2);
1145
1146                             if (pa_database_set(u->database, &key, &data, TRUE))
1147                                 pa_log_warn("Could not save device");
1148                         }
1149
1150                         pa_xfree(e2);
1151                     }
1152
1153                     pa_xfree(name);
1154                 }
1155
1156                 pa_datum_free(&key);
1157                 key = next_key;
1158             }
1159
1160             /* Now write out our actual entry */
1161             if (haschanged) {
1162                 if (SUBCOMMAND_PREFER_DEVICE == command)
1163                     e->priority[role_index]--;
1164                 else
1165                     e->priority[role_index]++;
1166
1167                 key.data = (char *) device;
1168                 key.size = strlen(device);
1169
1170                 data.data = e;
1171                 data.size = sizeof(*e);
1172
1173                 if (pa_database_set(u->database, &key, &data, TRUE))
1174                     pa_log_warn("Could not save device");
1175
1176                 trigger_save(u);
1177             }
1178
1179             pa_xfree(e);
1180
1181             pa_xfree(prefix);
1182         }
1183         else
1184             pa_log_warn("Could not reorder device %s, no entry in database", device);
1185
1186         break;
1187     }
1188
1189     case SUBCOMMAND_SUBSCRIBE: {
1190
1191       pa_bool_t enabled;
1192
1193       if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1194         !pa_tagstruct_eof(t))
1195         goto fail;
1196
1197       if (enabled)
1198         pa_idxset_put(u->subscribed, c, NULL);
1199       else
1200         pa_idxset_remove_by_data(u->subscribed, c, NULL);
1201
1202       break;
1203     }
1204
1205     default:
1206       goto fail;
1207   }
1208
1209   pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1210   return 0;
1211
1212   fail:
1213
1214   if (reply)
1215     pa_tagstruct_free(reply);
1216
1217   return -1;
1218 }
1219
1220 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1221     pa_assert(p);
1222     pa_assert(c);
1223     pa_assert(u);
1224
1225     pa_idxset_remove_by_data(u->subscribed, c, NULL);
1226     return PA_HOOK_OK;
1227 }
1228
1229 int pa__init(pa_module*m) {
1230     pa_modargs *ma = NULL;
1231     struct userdata *u;
1232     char *fname;
1233     pa_sink *sink;
1234     pa_source *source;
1235     uint32_t idx;
1236     pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1237
1238     pa_assert(m);
1239
1240     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1241         pa_log("Failed to parse module arguments");
1242         goto fail;
1243     }
1244
1245     if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1246         pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1247         pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1248         pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1249         goto fail;
1250     }
1251
1252     m->userdata = u = pa_xnew0(struct userdata, 1);
1253     u->core = m->core;
1254     u->module = m;
1255     u->do_routing = do_routing;
1256     u->on_hotplug = on_hotplug;
1257     u->on_rescue = on_rescue;
1258     u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1259
1260     u->protocol = pa_native_protocol_get(m->core);
1261     pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1262
1263     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);
1264
1265     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);
1266
1267     /* Used to handle device description management */
1268     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);
1269     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);
1270
1271     /* The following slots are used to deal with routing */
1272     /* A little bit later than module-stream-restore, module-intended-roles */
1273     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);
1274     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);
1275
1276     if (on_hotplug) {
1277         /* A little bit later than module-stream-restore, module-intended-roles */
1278         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);
1279         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);
1280     }
1281
1282     if (on_rescue) {
1283         /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1284         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);
1285         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);
1286     }
1287
1288     if (!(fname = pa_state_path("device-manager", TRUE)))
1289         goto fail;
1290
1291     if (!(u->database = pa_database_open(fname, TRUE))) {
1292         pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1293         pa_xfree(fname);
1294         goto fail;
1295     }
1296
1297     pa_log_info("Sucessfully opened database file '%s'.", fname);
1298     pa_xfree(fname);
1299
1300     /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1301     PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1302         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1303
1304     PA_IDXSET_FOREACH(source, m->core->sources, idx)
1305         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1306
1307     /* Perform the routing (if it's enabled) which will update our priority list cache too */
1308     route_sink_inputs(u, NULL);
1309     route_source_outputs(u, NULL);
1310
1311 #ifdef DUMP_DATABASE
1312     dump_database(u);
1313 #endif
1314
1315     pa_modargs_free(ma);
1316     return 0;
1317
1318 fail:
1319     pa__done(m);
1320
1321     if (ma)
1322         pa_modargs_free(ma);
1323
1324     return  -1;
1325 }
1326
1327 void pa__done(pa_module*m) {
1328     struct userdata* u;
1329
1330     pa_assert(m);
1331
1332     if (!(u = m->userdata))
1333         return;
1334
1335     if (u->subscription)
1336         pa_subscription_free(u->subscription);
1337
1338     if (u->sink_new_hook_slot)
1339         pa_hook_slot_free(u->sink_new_hook_slot);
1340     if (u->source_new_hook_slot)
1341         pa_hook_slot_free(u->source_new_hook_slot);
1342
1343     if (u->sink_input_new_hook_slot)
1344         pa_hook_slot_free(u->sink_input_new_hook_slot);
1345     if (u->source_output_new_hook_slot)
1346         pa_hook_slot_free(u->source_output_new_hook_slot);
1347
1348     if (u->sink_put_hook_slot)
1349         pa_hook_slot_free(u->sink_put_hook_slot);
1350     if (u->source_put_hook_slot)
1351         pa_hook_slot_free(u->source_put_hook_slot);
1352
1353     if (u->sink_unlink_hook_slot)
1354         pa_hook_slot_free(u->sink_unlink_hook_slot);
1355     if (u->source_unlink_hook_slot)
1356         pa_hook_slot_free(u->source_unlink_hook_slot);
1357
1358     if (u->save_time_event)
1359         u->core->mainloop->time_free(u->save_time_event);
1360
1361     if (u->database)
1362         pa_database_close(u->database);
1363
1364     if (u->protocol) {
1365         pa_native_protocol_remove_ext(u->protocol, m);
1366         pa_native_protocol_unref(u->protocol);
1367     }
1368
1369     if (u->subscribed)
1370         pa_idxset_free(u->subscribed, NULL, NULL);
1371
1372     pa_xfree(u);
1373 }