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