"Fossies" - the Fresh Open Source Software Archive

Member "pulseaudio-14.2/src/modules/module-switch-on-port-available.c" (16 Jan 2021, 21849 Bytes) of package /linux/misc/pulseaudio-14.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "module-switch-on-port-available.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.0_vs_14.2.

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