153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2009 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
853a5a1b3Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of the
953a5a1b3Sopenharmony_ci  License, or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public
1753a5a1b3Sopenharmony_ci  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <errno.h>
2553a5a1b3Sopenharmony_ci#include <limits.h>
2653a5a1b3Sopenharmony_ci#include <dirent.h>
2753a5a1b3Sopenharmony_ci#include <sys/inotify.h>
2853a5a1b3Sopenharmony_ci#include <libudev.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3153a5a1b3Sopenharmony_ci
3253a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
3353a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/ratelimit.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/strbuf.h>
3853a5a1b3Sopenharmony_ci
3953a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
4053a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
4153a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
4253a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true);
4353a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
4453a5a1b3Sopenharmony_ci        "tsched=<enable system timer based scheduling mode?> "
4553a5a1b3Sopenharmony_ci        "tsched_buffer_size=<buffer size when using timer based scheduling> "
4653a5a1b3Sopenharmony_ci        "fixed_latency_range=<disable latency range changes on underrun?> "
4753a5a1b3Sopenharmony_ci        "ignore_dB=<ignore dB information from the device?> "
4853a5a1b3Sopenharmony_ci        "deferred_volume=<syncronize sw and hw volume changes in IO-thread?> "
4953a5a1b3Sopenharmony_ci        "use_ucm=<use ALSA UCM for card configuration?> "
5053a5a1b3Sopenharmony_ci        "avoid_resampling=<use stream original sample rate if possible?>");
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_cistruct device {
5353a5a1b3Sopenharmony_ci    char *path;
5453a5a1b3Sopenharmony_ci    bool need_verify;
5553a5a1b3Sopenharmony_ci    bool ignore;
5653a5a1b3Sopenharmony_ci    char *card_name;
5753a5a1b3Sopenharmony_ci    char *args;
5853a5a1b3Sopenharmony_ci    uint32_t module;
5953a5a1b3Sopenharmony_ci    pa_ratelimit ratelimit;
6053a5a1b3Sopenharmony_ci};
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_cistruct userdata {
6353a5a1b3Sopenharmony_ci    pa_core *core;
6453a5a1b3Sopenharmony_ci    pa_hashmap *devices;
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_ci    bool use_tsched:1;
6753a5a1b3Sopenharmony_ci    bool tsched_buffer_size_valid:1;
6853a5a1b3Sopenharmony_ci    bool fixed_latency_range:1;
6953a5a1b3Sopenharmony_ci    bool ignore_dB:1;
7053a5a1b3Sopenharmony_ci    bool deferred_volume:1;
7153a5a1b3Sopenharmony_ci    bool use_ucm:1;
7253a5a1b3Sopenharmony_ci    bool avoid_resampling:1;
7353a5a1b3Sopenharmony_ci
7453a5a1b3Sopenharmony_ci    uint32_t tsched_buffer_size;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    struct udev* udev;
7753a5a1b3Sopenharmony_ci    struct udev_monitor *monitor;
7853a5a1b3Sopenharmony_ci    pa_io_event *udev_io;
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    int inotify_fd;
8153a5a1b3Sopenharmony_ci    pa_io_event *inotify_io;
8253a5a1b3Sopenharmony_ci};
8353a5a1b3Sopenharmony_ci
8453a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
8553a5a1b3Sopenharmony_ci    "tsched",
8653a5a1b3Sopenharmony_ci    "tsched_buffer_size",
8753a5a1b3Sopenharmony_ci    "fixed_latency_range",
8853a5a1b3Sopenharmony_ci    "ignore_dB",
8953a5a1b3Sopenharmony_ci    "deferred_volume",
9053a5a1b3Sopenharmony_ci    "use_ucm",
9153a5a1b3Sopenharmony_ci    "avoid_resampling",
9253a5a1b3Sopenharmony_ci    NULL
9353a5a1b3Sopenharmony_ci};
9453a5a1b3Sopenharmony_ci
9553a5a1b3Sopenharmony_cistatic int setup_inotify(struct userdata *u);
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_cistatic void device_free(struct device *d) {
9853a5a1b3Sopenharmony_ci    pa_assert(d);
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci    pa_xfree(d->path);
10153a5a1b3Sopenharmony_ci    pa_xfree(d->card_name);
10253a5a1b3Sopenharmony_ci    pa_xfree(d->args);
10353a5a1b3Sopenharmony_ci    pa_xfree(d);
10453a5a1b3Sopenharmony_ci}
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_cistatic const char *path_get_card_id(const char *path) {
10753a5a1b3Sopenharmony_ci    const char *e;
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci    if (!path)
11053a5a1b3Sopenharmony_ci        return NULL;
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    if (!(e = strrchr(path, '/')))
11353a5a1b3Sopenharmony_ci        return NULL;
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    if (!pa_startswith(e, "/card"))
11653a5a1b3Sopenharmony_ci        return NULL;
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci    return e + 5;
11953a5a1b3Sopenharmony_ci}
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_cistatic char *card_get_sysattr(const char *card_idx, const char *name) {
12253a5a1b3Sopenharmony_ci    struct udev *udev;
12353a5a1b3Sopenharmony_ci    struct udev_device *card = NULL;
12453a5a1b3Sopenharmony_ci    char *t, *r = NULL;
12553a5a1b3Sopenharmony_ci    const char *v;
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci    pa_assert(card_idx);
12853a5a1b3Sopenharmony_ci    pa_assert(name);
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    if (!(udev = udev_new())) {
13153a5a1b3Sopenharmony_ci        pa_log_error("Failed to allocate udev context.");
13253a5a1b3Sopenharmony_ci        goto finish;
13353a5a1b3Sopenharmony_ci    }
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ci    t = pa_sprintf_malloc("/sys/class/sound/card%s", card_idx);
13653a5a1b3Sopenharmony_ci    card = udev_device_new_from_syspath(udev, t);
13753a5a1b3Sopenharmony_ci    pa_xfree(t);
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    if (!card) {
14053a5a1b3Sopenharmony_ci        pa_log_error("Failed to get card object.");
14153a5a1b3Sopenharmony_ci        goto finish;
14253a5a1b3Sopenharmony_ci    }
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_ci    if ((v = udev_device_get_sysattr_value(card, name)) && *v)
14553a5a1b3Sopenharmony_ci        r = pa_xstrdup(v);
14653a5a1b3Sopenharmony_ci
14753a5a1b3Sopenharmony_cifinish:
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci    if (card)
15053a5a1b3Sopenharmony_ci        udev_device_unref(card);
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci    if (udev)
15353a5a1b3Sopenharmony_ci        udev_unref(udev);
15453a5a1b3Sopenharmony_ci
15553a5a1b3Sopenharmony_ci    return r;
15653a5a1b3Sopenharmony_ci}
15753a5a1b3Sopenharmony_ci
15853a5a1b3Sopenharmony_cistatic bool pcm_is_modem(const char *card_idx, const char *pcm) {
15953a5a1b3Sopenharmony_ci    char *sysfs_path, *pcm_class;
16053a5a1b3Sopenharmony_ci    bool is_modem;
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci    pa_assert(card_idx);
16353a5a1b3Sopenharmony_ci    pa_assert(pcm);
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci    /* Check /sys/class/sound/card.../pcmC...../pcm_class. An HDA
16653a5a1b3Sopenharmony_ci     * modem can be used simultaneously with generic
16753a5a1b3Sopenharmony_ci     * playback/record. */
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci    sysfs_path = pa_sprintf_malloc("pcmC%sD%s/pcm_class", card_idx, pcm);
17053a5a1b3Sopenharmony_ci    pcm_class = card_get_sysattr(card_idx, sysfs_path);
17153a5a1b3Sopenharmony_ci    is_modem = pcm_class && pa_streq(pcm_class, "modem");
17253a5a1b3Sopenharmony_ci    pa_xfree(pcm_class);
17353a5a1b3Sopenharmony_ci    pa_xfree(sysfs_path);
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_ci    return is_modem;
17653a5a1b3Sopenharmony_ci}
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_cistatic bool is_card_busy(const char *id) {
17953a5a1b3Sopenharmony_ci    char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL;
18053a5a1b3Sopenharmony_ci    DIR *card_dir = NULL, *pcm_dir = NULL;
18153a5a1b3Sopenharmony_ci    FILE *status_file = NULL;
18253a5a1b3Sopenharmony_ci    struct dirent *de;
18353a5a1b3Sopenharmony_ci    bool busy = false;
18453a5a1b3Sopenharmony_ci
18553a5a1b3Sopenharmony_ci    pa_assert(id);
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci    /* This simply uses /proc/asound/card.../pcm.../sub.../status to
18853a5a1b3Sopenharmony_ci     * check whether there is still a process using this audio device. */
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_ci    card_path = pa_sprintf_malloc("/proc/asound/card%s", id);
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    if (!(card_dir = opendir(card_path))) {
19353a5a1b3Sopenharmony_ci        pa_log_warn("Failed to open %s: %s", card_path, pa_cstrerror(errno));
19453a5a1b3Sopenharmony_ci        goto fail;
19553a5a1b3Sopenharmony_ci    }
19653a5a1b3Sopenharmony_ci
19753a5a1b3Sopenharmony_ci    for (;;) {
19853a5a1b3Sopenharmony_ci        errno = 0;
19953a5a1b3Sopenharmony_ci        de = readdir(card_dir);
20053a5a1b3Sopenharmony_ci        if (!de && errno) {
20153a5a1b3Sopenharmony_ci            pa_log_warn("readdir() failed: %s", pa_cstrerror(errno));
20253a5a1b3Sopenharmony_ci            goto fail;
20353a5a1b3Sopenharmony_ci        }
20453a5a1b3Sopenharmony_ci
20553a5a1b3Sopenharmony_ci        if (!de)
20653a5a1b3Sopenharmony_ci            break;
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci        if (!pa_startswith(de->d_name, "pcm"))
20953a5a1b3Sopenharmony_ci            continue;
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci        if (pcm_is_modem(id, de->d_name + 3))
21253a5a1b3Sopenharmony_ci            continue;
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci        pa_xfree(pcm_path);
21553a5a1b3Sopenharmony_ci        pcm_path = pa_sprintf_malloc("%s/%s", card_path, de->d_name);
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_ci        if (pcm_dir)
21853a5a1b3Sopenharmony_ci            closedir(pcm_dir);
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci        if (!(pcm_dir = opendir(pcm_path))) {
22153a5a1b3Sopenharmony_ci            pa_log_warn("Failed to open %s: %s", pcm_path, pa_cstrerror(errno));
22253a5a1b3Sopenharmony_ci            continue;
22353a5a1b3Sopenharmony_ci        }
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci        for (;;) {
22653a5a1b3Sopenharmony_ci            char line[32];
22753a5a1b3Sopenharmony_ci
22853a5a1b3Sopenharmony_ci            errno = 0;
22953a5a1b3Sopenharmony_ci            de = readdir(pcm_dir);
23053a5a1b3Sopenharmony_ci            if (!de && errno) {
23153a5a1b3Sopenharmony_ci                pa_log_warn("readdir() failed: %s", pa_cstrerror(errno));
23253a5a1b3Sopenharmony_ci                goto fail;
23353a5a1b3Sopenharmony_ci            }
23453a5a1b3Sopenharmony_ci
23553a5a1b3Sopenharmony_ci            if (!de)
23653a5a1b3Sopenharmony_ci                break;
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci            if (!pa_startswith(de->d_name, "sub"))
23953a5a1b3Sopenharmony_ci                continue;
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci            pa_xfree(sub_status);
24253a5a1b3Sopenharmony_ci            sub_status = pa_sprintf_malloc("%s/%s/status", pcm_path, de->d_name);
24353a5a1b3Sopenharmony_ci
24453a5a1b3Sopenharmony_ci            if (status_file)
24553a5a1b3Sopenharmony_ci                fclose(status_file);
24653a5a1b3Sopenharmony_ci
24753a5a1b3Sopenharmony_ci            if (!(status_file = pa_fopen_cloexec(sub_status, "r"))) {
24853a5a1b3Sopenharmony_ci                pa_log_warn("Failed to open %s: %s", sub_status, pa_cstrerror(errno));
24953a5a1b3Sopenharmony_ci                continue;
25053a5a1b3Sopenharmony_ci            }
25153a5a1b3Sopenharmony_ci
25253a5a1b3Sopenharmony_ci            if (!(fgets(line, sizeof(line)-1, status_file))) {
25353a5a1b3Sopenharmony_ci                pa_log_warn("Failed to read from %s: %s", sub_status, pa_cstrerror(errno));
25453a5a1b3Sopenharmony_ci                continue;
25553a5a1b3Sopenharmony_ci            }
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci            if (!pa_streq(line, "closed\n")) {
25853a5a1b3Sopenharmony_ci                busy = true;
25953a5a1b3Sopenharmony_ci                break;
26053a5a1b3Sopenharmony_ci            }
26153a5a1b3Sopenharmony_ci        }
26253a5a1b3Sopenharmony_ci    }
26353a5a1b3Sopenharmony_ci
26453a5a1b3Sopenharmony_cifail:
26553a5a1b3Sopenharmony_ci
26653a5a1b3Sopenharmony_ci    pa_xfree(card_path);
26753a5a1b3Sopenharmony_ci    pa_xfree(pcm_path);
26853a5a1b3Sopenharmony_ci    pa_xfree(sub_status);
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_ci    if (card_dir)
27153a5a1b3Sopenharmony_ci        closedir(card_dir);
27253a5a1b3Sopenharmony_ci
27353a5a1b3Sopenharmony_ci    if (pcm_dir)
27453a5a1b3Sopenharmony_ci        closedir(pcm_dir);
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ci    if (status_file)
27753a5a1b3Sopenharmony_ci        fclose(status_file);
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_ci    return busy;
28053a5a1b3Sopenharmony_ci}
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_cistatic void verify_access(struct userdata *u, struct device *d) {
28353a5a1b3Sopenharmony_ci    char *cd;
28453a5a1b3Sopenharmony_ci    pa_card *card;
28553a5a1b3Sopenharmony_ci    bool accessible;
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_ci    pa_assert(u);
28853a5a1b3Sopenharmony_ci    pa_assert(d);
28953a5a1b3Sopenharmony_ci
29053a5a1b3Sopenharmony_ci    if (d->ignore)
29153a5a1b3Sopenharmony_ci        return;
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci    cd = pa_sprintf_malloc("/dev/snd/controlC%s", path_get_card_id(d->path));
29453a5a1b3Sopenharmony_ci    accessible = access(cd, R_OK|W_OK) >= 0;
29553a5a1b3Sopenharmony_ci    pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_ci    pa_xfree(cd);
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_ci    if (d->module == PA_INVALID_INDEX) {
30053a5a1b3Sopenharmony_ci
30153a5a1b3Sopenharmony_ci        /* If we are not loaded, try to load */
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_ci        if (accessible) {
30453a5a1b3Sopenharmony_ci            pa_module *m;
30553a5a1b3Sopenharmony_ci            bool busy;
30653a5a1b3Sopenharmony_ci
30753a5a1b3Sopenharmony_ci            /* Check if any of the PCM devices that belong to this
30853a5a1b3Sopenharmony_ci             * card are currently busy. If they are, don't try to load
30953a5a1b3Sopenharmony_ci             * right now, to make sure the probing phase can
31053a5a1b3Sopenharmony_ci             * successfully complete. When the current user of the
31153a5a1b3Sopenharmony_ci             * device closes it we will get another notification via
31253a5a1b3Sopenharmony_ci             * inotify and can then recheck. */
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_ci            busy = is_card_busy(path_get_card_id(d->path));
31553a5a1b3Sopenharmony_ci            pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy));
31653a5a1b3Sopenharmony_ci
31753a5a1b3Sopenharmony_ci            if (!busy) {
31853a5a1b3Sopenharmony_ci
31953a5a1b3Sopenharmony_ci                /* So, why do we rate limit here? It's certainly ugly,
32053a5a1b3Sopenharmony_ci                 * but there seems to be no other way. Problem is
32153a5a1b3Sopenharmony_ci                 * this: if we are unable to configure/probe an audio
32253a5a1b3Sopenharmony_ci                 * device after opening it we will close it again and
32353a5a1b3Sopenharmony_ci                 * the module initialization will fail. This will then
32453a5a1b3Sopenharmony_ci                 * cause an inotify event on the device node which
32553a5a1b3Sopenharmony_ci                 * will be forwarded to us. We then try to reopen the
32653a5a1b3Sopenharmony_ci                 * audio device again, practically entering a busy
32753a5a1b3Sopenharmony_ci                 * loop.
32853a5a1b3Sopenharmony_ci                 *
32953a5a1b3Sopenharmony_ci                 * A clean fix would be if we would be able to ignore
33053a5a1b3Sopenharmony_ci                 * our own inotify close events. However, inotify
33153a5a1b3Sopenharmony_ci                 * lacks such functionality. Also, during probing of
33253a5a1b3Sopenharmony_ci                 * the device we cannot really distinguish between
33353a5a1b3Sopenharmony_ci                 * other processes causing EBUSY or ourselves, which
33453a5a1b3Sopenharmony_ci                 * means we have no way to figure out if the probing
33553a5a1b3Sopenharmony_ci                 * during opening was canceled by a "try again"
33653a5a1b3Sopenharmony_ci                 * failure or a "fatal" failure. */
33753a5a1b3Sopenharmony_ci
33853a5a1b3Sopenharmony_ci                if (pa_ratelimit_test(&d->ratelimit, PA_LOG_DEBUG)) {
33953a5a1b3Sopenharmony_ci                    int err;
34053a5a1b3Sopenharmony_ci
34153a5a1b3Sopenharmony_ci                    pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
34253a5a1b3Sopenharmony_ci                    err = pa_module_load(&m, u->core, "module-alsa-card", d->args);
34353a5a1b3Sopenharmony_ci
34453a5a1b3Sopenharmony_ci                    if (m) {
34553a5a1b3Sopenharmony_ci                        d->module = m->index;
34653a5a1b3Sopenharmony_ci                        pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name);
34753a5a1b3Sopenharmony_ci                    } else if (err == -PA_ERR_NOENTITY) {
34853a5a1b3Sopenharmony_ci                        pa_log_info("Card %s (%s) module skipped.", d->path, d->card_name);
34953a5a1b3Sopenharmony_ci                        d->ignore = true;
35053a5a1b3Sopenharmony_ci                    } else {
35153a5a1b3Sopenharmony_ci                        pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name);
35253a5a1b3Sopenharmony_ci                    }
35353a5a1b3Sopenharmony_ci                } else
35453a5a1b3Sopenharmony_ci                    pa_log_warn("Tried to configure %s (%s) more often than %u times in %llus",
35553a5a1b3Sopenharmony_ci                                d->path,
35653a5a1b3Sopenharmony_ci                                d->card_name,
35753a5a1b3Sopenharmony_ci                                d->ratelimit.burst,
35853a5a1b3Sopenharmony_ci                                (long long unsigned) (d->ratelimit.interval / PA_USEC_PER_SEC));
35953a5a1b3Sopenharmony_ci            }
36053a5a1b3Sopenharmony_ci        }
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_ci    } else {
36353a5a1b3Sopenharmony_ci
36453a5a1b3Sopenharmony_ci        /* If we are already loaded update suspend status with
36553a5a1b3Sopenharmony_ci         * accessible boolean */
36653a5a1b3Sopenharmony_ci
36753a5a1b3Sopenharmony_ci        if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
36853a5a1b3Sopenharmony_ci            pa_log_debug("%s all sinks and sources of card %s.", accessible ? "Resuming" : "Suspending", d->card_name);
36953a5a1b3Sopenharmony_ci            pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
37053a5a1b3Sopenharmony_ci        }
37153a5a1b3Sopenharmony_ci    }
37253a5a1b3Sopenharmony_ci}
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_cistatic void card_changed(struct userdata *u, struct udev_device *dev) {
37553a5a1b3Sopenharmony_ci    struct device *d;
37653a5a1b3Sopenharmony_ci    const char *path;
37753a5a1b3Sopenharmony_ci    const char *t;
37853a5a1b3Sopenharmony_ci    char *n;
37953a5a1b3Sopenharmony_ci    pa_strbuf *args_buf;
38053a5a1b3Sopenharmony_ci
38153a5a1b3Sopenharmony_ci    pa_assert(u);
38253a5a1b3Sopenharmony_ci    pa_assert(dev);
38353a5a1b3Sopenharmony_ci
38453a5a1b3Sopenharmony_ci    /* Maybe /dev/snd is now available? */
38553a5a1b3Sopenharmony_ci    setup_inotify(u);
38653a5a1b3Sopenharmony_ci
38753a5a1b3Sopenharmony_ci    path = udev_device_get_devpath(dev);
38853a5a1b3Sopenharmony_ci
38953a5a1b3Sopenharmony_ci    if ((d = pa_hashmap_get(u->devices, path))) {
39053a5a1b3Sopenharmony_ci        verify_access(u, d);
39153a5a1b3Sopenharmony_ci        return;
39253a5a1b3Sopenharmony_ci    }
39353a5a1b3Sopenharmony_ci
39453a5a1b3Sopenharmony_ci    d = pa_xnew0(struct device, 1);
39553a5a1b3Sopenharmony_ci    d->path = pa_xstrdup(path);
39653a5a1b3Sopenharmony_ci    d->module = PA_INVALID_INDEX;
39753a5a1b3Sopenharmony_ci    PA_INIT_RATELIMIT(d->ratelimit, 10*PA_USEC_PER_SEC, 5);
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_ci    if (!(t = udev_device_get_property_value(dev, "PULSE_NAME")))
40053a5a1b3Sopenharmony_ci        if (!(t = udev_device_get_property_value(dev, "ID_ID")))
40153a5a1b3Sopenharmony_ci            if (!(t = udev_device_get_property_value(dev, "ID_PATH")))
40253a5a1b3Sopenharmony_ci                t = path_get_card_id(path);
40353a5a1b3Sopenharmony_ci
40453a5a1b3Sopenharmony_ci    n = pa_namereg_make_valid_name(t);
40553a5a1b3Sopenharmony_ci    d->card_name = pa_sprintf_malloc("alsa_card.%s", n);
40653a5a1b3Sopenharmony_ci    args_buf = pa_strbuf_new();
40753a5a1b3Sopenharmony_ci    pa_strbuf_printf(args_buf,
40853a5a1b3Sopenharmony_ci                     "device_id=\"%s\" "
40953a5a1b3Sopenharmony_ci                     "name=\"%s\" "
41053a5a1b3Sopenharmony_ci                     "card_name=\"%s\" "
41153a5a1b3Sopenharmony_ci                     "namereg_fail=false "
41253a5a1b3Sopenharmony_ci                     "tsched=%s "
41353a5a1b3Sopenharmony_ci                     "fixed_latency_range=%s "
41453a5a1b3Sopenharmony_ci                     "ignore_dB=%s "
41553a5a1b3Sopenharmony_ci                     "deferred_volume=%s "
41653a5a1b3Sopenharmony_ci                     "use_ucm=%s "
41753a5a1b3Sopenharmony_ci                     "avoid_resampling=%s "
41853a5a1b3Sopenharmony_ci                     "card_properties=\"module-udev-detect.discovered=1\"",
41953a5a1b3Sopenharmony_ci                     path_get_card_id(path),
42053a5a1b3Sopenharmony_ci                     n,
42153a5a1b3Sopenharmony_ci                     d->card_name,
42253a5a1b3Sopenharmony_ci                     pa_yes_no(u->use_tsched),
42353a5a1b3Sopenharmony_ci                     pa_yes_no(u->fixed_latency_range),
42453a5a1b3Sopenharmony_ci                     pa_yes_no(u->ignore_dB),
42553a5a1b3Sopenharmony_ci                     pa_yes_no(u->deferred_volume),
42653a5a1b3Sopenharmony_ci                     pa_yes_no(u->use_ucm),
42753a5a1b3Sopenharmony_ci                     pa_yes_no(u->avoid_resampling));
42853a5a1b3Sopenharmony_ci    pa_xfree(n);
42953a5a1b3Sopenharmony_ci
43053a5a1b3Sopenharmony_ci    if (u->tsched_buffer_size_valid)
43153a5a1b3Sopenharmony_ci        pa_strbuf_printf(args_buf, " tsched_buffer_size=%" PRIu32, u->tsched_buffer_size);
43253a5a1b3Sopenharmony_ci
43353a5a1b3Sopenharmony_ci    d->args = pa_strbuf_to_string_free(args_buf);
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_ci    pa_hashmap_put(u->devices, d->path, d);
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_ci    verify_access(u, d);
43853a5a1b3Sopenharmony_ci}
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_cistatic void remove_card(struct userdata *u, struct udev_device *dev) {
44153a5a1b3Sopenharmony_ci    struct device *d;
44253a5a1b3Sopenharmony_ci
44353a5a1b3Sopenharmony_ci    pa_assert(u);
44453a5a1b3Sopenharmony_ci    pa_assert(dev);
44553a5a1b3Sopenharmony_ci
44653a5a1b3Sopenharmony_ci    if (!(d = pa_hashmap_remove(u->devices, udev_device_get_devpath(dev))))
44753a5a1b3Sopenharmony_ci        return;
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    pa_log_info("Card %s removed.", d->path);
45053a5a1b3Sopenharmony_ci
45153a5a1b3Sopenharmony_ci    if (d->module != PA_INVALID_INDEX)
45253a5a1b3Sopenharmony_ci        pa_module_unload_request_by_index(u->core, d->module, true);
45353a5a1b3Sopenharmony_ci
45453a5a1b3Sopenharmony_ci    device_free(d);
45553a5a1b3Sopenharmony_ci}
45653a5a1b3Sopenharmony_ci
45753a5a1b3Sopenharmony_cistatic void process_device(struct userdata *u, struct udev_device *dev) {
45853a5a1b3Sopenharmony_ci    const char *action, *ff;
45953a5a1b3Sopenharmony_ci
46053a5a1b3Sopenharmony_ci    pa_assert(u);
46153a5a1b3Sopenharmony_ci    pa_assert(dev);
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci    if (udev_device_get_property_value(dev, "PULSE_IGNORE")) {
46453a5a1b3Sopenharmony_ci        pa_log_debug("Ignoring %s, because marked so.", udev_device_get_devpath(dev));
46553a5a1b3Sopenharmony_ci        return;
46653a5a1b3Sopenharmony_ci    }
46753a5a1b3Sopenharmony_ci
46853a5a1b3Sopenharmony_ci    if ((ff = udev_device_get_property_value(dev, "SOUND_CLASS")) &&
46953a5a1b3Sopenharmony_ci        pa_streq(ff, "modem")) {
47053a5a1b3Sopenharmony_ci        pa_log_debug("Ignoring %s, because it is a modem.", udev_device_get_devpath(dev));
47153a5a1b3Sopenharmony_ci        return;
47253a5a1b3Sopenharmony_ci    }
47353a5a1b3Sopenharmony_ci
47453a5a1b3Sopenharmony_ci    action = udev_device_get_action(dev);
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_ci    if (action && pa_streq(action, "remove"))
47753a5a1b3Sopenharmony_ci        remove_card(u, dev);
47853a5a1b3Sopenharmony_ci    else if ((!action || pa_streq(action, "change")) && udev_device_get_property_value(dev, "SOUND_INITIALIZED"))
47953a5a1b3Sopenharmony_ci        card_changed(u, dev);
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    /* For an explanation why we don't look for 'add' events here
48253a5a1b3Sopenharmony_ci     * have a look into /lib/udev/rules.d/78-sound-card.rules! */
48353a5a1b3Sopenharmony_ci}
48453a5a1b3Sopenharmony_ci
48553a5a1b3Sopenharmony_cistatic void process_path(struct userdata *u, const char *path) {
48653a5a1b3Sopenharmony_ci    struct udev_device *dev;
48753a5a1b3Sopenharmony_ci
48853a5a1b3Sopenharmony_ci    if (!path_get_card_id(path))
48953a5a1b3Sopenharmony_ci        return;
49053a5a1b3Sopenharmony_ci
49153a5a1b3Sopenharmony_ci    if (!(dev = udev_device_new_from_syspath(u->udev, path))) {
49253a5a1b3Sopenharmony_ci        pa_log("Failed to get udev device object from udev.");
49353a5a1b3Sopenharmony_ci        return;
49453a5a1b3Sopenharmony_ci    }
49553a5a1b3Sopenharmony_ci
49653a5a1b3Sopenharmony_ci    process_device(u, dev);
49753a5a1b3Sopenharmony_ci    udev_device_unref(dev);
49853a5a1b3Sopenharmony_ci}
49953a5a1b3Sopenharmony_ci
50053a5a1b3Sopenharmony_cistatic void monitor_cb(
50153a5a1b3Sopenharmony_ci        pa_mainloop_api*a,
50253a5a1b3Sopenharmony_ci        pa_io_event* e,
50353a5a1b3Sopenharmony_ci        int fd,
50453a5a1b3Sopenharmony_ci        pa_io_event_flags_t events,
50553a5a1b3Sopenharmony_ci        void *userdata) {
50653a5a1b3Sopenharmony_ci
50753a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
50853a5a1b3Sopenharmony_ci    struct udev_device *dev;
50953a5a1b3Sopenharmony_ci
51053a5a1b3Sopenharmony_ci    pa_assert(a);
51153a5a1b3Sopenharmony_ci
51253a5a1b3Sopenharmony_ci    if (!(dev = udev_monitor_receive_device(u->monitor))) {
51353a5a1b3Sopenharmony_ci        pa_log("Failed to get udev device object from monitor.");
51453a5a1b3Sopenharmony_ci        goto fail;
51553a5a1b3Sopenharmony_ci    }
51653a5a1b3Sopenharmony_ci
51753a5a1b3Sopenharmony_ci    if (!path_get_card_id(udev_device_get_devpath(dev))) {
51853a5a1b3Sopenharmony_ci        udev_device_unref(dev);
51953a5a1b3Sopenharmony_ci        return;
52053a5a1b3Sopenharmony_ci    }
52153a5a1b3Sopenharmony_ci
52253a5a1b3Sopenharmony_ci    process_device(u, dev);
52353a5a1b3Sopenharmony_ci    udev_device_unref(dev);
52453a5a1b3Sopenharmony_ci    return;
52553a5a1b3Sopenharmony_ci
52653a5a1b3Sopenharmony_cifail:
52753a5a1b3Sopenharmony_ci    a->io_free(u->udev_io);
52853a5a1b3Sopenharmony_ci    u->udev_io = NULL;
52953a5a1b3Sopenharmony_ci}
53053a5a1b3Sopenharmony_ci
53153a5a1b3Sopenharmony_cistatic bool pcm_node_belongs_to_device(
53253a5a1b3Sopenharmony_ci        struct device *d,
53353a5a1b3Sopenharmony_ci        const char *node) {
53453a5a1b3Sopenharmony_ci
53553a5a1b3Sopenharmony_ci    char *cd;
53653a5a1b3Sopenharmony_ci    bool b;
53753a5a1b3Sopenharmony_ci
53853a5a1b3Sopenharmony_ci    cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path));
53953a5a1b3Sopenharmony_ci    b = pa_startswith(node, cd);
54053a5a1b3Sopenharmony_ci    pa_xfree(cd);
54153a5a1b3Sopenharmony_ci
54253a5a1b3Sopenharmony_ci    return b;
54353a5a1b3Sopenharmony_ci}
54453a5a1b3Sopenharmony_ci
54553a5a1b3Sopenharmony_cistatic bool control_node_belongs_to_device(
54653a5a1b3Sopenharmony_ci        struct device *d,
54753a5a1b3Sopenharmony_ci        const char *node) {
54853a5a1b3Sopenharmony_ci
54953a5a1b3Sopenharmony_ci    char *cd;
55053a5a1b3Sopenharmony_ci    bool b;
55153a5a1b3Sopenharmony_ci
55253a5a1b3Sopenharmony_ci    cd = pa_sprintf_malloc("controlC%s", path_get_card_id(d->path));
55353a5a1b3Sopenharmony_ci    b = pa_streq(node, cd);
55453a5a1b3Sopenharmony_ci    pa_xfree(cd);
55553a5a1b3Sopenharmony_ci
55653a5a1b3Sopenharmony_ci    return b;
55753a5a1b3Sopenharmony_ci}
55853a5a1b3Sopenharmony_ci
55953a5a1b3Sopenharmony_cistatic void inotify_cb(
56053a5a1b3Sopenharmony_ci        pa_mainloop_api*a,
56153a5a1b3Sopenharmony_ci        pa_io_event* e,
56253a5a1b3Sopenharmony_ci        int fd,
56353a5a1b3Sopenharmony_ci        pa_io_event_flags_t events,
56453a5a1b3Sopenharmony_ci        void *userdata) {
56553a5a1b3Sopenharmony_ci
56653a5a1b3Sopenharmony_ci    struct {
56753a5a1b3Sopenharmony_ci        struct inotify_event e;
56853a5a1b3Sopenharmony_ci        char name[NAME_MAX];
56953a5a1b3Sopenharmony_ci    } buf;
57053a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
57153a5a1b3Sopenharmony_ci    static int type = 0;
57253a5a1b3Sopenharmony_ci    bool deleted = false;
57353a5a1b3Sopenharmony_ci    struct device *d;
57453a5a1b3Sopenharmony_ci    void *state;
57553a5a1b3Sopenharmony_ci
57653a5a1b3Sopenharmony_ci    for (;;) {
57753a5a1b3Sopenharmony_ci        ssize_t r;
57853a5a1b3Sopenharmony_ci        struct inotify_event *event;
57953a5a1b3Sopenharmony_ci
58053a5a1b3Sopenharmony_ci        pa_zero(buf);
58153a5a1b3Sopenharmony_ci        if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) {
58253a5a1b3Sopenharmony_ci
58353a5a1b3Sopenharmony_ci            if (r < 0 && errno == EAGAIN)
58453a5a1b3Sopenharmony_ci                break;
58553a5a1b3Sopenharmony_ci
58653a5a1b3Sopenharmony_ci            pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
58753a5a1b3Sopenharmony_ci            goto fail;
58853a5a1b3Sopenharmony_ci        }
58953a5a1b3Sopenharmony_ci
59053a5a1b3Sopenharmony_ci        event = &buf.e;
59153a5a1b3Sopenharmony_ci        while (r > 0) {
59253a5a1b3Sopenharmony_ci            size_t len;
59353a5a1b3Sopenharmony_ci
59453a5a1b3Sopenharmony_ci            if ((size_t) r < sizeof(struct inotify_event)) {
59553a5a1b3Sopenharmony_ci                pa_log("read() too short.");
59653a5a1b3Sopenharmony_ci                goto fail;
59753a5a1b3Sopenharmony_ci            }
59853a5a1b3Sopenharmony_ci
59953a5a1b3Sopenharmony_ci            len = sizeof(struct inotify_event) + event->len;
60053a5a1b3Sopenharmony_ci
60153a5a1b3Sopenharmony_ci            if ((size_t) r < len) {
60253a5a1b3Sopenharmony_ci                pa_log("Payload missing.");
60353a5a1b3Sopenharmony_ci                goto fail;
60453a5a1b3Sopenharmony_ci            }
60553a5a1b3Sopenharmony_ci
60653a5a1b3Sopenharmony_ci            /* From udev we get the guarantee that the control
60753a5a1b3Sopenharmony_ci             * device's ACL is changed last. To avoid races when ACLs
60853a5a1b3Sopenharmony_ci             * are changed we hence watch only the control device */
60953a5a1b3Sopenharmony_ci            if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "controlC")))
61053a5a1b3Sopenharmony_ci                PA_HASHMAP_FOREACH(d, u->devices, state)
61153a5a1b3Sopenharmony_ci                    if (control_node_belongs_to_device(d, event->name))
61253a5a1b3Sopenharmony_ci                        d->need_verify = true;
61353a5a1b3Sopenharmony_ci
61453a5a1b3Sopenharmony_ci            /* ALSA doesn't really give us any guarantee on the closing
61553a5a1b3Sopenharmony_ci             * order, so let's simply hope */
61653a5a1b3Sopenharmony_ci            if (((event->mask & IN_CLOSE_WRITE) && pa_startswith(event->name, "pcmC")))
61753a5a1b3Sopenharmony_ci                PA_HASHMAP_FOREACH(d, u->devices, state)
61853a5a1b3Sopenharmony_ci                    if (pcm_node_belongs_to_device(d, event->name))
61953a5a1b3Sopenharmony_ci                        d->need_verify = true;
62053a5a1b3Sopenharmony_ci
62153a5a1b3Sopenharmony_ci            /* /dev/snd/ might have been removed */
62253a5a1b3Sopenharmony_ci            if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF)))
62353a5a1b3Sopenharmony_ci                deleted = true;
62453a5a1b3Sopenharmony_ci
62553a5a1b3Sopenharmony_ci            event = (struct inotify_event*) ((uint8_t*) event + len);
62653a5a1b3Sopenharmony_ci            r -= len;
62753a5a1b3Sopenharmony_ci        }
62853a5a1b3Sopenharmony_ci    }
62953a5a1b3Sopenharmony_ci
63053a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(d, u->devices, state)
63153a5a1b3Sopenharmony_ci        if (d->need_verify) {
63253a5a1b3Sopenharmony_ci            d->need_verify = false;
63353a5a1b3Sopenharmony_ci            verify_access(u, d);
63453a5a1b3Sopenharmony_ci        }
63553a5a1b3Sopenharmony_ci
63653a5a1b3Sopenharmony_ci    if (!deleted)
63753a5a1b3Sopenharmony_ci        return;
63853a5a1b3Sopenharmony_ci
63953a5a1b3Sopenharmony_cifail:
64053a5a1b3Sopenharmony_ci    if (u->inotify_io) {
64153a5a1b3Sopenharmony_ci        a->io_free(u->inotify_io);
64253a5a1b3Sopenharmony_ci        u->inotify_io = NULL;
64353a5a1b3Sopenharmony_ci    }
64453a5a1b3Sopenharmony_ci
64553a5a1b3Sopenharmony_ci    if (u->inotify_fd >= 0) {
64653a5a1b3Sopenharmony_ci        pa_close(u->inotify_fd);
64753a5a1b3Sopenharmony_ci        u->inotify_fd = -1;
64853a5a1b3Sopenharmony_ci    }
64953a5a1b3Sopenharmony_ci}
65053a5a1b3Sopenharmony_ci
65153a5a1b3Sopenharmony_cistatic int setup_inotify(struct userdata *u) {
65253a5a1b3Sopenharmony_ci    int r;
65353a5a1b3Sopenharmony_ci
65453a5a1b3Sopenharmony_ci    if (u->inotify_fd >= 0)
65553a5a1b3Sopenharmony_ci        return 0;
65653a5a1b3Sopenharmony_ci
65753a5a1b3Sopenharmony_ci    if ((u->inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
65853a5a1b3Sopenharmony_ci        pa_log("inotify_init1() failed: %s", pa_cstrerror(errno));
65953a5a1b3Sopenharmony_ci        return -1;
66053a5a1b3Sopenharmony_ci    }
66153a5a1b3Sopenharmony_ci
66253a5a1b3Sopenharmony_ci    r = inotify_add_watch(u->inotify_fd, "/dev/snd", IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
66353a5a1b3Sopenharmony_ci
66453a5a1b3Sopenharmony_ci    if (r < 0) {
66553a5a1b3Sopenharmony_ci        int saved_errno = errno;
66653a5a1b3Sopenharmony_ci
66753a5a1b3Sopenharmony_ci        pa_close(u->inotify_fd);
66853a5a1b3Sopenharmony_ci        u->inotify_fd = -1;
66953a5a1b3Sopenharmony_ci
67053a5a1b3Sopenharmony_ci        if (saved_errno == ENOENT) {
67153a5a1b3Sopenharmony_ci            pa_log_debug("/dev/snd/ is apparently not existing yet, retrying to create inotify watch later.");
67253a5a1b3Sopenharmony_ci            return 0;
67353a5a1b3Sopenharmony_ci        }
67453a5a1b3Sopenharmony_ci
67553a5a1b3Sopenharmony_ci        if (saved_errno == ENOSPC) {
67653a5a1b3Sopenharmony_ci            pa_log("You apparently ran out of inotify watches, probably because Tracker/Beagle took them all away. "
67753a5a1b3Sopenharmony_ci                   "I wished people would do their homework first and fix inotify before using it for watching whole "
67853a5a1b3Sopenharmony_ci                   "directory trees which is something the current inotify is certainly not useful for. "
67953a5a1b3Sopenharmony_ci                   "Please make sure to drop the Tracker/Beagle guys a line complaining about their broken use of inotify.");
68053a5a1b3Sopenharmony_ci            return 0;
68153a5a1b3Sopenharmony_ci        }
68253a5a1b3Sopenharmony_ci
68353a5a1b3Sopenharmony_ci        pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno));
68453a5a1b3Sopenharmony_ci        return -1;
68553a5a1b3Sopenharmony_ci    }
68653a5a1b3Sopenharmony_ci
68753a5a1b3Sopenharmony_ci    pa_assert_se(u->inotify_io = u->core->mainloop->io_new(u->core->mainloop, u->inotify_fd, PA_IO_EVENT_INPUT, inotify_cb, u));
68853a5a1b3Sopenharmony_ci
68953a5a1b3Sopenharmony_ci    return 0;
69053a5a1b3Sopenharmony_ci}
69153a5a1b3Sopenharmony_ci
69253a5a1b3Sopenharmony_ciint pa__init(pa_module *m) {
69353a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
69453a5a1b3Sopenharmony_ci    pa_modargs *ma;
69553a5a1b3Sopenharmony_ci    struct udev_enumerate *enumerate = NULL;
69653a5a1b3Sopenharmony_ci    struct udev_list_entry *item = NULL, *first = NULL;
69753a5a1b3Sopenharmony_ci    int fd;
69853a5a1b3Sopenharmony_ci    bool use_tsched = true, fixed_latency_range = false, ignore_dB = false, deferred_volume = m->core->deferred_volume;
69953a5a1b3Sopenharmony_ci    bool use_ucm = true;
70053a5a1b3Sopenharmony_ci    bool avoid_resampling;
70153a5a1b3Sopenharmony_ci
70253a5a1b3Sopenharmony_ci    pa_assert(m);
70353a5a1b3Sopenharmony_ci
70453a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
70553a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments");
70653a5a1b3Sopenharmony_ci        goto fail;
70753a5a1b3Sopenharmony_ci    }
70853a5a1b3Sopenharmony_ci
70953a5a1b3Sopenharmony_ci    m->userdata = u = pa_xnew0(struct userdata, 1);
71053a5a1b3Sopenharmony_ci    u->core = m->core;
71153a5a1b3Sopenharmony_ci    u->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) device_free);
71253a5a1b3Sopenharmony_ci    u->inotify_fd = -1;
71353a5a1b3Sopenharmony_ci
71453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
71553a5a1b3Sopenharmony_ci        pa_log("Failed to parse tsched= argument.");
71653a5a1b3Sopenharmony_ci        goto fail;
71753a5a1b3Sopenharmony_ci    }
71853a5a1b3Sopenharmony_ci    u->use_tsched = use_tsched;
71953a5a1b3Sopenharmony_ci
72053a5a1b3Sopenharmony_ci    if (pa_modargs_get_value(ma, "tsched_buffer_size", NULL)) {
72153a5a1b3Sopenharmony_ci        if (pa_modargs_get_value_u32(ma, "tsched_buffer_size", &u->tsched_buffer_size) < 0) {
72253a5a1b3Sopenharmony_ci            pa_log("Failed to parse tsched_buffer_size= argument.");
72353a5a1b3Sopenharmony_ci            goto fail;
72453a5a1b3Sopenharmony_ci        }
72553a5a1b3Sopenharmony_ci
72653a5a1b3Sopenharmony_ci        u->tsched_buffer_size_valid = true;
72753a5a1b3Sopenharmony_ci    }
72853a5a1b3Sopenharmony_ci
72953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "fixed_latency_range", &fixed_latency_range) < 0) {
73053a5a1b3Sopenharmony_ci        pa_log("Failed to parse fixed_latency_range= argument.");
73153a5a1b3Sopenharmony_ci        goto fail;
73253a5a1b3Sopenharmony_ci    }
73353a5a1b3Sopenharmony_ci    u->fixed_latency_range = fixed_latency_range;
73453a5a1b3Sopenharmony_ci
73553a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
73653a5a1b3Sopenharmony_ci        pa_log("Failed to parse ignore_dB= argument.");
73753a5a1b3Sopenharmony_ci        goto fail;
73853a5a1b3Sopenharmony_ci    }
73953a5a1b3Sopenharmony_ci    u->ignore_dB = ignore_dB;
74053a5a1b3Sopenharmony_ci
74153a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "deferred_volume", &deferred_volume) < 0) {
74253a5a1b3Sopenharmony_ci        pa_log("Failed to parse deferred_volume= argument.");
74353a5a1b3Sopenharmony_ci        goto fail;
74453a5a1b3Sopenharmony_ci    }
74553a5a1b3Sopenharmony_ci    u->deferred_volume = deferred_volume;
74653a5a1b3Sopenharmony_ci
74753a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "use_ucm", &use_ucm) < 0) {
74853a5a1b3Sopenharmony_ci        pa_log("Failed to parse use_ucm= argument.");
74953a5a1b3Sopenharmony_ci        goto fail;
75053a5a1b3Sopenharmony_ci    }
75153a5a1b3Sopenharmony_ci    u->use_ucm = use_ucm;
75253a5a1b3Sopenharmony_ci
75353a5a1b3Sopenharmony_ci    avoid_resampling = m->core->avoid_resampling;
75453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "avoid_resampling", &avoid_resampling) < 0) {
75553a5a1b3Sopenharmony_ci        pa_log("Failed to parse avoid_resampling= argument.");
75653a5a1b3Sopenharmony_ci        goto fail;
75753a5a1b3Sopenharmony_ci    }
75853a5a1b3Sopenharmony_ci    u->avoid_resampling = avoid_resampling;
75953a5a1b3Sopenharmony_ci
76053a5a1b3Sopenharmony_ci    if (!(u->udev = udev_new())) {
76153a5a1b3Sopenharmony_ci        pa_log("Failed to initialize udev library.");
76253a5a1b3Sopenharmony_ci        goto fail;
76353a5a1b3Sopenharmony_ci    }
76453a5a1b3Sopenharmony_ci
76553a5a1b3Sopenharmony_ci    if (setup_inotify(u) < 0)
76653a5a1b3Sopenharmony_ci        goto fail;
76753a5a1b3Sopenharmony_ci
76853a5a1b3Sopenharmony_ci    if (!(u->monitor = udev_monitor_new_from_netlink(u->udev, "udev"))) {
76953a5a1b3Sopenharmony_ci        pa_log("Failed to initialize monitor.");
77053a5a1b3Sopenharmony_ci        goto fail;
77153a5a1b3Sopenharmony_ci    }
77253a5a1b3Sopenharmony_ci
77353a5a1b3Sopenharmony_ci    if (udev_monitor_filter_add_match_subsystem_devtype(u->monitor, "sound", NULL) < 0) {
77453a5a1b3Sopenharmony_ci        pa_log("Failed to subscribe to sound devices.");
77553a5a1b3Sopenharmony_ci        goto fail;
77653a5a1b3Sopenharmony_ci    }
77753a5a1b3Sopenharmony_ci
77853a5a1b3Sopenharmony_ci    errno = 0;
77953a5a1b3Sopenharmony_ci    if (udev_monitor_enable_receiving(u->monitor) < 0) {
78053a5a1b3Sopenharmony_ci        pa_log("Failed to enable monitor: %s", pa_cstrerror(errno));
78153a5a1b3Sopenharmony_ci        if (errno == EPERM)
78253a5a1b3Sopenharmony_ci            pa_log_info("Most likely your kernel is simply too old and "
78353a5a1b3Sopenharmony_ci                        "allows only privileged processes to listen to device events. "
78453a5a1b3Sopenharmony_ci                        "Please upgrade your kernel to at least 2.6.30.");
78553a5a1b3Sopenharmony_ci        goto fail;
78653a5a1b3Sopenharmony_ci    }
78753a5a1b3Sopenharmony_ci
78853a5a1b3Sopenharmony_ci    if ((fd = udev_monitor_get_fd(u->monitor)) < 0) {
78953a5a1b3Sopenharmony_ci        pa_log("Failed to get udev monitor fd.");
79053a5a1b3Sopenharmony_ci        goto fail;
79153a5a1b3Sopenharmony_ci    }
79253a5a1b3Sopenharmony_ci
79353a5a1b3Sopenharmony_ci    pa_assert_se(u->udev_io = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, monitor_cb, u));
79453a5a1b3Sopenharmony_ci
79553a5a1b3Sopenharmony_ci    if (!(enumerate = udev_enumerate_new(u->udev))) {
79653a5a1b3Sopenharmony_ci        pa_log("Failed to initialize udev enumerator.");
79753a5a1b3Sopenharmony_ci        goto fail;
79853a5a1b3Sopenharmony_ci    }
79953a5a1b3Sopenharmony_ci
80053a5a1b3Sopenharmony_ci    if (udev_enumerate_add_match_subsystem(enumerate, "sound") < 0) {
80153a5a1b3Sopenharmony_ci        pa_log("Failed to match to subsystem.");
80253a5a1b3Sopenharmony_ci        goto fail;
80353a5a1b3Sopenharmony_ci    }
80453a5a1b3Sopenharmony_ci
80553a5a1b3Sopenharmony_ci    if (udev_enumerate_scan_devices(enumerate) < 0) {
80653a5a1b3Sopenharmony_ci        pa_log("Failed to scan for devices.");
80753a5a1b3Sopenharmony_ci        goto fail;
80853a5a1b3Sopenharmony_ci    }
80953a5a1b3Sopenharmony_ci
81053a5a1b3Sopenharmony_ci    first = udev_enumerate_get_list_entry(enumerate);
81153a5a1b3Sopenharmony_ci    udev_list_entry_foreach(item, first)
81253a5a1b3Sopenharmony_ci        process_path(u, udev_list_entry_get_name(item));
81353a5a1b3Sopenharmony_ci
81453a5a1b3Sopenharmony_ci    udev_enumerate_unref(enumerate);
81553a5a1b3Sopenharmony_ci
81653a5a1b3Sopenharmony_ci    pa_log_info("Found %u cards.", pa_hashmap_size(u->devices));
81753a5a1b3Sopenharmony_ci
81853a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
81953a5a1b3Sopenharmony_ci
82053a5a1b3Sopenharmony_ci    return 0;
82153a5a1b3Sopenharmony_ci
82253a5a1b3Sopenharmony_cifail:
82353a5a1b3Sopenharmony_ci
82453a5a1b3Sopenharmony_ci    if (enumerate)
82553a5a1b3Sopenharmony_ci        udev_enumerate_unref(enumerate);
82653a5a1b3Sopenharmony_ci
82753a5a1b3Sopenharmony_ci    if (ma)
82853a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
82953a5a1b3Sopenharmony_ci
83053a5a1b3Sopenharmony_ci    pa__done(m);
83153a5a1b3Sopenharmony_ci
83253a5a1b3Sopenharmony_ci    return -1;
83353a5a1b3Sopenharmony_ci}
83453a5a1b3Sopenharmony_ci
83553a5a1b3Sopenharmony_civoid pa__done(pa_module *m) {
83653a5a1b3Sopenharmony_ci    struct userdata *u;
83753a5a1b3Sopenharmony_ci
83853a5a1b3Sopenharmony_ci    pa_assert(m);
83953a5a1b3Sopenharmony_ci
84053a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
84153a5a1b3Sopenharmony_ci        return;
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci    if (u->udev_io)
84453a5a1b3Sopenharmony_ci        m->core->mainloop->io_free(u->udev_io);
84553a5a1b3Sopenharmony_ci
84653a5a1b3Sopenharmony_ci    if (u->monitor)
84753a5a1b3Sopenharmony_ci        udev_monitor_unref(u->monitor);
84853a5a1b3Sopenharmony_ci
84953a5a1b3Sopenharmony_ci    if (u->udev)
85053a5a1b3Sopenharmony_ci        udev_unref(u->udev);
85153a5a1b3Sopenharmony_ci
85253a5a1b3Sopenharmony_ci    if (u->inotify_io)
85353a5a1b3Sopenharmony_ci        m->core->mainloop->io_free(u->inotify_io);
85453a5a1b3Sopenharmony_ci
85553a5a1b3Sopenharmony_ci    if (u->inotify_fd >= 0)
85653a5a1b3Sopenharmony_ci        pa_close(u->inotify_fd);
85753a5a1b3Sopenharmony_ci
85853a5a1b3Sopenharmony_ci    if (u->devices)
85953a5a1b3Sopenharmony_ci        pa_hashmap_free(u->devices);
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_ci    pa_xfree(u);
86253a5a1b3Sopenharmony_ci}
863