forked from ports/contrib
162 lines
4.6 KiB
Diff
162 lines
4.6 KiB
Diff
From 97a5fe80c1d0fb0d8173a61a87051053cff7a5c2 Mon Sep 17 00:00:00 2001
|
|
From: Pauli Virtanen <pav@iki.fi>
|
|
Date: Thu, 3 Feb 2022 19:23:23 +0200
|
|
Subject: [PATCH] spa/alsa-udev: fix /proc/asound handling without
|
|
CONFIG_SND_VERBOSE_PROCFS
|
|
|
|
For kernels compiled with CONFIG_SND_VERBOSE_PROCFS=n, the pcmXX
|
|
/proc/asound entries do not exist. In that case, the "device busy"
|
|
check cannot be done, but we should still check existence of PCM devices
|
|
correctly.
|
|
|
|
Count the number of PCM devices from /dev/snd, which should work also
|
|
without /proc/asound or /sysfs/class/sound.
|
|
---
|
|
spa/plugins/alsa/alsa-udev.c | 88 ++++++++++++++++++++++++++++++------
|
|
1 file changed, 73 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
|
|
index 572bb8a32..08373239d 100644
|
|
--- a/spa/plugins/alsa/alsa-udev.c
|
|
+++ b/spa/plugins/alsa/alsa-udev.c
|
|
@@ -246,6 +246,60 @@ static void unescape(const char *src, char *dst)
|
|
*d = 0;
|
|
}
|
|
|
|
+static int check_device_pcm_class(const char *devname)
|
|
+{
|
|
+ FILE *f;
|
|
+ char path[PATH_MAX];
|
|
+ char buf[16];
|
|
+ size_t sz;
|
|
+
|
|
+ /* Check device class */
|
|
+ spa_scnprintf(path, sizeof(path), "/sys/class/sound/%s/pcm_class",
|
|
+ devname);
|
|
+ f = fopen(path, "r");
|
|
+ if (f == NULL)
|
|
+ return -errno;
|
|
+ sz = fread(buf, 1, sizeof(buf) - 1, f);
|
|
+ buf[sz] = '\0';
|
|
+ fclose(f);
|
|
+ return spa_strstartswith(buf, "modem") ? -ENXIO : 0;
|
|
+}
|
|
+
|
|
+static int get_num_pcm_devices(unsigned int card_id)
|
|
+{
|
|
+ char prefix[32];
|
|
+ DIR *snd = NULL;
|
|
+ struct dirent *entry;
|
|
+ int num_dev = 0;
|
|
+ int res;
|
|
+
|
|
+ /* Check if card has PCM devices, without opening them */
|
|
+
|
|
+ spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", card_id);
|
|
+
|
|
+ if ((snd = opendir("/dev/snd")) == NULL)
|
|
+ return -errno;
|
|
+
|
|
+ while ((errno = 0, entry = readdir(snd)) != NULL) {
|
|
+ if (!(entry->d_type == DT_CHR &&
|
|
+ spa_strstartswith(entry->d_name, prefix)))
|
|
+ continue;
|
|
+
|
|
+ res = check_device_pcm_class(entry->d_name);
|
|
+ if (res == 0 || res == -ENOENT) {
|
|
+ /* count device also if sysfs status file not there */
|
|
+ ++num_dev;
|
|
+ }
|
|
+ }
|
|
+ if (errno != 0)
|
|
+ res = -errno;
|
|
+ else
|
|
+ res = num_dev;
|
|
+
|
|
+ closedir(snd);
|
|
+ return res;
|
|
+}
|
|
+
|
|
static int check_device_available(struct impl *this, struct device *device, int *num_pcm)
|
|
{
|
|
char path[PATH_MAX];
|
|
@@ -256,14 +310,26 @@ static int check_device_available(struct impl *this, struct device *device, int
|
|
struct dirent *entry, *entry_pcm;
|
|
int res;
|
|
|
|
+ res = get_num_pcm_devices(device->id);
|
|
+ if (res < 0) {
|
|
+ spa_log_error(this->log, "Error finding PCM devices for ALSA card %u: %s",
|
|
+ (unsigned int)device->id, spa_strerror(res));
|
|
+ return res;
|
|
+ }
|
|
+ *num_pcm = res;
|
|
+
|
|
+ spa_log_debug(this->log, "card %u has %d pcm device(s)", (unsigned int)device->id, *num_pcm);
|
|
+
|
|
/*
|
|
* Check if some pcm devices of the card are busy. Check it via /proc, as we
|
|
* don't want to actually open any devices using alsa-lib (generates uncontrolled
|
|
* number of inotify events), or replicate its subdevice logic.
|
|
+ *
|
|
+ * The pcmXX directories do not exist if kernel is compiled with
|
|
+ * CONFIG_SND_VERBOSE_PROCFS=n. In that case, the busy check always
|
|
+ * succeeds.
|
|
*/
|
|
|
|
- *num_pcm = 0;
|
|
-
|
|
spa_scnprintf(path, sizeof(path), "/proc/asound/card%u", (unsigned int)device->id);
|
|
|
|
if ((card = opendir(path)) == NULL)
|
|
@@ -274,16 +340,9 @@ static int check_device_available(struct impl *this, struct device *device, int
|
|
spa_strstartswith(entry->d_name, "pcm")))
|
|
continue;
|
|
|
|
- /* Check device class */
|
|
- spa_scnprintf(path, sizeof(path), "/sys/class/sound/pcmC%uD%s/pcm_class",
|
|
+ spa_scnprintf(path, sizeof(path), "pcmC%uD%s",
|
|
(unsigned int)device->id, entry->d_name+3);
|
|
- f = fopen(path, "r");
|
|
- if (f == NULL)
|
|
- goto done;
|
|
- sz = fread(buf, 1, sizeof(buf) - 1, f);
|
|
- buf[sz] = '\0';
|
|
- fclose(f);
|
|
- if (spa_strstartswith(buf, "modem"))
|
|
+ if (check_device_pcm_class(path) < 0)
|
|
continue;
|
|
|
|
/* Check busy status */
|
|
@@ -319,8 +378,6 @@ static int check_device_available(struct impl *this, struct device *device, int
|
|
if (errno != 0)
|
|
goto done;
|
|
|
|
- ++*num_pcm;
|
|
-
|
|
closedir(pcm);
|
|
pcm = NULL;
|
|
}
|
|
@@ -352,15 +409,16 @@ static int emit_object_info(struct impl *this, struct device *device)
|
|
* device->emitted to true. alsalib functions can be used after that.
|
|
*/
|
|
|
|
+ snprintf(path, sizeof(path), "hw:%u", id);
|
|
+
|
|
if ((res = check_device_available(this, device, &pcm)) < 0)
|
|
return res;
|
|
if (pcm == 0) {
|
|
spa_log_debug(this->log, "no pcm devices for %s", path);
|
|
device->ignored = true;
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
}
|
|
|
|
- snprintf(path, sizeof(path), "hw:%u", id);
|
|
spa_log_debug(this->log, "emitting card %s", path);
|
|
device->emitted = true;
|
|
|
|
--
|
|
GitLab
|
|
|