lorbus / rpms / pipewire

Forked from rpms/pipewire a year ago
Clone
Blob Blame History Raw
From 0e79a93828069875bd66f75c33db9f97c6c6b966 Mon Sep 17 00:00:00 2001
From: Pauli Virtanen <pav@iki.fi>
Date: Thu, 3 Feb 2022 19:23:23 +0200
Subject: [PATCH 1/2] 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;
 
-- 
2.34.1