1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2006 Lennart Poettering
5   Copyright 2011 Canonical Ltd
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, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <pulsecore/core.h>
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/device-port.h>
28 #include <pulsecore/hashmap.h>
29 
30 PA_MODULE_AUTHOR("David Henningsson");
31 PA_MODULE_DESCRIPTION("Switches ports and profiles when devices are plugged/unplugged");
32 PA_MODULE_LOAD_ONCE(true);
33 PA_MODULE_VERSION(PACKAGE_VERSION);
34 
35 struct card_info {
36     struct userdata *userdata;
37     pa_card *card;
38 
39     /* We need to cache the active profile, because we want to compare the old
40      * and new profiles in the PROFILE_CHANGED hook. Without this we'd only
41      * have access to the new profile. */
42     pa_card_profile *active_profile;
43 };
44 
45 struct userdata {
46     pa_hashmap *card_infos; /* pa_card -> struct card_info */
47 };
48 
card_info_new(struct userdata *u, pa_card *card)49 static void card_info_new(struct userdata *u, pa_card *card) {
50     struct card_info *info;
51 
52     info = pa_xnew0(struct card_info, 1);
53     info->userdata = u;
54     info->card = card;
55     info->active_profile = card->active_profile;
56 
57     pa_hashmap_put(u->card_infos, card, info);
58 }
59 
card_info_free(struct card_info *info)60 static void card_info_free(struct card_info *info) {
61     pa_hashmap_remove(info->userdata->card_infos, info->card);
62     pa_xfree(info);
63 }
64 
profile_good_for_output(pa_card_profile *profile, pa_device_port *port)65 static bool profile_good_for_output(pa_card_profile *profile, pa_device_port *port) {
66     pa_card *card;
67     pa_sink *sink;
68     uint32_t idx;
69 
70     pa_assert(profile);
71 
72     card = profile->card;
73 
74     if (pa_safe_streq(card->active_profile->name, "off"))
75         return true;
76 
77     if (!pa_safe_streq(card->active_profile->input_name, profile->input_name))
78         return false;
79 
80     if (card->active_profile->n_sources != profile->n_sources)
81         return false;
82 
83     if (card->active_profile->max_source_channels != profile->max_source_channels)
84         return false;
85 
86     if (port == card->preferred_output_port)
87         return true;
88 
89     PA_IDXSET_FOREACH(sink, card->sinks, idx) {
90         if (!sink->active_port)
91             continue;
92 
93         if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= port->priority))
94             return false;
95     }
96 
97     return true;
98 }
99 
profile_good_for_input(pa_card_profile *profile, pa_device_port *port)100 static bool profile_good_for_input(pa_card_profile *profile, pa_device_port *port) {
101     pa_card *card;
102     pa_source *source;
103     uint32_t idx;
104 
105     pa_assert(profile);
106 
107     card = profile->card;
108 
109     if (pa_safe_streq(card->active_profile->name, "off"))
110         return true;
111 
112     if (!pa_safe_streq(card->active_profile->output_name, profile->output_name))
113         return false;
114 
115     if (card->active_profile->n_sinks != profile->n_sinks)
116         return false;
117 
118     if (card->active_profile->max_sink_channels != profile->max_sink_channels)
119         return false;
120 
121     if (port == card->preferred_input_port)
122         return true;
123 
124     PA_IDXSET_FOREACH(source, card->sources, idx) {
125         if (!source->active_port)
126             continue;
127 
128         if ((source->active_port->available != PA_AVAILABLE_NO) && (source->active_port->priority >= port->priority))
129             return false;
130     }
131 
132     return true;
133 }
134 
try_to_switch_profile(pa_device_port *port)135 static int try_to_switch_profile(pa_device_port *port) {
136     pa_card_profile *best_profile = NULL, *profile;
137     void *state;
138     unsigned best_prio = 0;
139 
140     if (port->card->profile_is_sticky) {
141         pa_log_info("Keeping sticky card profile '%s'", port->card->active_profile->name);
142         return -1;
143     }
144 
145     pa_log_debug("Finding best profile for port %s, preferred = %s",
146                  port->name, pa_strnull(port->preferred_profile));
147 
148     PA_HASHMAP_FOREACH(profile, port->profiles, state) {
149         bool good = false;
150         const char *name;
151         unsigned prio = profile->priority;
152 
153         /* We make a best effort to keep other direction unchanged */
154         switch (port->direction) {
155             case PA_DIRECTION_OUTPUT:
156                 name = profile->output_name;
157                 good = profile_good_for_output(profile, port);
158                 break;
159 
160             case PA_DIRECTION_INPUT:
161                 name = profile->input_name;
162                 good = profile_good_for_input(profile, port);
163                 break;
164         }
165 
166         if (!good)
167             continue;
168 
169         /* Give a high bonus in case this is the preferred profile */
170         if (pa_safe_streq(name ? name : profile->name, port->preferred_profile))
171             prio += 1000000;
172 
173         if (best_profile && best_prio >= prio)
174             continue;
175 
176         best_profile = profile;
177         best_prio = prio;
178     }
179 
180     if (!best_profile) {
181         pa_log_debug("No suitable profile found");
182         return -1;
183     }
184 
185     if (pa_card_set_profile(port->card, best_profile, false) != 0) {
186         pa_log_debug("Could not set profile %s", best_profile->name);
187         return -1;
188     }
189 
190     return 0;
191 }
192 
193 struct port_pointers {
194     pa_device_port *port;
195     pa_sink *sink;
196     pa_source *source;
197     bool is_possible_profile_active;
198     bool is_preferred_profile_active;
199     bool is_port_active;
200 };
201 
profile_name_for_dir(pa_card_profile *cp, pa_direction_t dir)202 static const char* profile_name_for_dir(pa_card_profile *cp, pa_direction_t dir) {
203     if (dir == PA_DIRECTION_OUTPUT && cp->output_name)
204         return cp->output_name;
205     if (dir == PA_DIRECTION_INPUT && cp->input_name)
206         return cp->input_name;
207     return cp->name;
208 }
209 
find_port_pointers(pa_device_port *port)210 static struct port_pointers find_port_pointers(pa_device_port *port) {
211     struct port_pointers pp = { .port = port };
212     uint32_t state;
213     pa_card *card;
214 
215     pa_assert(port);
216     pa_assert_se(card = port->card);
217 
218     switch (port->direction) {
219         case PA_DIRECTION_OUTPUT:
220             PA_IDXSET_FOREACH(pp.sink, card->sinks, state)
221                 if (port == pa_hashmap_get(pp.sink->ports, port->name))
222                     break;
223             break;
224 
225         case PA_DIRECTION_INPUT:
226             PA_IDXSET_FOREACH(pp.source, card->sources, state)
227                 if (port == pa_hashmap_get(pp.source->ports, port->name))
228                     break;
229             break;
230     }
231 
232     pp.is_possible_profile_active =
233         card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name);
234     pp.is_preferred_profile_active = pp.is_possible_profile_active && (!port->preferred_profile ||
235         pa_safe_streq(port->preferred_profile, profile_name_for_dir(card->active_profile, port->direction)));
236     pp.is_port_active = (pp.sink && pp.sink->active_port == port) || (pp.source && pp.source->active_port == port);
237 
238     return pp;
239 }
240 
241 /* Switches to a port, switching profiles if necessary or preferred */
switch_to_port(pa_device_port *port, struct port_pointers pp)242 static void switch_to_port(pa_device_port *port, struct port_pointers pp) {
243     if (pp.is_port_active)
244         return; /* Already selected */
245 
246     pa_log_debug("Trying to switch to port %s", port->name);
247     if (!pp.is_preferred_profile_active) {
248         if (try_to_switch_profile(port) < 0) {
249             if (!pp.is_possible_profile_active)
250                 return;
251         }
252         else
253             /* Now that profile has changed, our sink and source pointers must be updated */
254             pp = find_port_pointers(port);
255     }
256 
257     if (pp.source)
258         pa_source_set_port(pp.source, port->name, false);
259     if (pp.sink)
260         pa_sink_set_port(pp.sink, port->name, false);
261 }
262 
263 /* Switches away from a port, switching profiles if necessary or preferred */
switch_from_port(pa_device_port *port, struct port_pointers pp)264 static void switch_from_port(pa_device_port *port, struct port_pointers pp) {
265     pa_device_port *p, *best_port = NULL;
266     void *state;
267 
268     if (!pp.is_port_active)
269         return; /* Already deselected */
270 
271     /* Try to find a good enough port to switch to */
272     PA_HASHMAP_FOREACH(p, port->card->ports, state) {
273         if (p == port)
274             continue;
275 
276         if (p->available == PA_AVAILABLE_NO)
277             continue;
278 
279         if (p->direction != port->direction)
280             continue;
281 
282         if (!best_port || best_port->priority < p->priority)
283            best_port = p;
284     }
285 
286     pa_log_debug("Trying to switch away from port %s, found %s", port->name, best_port ? best_port->name : "no better option");
287 
288     /* If there is no available port to switch to we need check if the active
289      * profile is still available in the
290      * PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED callback, as at this point
291      * the profile availability hasn't been updated yet. */
292     if (best_port) {
293         struct port_pointers best_pp = find_port_pointers(best_port);
294         switch_to_port(best_port, best_pp);
295     }
296 }
297 
298 
port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata)299 static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) {
300     struct port_pointers pp = find_port_pointers(port);
301 
302     if (!port->card) {
303         pa_log_warn("Port %s does not have a card", port->name);
304         return PA_HOOK_OK;
305     }
306 
307     /* Our profile switching logic caused trouble with bluetooth headsets (see
308      * https://bugs.freedesktop.org/show_bug.cgi?id=107044) and
309      * module-bluetooth-policy takes care of automatic profile switching
310      * anyway, so we ignore bluetooth cards in
311      * module-switch-on-port-available. */
312     if (pa_safe_streq(pa_proplist_gets(port->card->proplist, PA_PROP_DEVICE_BUS), "bluetooth"))
313         return PA_HOOK_OK;
314 
315     switch (port->available) {
316     case PA_AVAILABLE_UNKNOWN:
317         /* If a port availability became unknown, let's see if it's part of
318          * some availability group. If it is, it is likely to be a headphone
319          * jack that does not have impedance sensing to detect whether what was
320          * plugged in was a headphone, headset or microphone. In desktop
321          * environments that support it, this will trigger a user choice to
322          * select what kind of device was plugged in. However, let's switch to
323          * the headphone port at least, so that we have don't break
324          * functionality for setups that can't trigger this kind of
325          * interaction.
326          *
327          * For headset or microphone, if they are part of some availability group
328          * and they become unknown from off, it needs to check if their source is
329          * unlinked or not, if their source is unlinked, let switch_to_port()
330          * process them, then with the running of pa_card_set_profile(), their
331          * source will be created, otherwise the headset or microphone can't be used
332          * to record sound since there is no source for these 2 ports. This issue
333          * is observed on Dell machines which have multi-function audio jack but no
334          * internal mic.
335          *
336          * We should make this configurable so that users can optionally
337          * override the default to a headset or mic. */
338 
339         /* Not part of a group of ports, so likely not a combination port */
340         if (!port->availability_group) {
341             pa_log_debug("Not switching to port %s, its availability is unknown and it's not in any availability group.", port->name);
342             break;
343         }
344 
345         /* Switch the headphone port, the input ports without source and the
346          * input ports their source->active_port is part of a group of ports.
347          */
348         if (port->direction == PA_DIRECTION_INPUT && pp.source && !pp.source->active_port->availability_group) {
349             pa_log_debug("Not switching to input port %s, its availability is unknown.", port->name);
350             break;
351         }
352 
353         switch_to_port(port, pp);
354         break;
355 
356     case PA_AVAILABLE_YES:
357         switch_to_port(port, pp);
358         break;
359     case PA_AVAILABLE_NO:
360         switch_from_port(port, pp);
361         break;
362     default:
363         break;
364     }
365 
366     return PA_HOOK_OK;
367 }
368 
find_best_profile(pa_card *card)369 static pa_card_profile *find_best_profile(pa_card *card) {
370     pa_card_profile *profile, *best_profile;
371     void *state;
372 
373     pa_assert(card);
374     best_profile = pa_hashmap_get(card->profiles, "off");
375 
376     PA_HASHMAP_FOREACH(profile, card->profiles, state) {
377         if (profile->available == PA_AVAILABLE_NO)
378             continue;
379 
380         if (profile->priority > best_profile->priority)
381             best_profile = profile;
382     }
383 
384     return best_profile;
385 }
386 
card_profile_available_hook_callback(pa_core *c, pa_card_profile *profile, struct userdata *u)387 static pa_hook_result_t card_profile_available_hook_callback(pa_core *c, pa_card_profile *profile, struct userdata *u) {
388     pa_card *card;
389 
390     pa_assert(profile);
391     pa_assert_se(card = profile->card);
392 
393     if (profile->available != PA_AVAILABLE_NO)
394         return PA_HOOK_OK;
395 
396     if (!pa_streq(profile->name, card->active_profile->name))
397         return PA_HOOK_OK;
398 
399     if (card->profile_is_sticky) {
400         pa_log_info("Keeping sticky card profile '%s'", profile->name);
401         return PA_HOOK_OK;
402     }
403 
404     pa_log_debug("Active profile %s on card %s became unavailable, switching to another profile", profile->name, card->name);
405     pa_card_set_profile(card, find_best_profile(card), false);
406 
407     return PA_HOOK_OK;
408 
409 }
410 
handle_all_unavailable(pa_core *core)411 static void handle_all_unavailable(pa_core *core) {
412     pa_card *card;
413     uint32_t state;
414 
415     PA_IDXSET_FOREACH(card, core->cards, state) {
416         pa_device_port *port;
417         void *state2;
418 
419         PA_HASHMAP_FOREACH(port, card->ports, state2) {
420             if (port->available == PA_AVAILABLE_NO)
421                 port_available_hook_callback(core, port, NULL);
422         }
423     }
424 }
425 
new_sink_source(pa_hashmap *ports, const char *name)426 static pa_device_port *new_sink_source(pa_hashmap *ports, const char *name) {
427 
428     void *state;
429     pa_device_port *i, *p = NULL;
430 
431     if (!ports)
432         return NULL;
433     if (name)
434         p = pa_hashmap_get(ports, name);
435     if (!p)
436         PA_HASHMAP_FOREACH(i, ports, state)
437             if (!p || i->priority > p->priority)
438                 p = i;
439     if (!p)
440         return NULL;
441     if (p->available != PA_AVAILABLE_NO)
442         return NULL;
443 
444     pa_assert_se(p = pa_device_port_find_best(ports));
445     return p;
446 }
447 
sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, void *u)448 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, void *u) {
449 
450     pa_device_port *p = new_sink_source(new_data->ports, new_data->active_port);
451 
452     if (p) {
453         pa_log_debug("Switching initial port for sink '%s' to '%s'", new_data->name, p->name);
454         pa_sink_new_data_set_port(new_data, p->name);
455     }
456     return PA_HOOK_OK;
457 }
458 
source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, void *u)459 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, void *u) {
460 
461     pa_device_port *p = new_sink_source(new_data->ports, new_data->active_port);
462 
463     if (p) {
464         pa_log_debug("Switching initial port for source '%s' to '%s'", new_data->name, p->name);
465         pa_source_new_data_set_port(new_data, p->name);
466     }
467     return PA_HOOK_OK;
468 }
469 
card_put_hook_callback(pa_core *core, pa_card *card, struct userdata *u)470 static pa_hook_result_t card_put_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
471     card_info_new(u, card);
472 
473     return PA_HOOK_OK;
474 }
475 
card_unlink_hook_callback(pa_core *core, pa_card *card, struct userdata *u)476 static pa_hook_result_t card_unlink_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
477     card_info_free(pa_hashmap_get(u->card_infos, card));
478 
479     return PA_HOOK_OK;
480 }
481 
update_preferred_input_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile)482 static void update_preferred_input_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
483     pa_source *source;
484 
485     /* If the profile change didn't affect input, it doesn't indicate change in
486      * the user's input port preference. */
487     if (pa_safe_streq(old_profile->input_name, new_profile->input_name))
488         return;
489 
490     /* If there are more than one source, we don't know which of those the user
491      * prefers. If there are no sources, then the user doesn't seem to care
492      * about input at all. */
493     if (pa_idxset_size(card->sources) != 1) {
494         pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
495         return;
496     }
497 
498     /* If the profile change modified the set of sinks, then it's unclear
499      * whether the user wanted to activate some specific input port, or was the
500      * input change only a side effect of activating some output. If the new
501      * profile contains no sinks, though, then we know the user only cares
502      * about input. */
503     if (pa_idxset_size(card->sinks) > 0 && !pa_safe_streq(old_profile->output_name, new_profile->output_name)) {
504         pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
505         return;
506     }
507 
508     source = pa_idxset_first(card->sources, NULL);
509 
510     /* We know the user wanted to activate this source. The user might not have
511      * wanted to activate the port that was selected by default, but if that's
512      * the case, the user will change the port manually, and we'll update the
513      * port preference at that time. If no port change occurs, we can assume
514      * that the user likes the port that is now active. */
515     pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, source->active_port);
516 }
517 
update_preferred_output_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile)518 static void update_preferred_output_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
519     pa_sink *sink;
520 
521     /* If the profile change didn't affect output, it doesn't indicate change in
522      * the user's output port preference. */
523     if (pa_safe_streq(old_profile->output_name, new_profile->output_name))
524         return;
525 
526     /* If there are more than one sink, we don't know which of those the user
527      * prefers. If there are no sinks, then the user doesn't seem to care about
528      * output at all. */
529     if (pa_idxset_size(card->sinks) != 1) {
530         pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
531         return;
532     }
533 
534     /* If the profile change modified the set of sources, then it's unclear
535      * whether the user wanted to activate some specific output port, or was
536      * the output change only a side effect of activating some input. If the
537      * new profile contains no sources, though, then we know the user only
538      * cares about output. */
539     if (pa_idxset_size(card->sources) > 0 && !pa_safe_streq(old_profile->input_name, new_profile->input_name)) {
540         pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
541         return;
542     }
543 
544     sink = pa_idxset_first(card->sinks, NULL);
545 
546     /* We know the user wanted to activate this sink. The user might not have
547      * wanted to activate the port that was selected by default, but if that's
548      * the case, the user will change the port manually, and we'll update the
549      * port preference at that time. If no port change occurs, we can assume
550      * that the user likes the port that is now active. */
551     pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, sink->active_port);
552 }
553 
card_profile_changed_callback(pa_core *core, pa_card *card, struct userdata *u)554 static pa_hook_result_t card_profile_changed_callback(pa_core *core, pa_card *card, struct userdata *u) {
555     struct card_info *info;
556     pa_card_profile *old_profile;
557     pa_card_profile *new_profile;
558 
559     info = pa_hashmap_get(u->card_infos, card);
560     old_profile = info->active_profile;
561     new_profile = card->active_profile;
562     info->active_profile = new_profile;
563 
564     /* This profile change wasn't initiated by the user, so it doesn't signal
565      * a change in the user's port preferences. */
566     if (!card->save_profile)
567         return PA_HOOK_OK;
568 
569     update_preferred_input_port(card, old_profile, new_profile);
570     update_preferred_output_port(card, old_profile, new_profile);
571 
572     return PA_HOOK_OK;
573 }
574 
source_port_changed_callback(pa_core *core, pa_source *source, void *userdata)575 static pa_hook_result_t source_port_changed_callback(pa_core *core, pa_source *source, void *userdata) {
576     if (!source->save_port)
577         return PA_HOOK_OK;
578 
579     pa_card_set_preferred_port(source->card, PA_DIRECTION_INPUT, source->active_port);
580 
581     return PA_HOOK_OK;
582 }
583 
sink_port_changed_callback(pa_core *core, pa_sink *sink, void *userdata)584 static pa_hook_result_t sink_port_changed_callback(pa_core *core, pa_sink *sink, void *userdata) {
585     if (!sink->save_port)
586         return PA_HOOK_OK;
587 
588     pa_card_set_preferred_port(sink->card, PA_DIRECTION_OUTPUT, sink->active_port);
589 
590     return PA_HOOK_OK;
591 }
592 
pa__init(pa_module*m)593 int pa__init(pa_module*m) {
594     struct userdata *u;
595     pa_card *card;
596     uint32_t idx;
597 
598     pa_assert(m);
599 
600     u = m->userdata = pa_xnew0(struct userdata, 1);
601     u->card_infos = pa_hashmap_new(NULL, NULL);
602 
603     PA_IDXSET_FOREACH(card, m->core->cards, idx)
604         card_info_new(u, card);
605 
606     /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
607     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW],
608                            PA_HOOK_NORMAL, (pa_hook_cb_t) sink_new_hook_callback, NULL);
609     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_NEW],
610                            PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, NULL);
611     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
612                            PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, NULL);
613     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED],
614                            PA_HOOK_LATE, (pa_hook_cb_t) card_profile_available_hook_callback, NULL);
615     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT],
616                            PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
617     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_UNLINK],
618                            PA_HOOK_NORMAL, (pa_hook_cb_t) card_unlink_hook_callback, u);
619     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED],
620                            PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
621     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
622                            PA_HOOK_NORMAL, (pa_hook_cb_t) source_port_changed_callback, NULL);
623     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
624                            PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_changed_callback, NULL);
625 
626     handle_all_unavailable(m->core);
627 
628     return 0;
629 }
630 
pa__done(pa_module *module)631 void pa__done(pa_module *module) {
632     struct userdata *u;
633     struct card_info *info;
634 
635     pa_assert(module);
636 
637     if (!(u = module->userdata))
638         return;
639 
640     while ((info = pa_hashmap_last(u->card_infos)))
641         card_info_free(info);
642 
643     pa_hashmap_free(u->card_infos);
644 
645     pa_xfree(u);
646 }
647