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