public inbox for alsa-devel@alsa-project.org
 help / color / mirror / Atom feed
From: Maciej Strozek <mstrozek@opensource.cirrus.com>
To: Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>
Cc: linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org,
	patches@opensource.cirrus.com, alsa-devel@alsa-project.org,
	Maciej Strozek <mstrozek@opensource.cirrus.com>
Subject: [PATCH v3 2/2] ALSA: control: add ioctl to retrieve full card components
Date: Tue,  3 Mar 2026 14:58:00 +0000	[thread overview]
Message-ID: <20260303145815.9930-2-mstrozek@opensource.cirrus.com> (raw)
In-Reply-To: <20260303145815.9930-1-mstrozek@opensource.cirrus.com>

The fixed-size components field in SNDRV_CTL_IOCTL_CARD_INFO can be too
small on systems with many audio devices.

Keep the existing struct snd_ctl_card_info ABI intact and add a new ioctl
to retrieve the full components string.

When the legacy components field is truncated, append '>' to indicate
that the full string is available via the new ioctl.

Link: https://github.com/alsa-project/alsa-lib/pull/494
Suggested-by: Jaroslav Kysela <perex@perex.cz>
Suggested-by: Takashi Iwai <tiwai@suse.com>
Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
---
Changes for v3:
 - change components field to a dynamic array resizable in 32 byte increments
 - removed SNDRV_CTL_COMPONENTS_LEN define
 - sanity check if 'components' requests more than 512 bytes
 - added a commit to clean up trailing whitespaces
 - alsa-utils link no longer needed
Changes for v2:
 - do not modify existing card->components field
 - add a new ioctl and struct to keep the full components string
 - handle the split/trim in snd_ctl_card_info()
---
 include/sound/core.h        |  4 ++--
 include/uapi/sound/asound.h | 14 ++++++++++++-
 sound/core/control.c        | 35 ++++++++++++++++++++++++++++++++-
 sound/core/control_compat.c |  2 ++
 sound/core/init.c           | 39 +++++++++++++++++++++++++++++--------
 5 files changed, 82 insertions(+), 12 deletions(-)

diff --git a/include/sound/core.h b/include/sound/core.h
index 4093ec82a0a1..2b58f79b524d 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -87,8 +87,8 @@ struct snd_card {
 	char longname[80];		/* name of this soundcard */
 	char irq_descr[32];		/* Interrupt description */
 	char mixername[80];		/* mixer name */
-	char components[128];		/* card components delimited with
-								space */
+	char *components_ptr;
+	unsigned int components_ptr_alloc_size; // current memory allocation components_ptr.
 	struct module *module;		/* top-level module */

 	void *private_data;		/* private data for soundcard */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index d3ce75ba938a..422b0b07613d 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -1058,7 +1058,7 @@ struct snd_timer_tread {
  *                                                                          *
  ****************************************************************************/

-#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 9)
+#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 10)

 struct snd_ctl_card_info {
 	int card;			/* card number */
@@ -1072,6 +1072,17 @@ struct snd_ctl_card_info {
 	unsigned char components[128];	/* card components / fine identification, delimited with one space (AC97 etc..) */
 };

+/*
+ * Card components can exceed the fixed 128 bytes in snd_ctl_card_info.
+ * Use SNDRV_CTL_IOCTL_CARD_COMPONENTS to retrieve the full string.
+ *
+ */
+struct snd_ctl_card_components {
+	int card;
+	unsigned int length;
+	unsigned char *components;
+};
+
 typedef int __bitwise snd_ctl_elem_type_t;
 #define	SNDRV_CTL_ELEM_TYPE_NONE	((__force snd_ctl_elem_type_t) 0) /* invalid */
 #define	SNDRV_CTL_ELEM_TYPE_BOOLEAN	((__force snd_ctl_elem_type_t) 1) /* boolean type */
@@ -1198,6 +1209,7 @@ struct snd_ctl_tlv {

 #define SNDRV_CTL_IOCTL_PVERSION	_IOR('U', 0x00, int)
 #define SNDRV_CTL_IOCTL_CARD_INFO	_IOR('U', 0x01, struct snd_ctl_card_info)
+#define SNDRV_CTL_IOCTL_CARD_COMPONENTS	_IOWR('U', 0x02, struct snd_ctl_card_components)
 #define SNDRV_CTL_IOCTL_ELEM_LIST	_IOWR('U', 0x10, struct snd_ctl_elem_list)
 #define SNDRV_CTL_IOCTL_ELEM_INFO	_IOWR('U', 0x11, struct snd_ctl_elem_info)
 #define SNDRV_CTL_IOCTL_ELEM_READ	_IOWR('U', 0x12, struct snd_ctl_elem_value)
diff --git a/sound/core/control.c b/sound/core/control.c
index 374e703d15a9..d793dbf85d15 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -876,9 +876,13 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
 {
 	struct snd_ctl_card_info *info __free(kfree) =
 		kzalloc(sizeof(*info), GFP_KERNEL);
+	ssize_t n;

 	if (! info)
 		return -ENOMEM;
+
+	static_assert(sizeof(info->components) >= 2);
+
 	scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
 		info->card = card->number;
 		strscpy(info->id, card->id, sizeof(info->id));
@@ -886,13 +890,40 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
 		strscpy(info->name, card->shortname, sizeof(info->name));
 		strscpy(info->longname, card->longname, sizeof(info->longname));
 		strscpy(info->mixername, card->mixername, sizeof(info->mixername));
-		strscpy(info->components, card->components, sizeof(info->components));
+		n = strscpy(info->components, card->components_ptr, sizeof(info->components));
+		if (n < 0) // mark the truncation with '>' before NULL terminator
+			info->components[sizeof(info->components) - 2] = '>';
 	}
 	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
 		return -EFAULT;
 	return 0;
 }

+static int snd_ctl_card_components(struct snd_card *card,
+				  struct snd_ctl_card_components __user *_info)
+{
+	struct snd_ctl_card_components info;
+	unsigned int copy_len;
+
+	if (copy_from_user(&info, _info, sizeof(info)))
+		return -EFAULT;
+	if (!info.components)
+		return -EINVAL;
+
+	scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+		copy_len = strlen(card->components_ptr) + 1;
+		info.card = card->number;
+		info.length = copy_len;
+		if (copy_to_user(info.components, card->components_ptr, copy_len))
+			return -EFAULT;
+	}
+
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
 static int snd_ctl_elem_list(struct snd_card *card,
 			     struct snd_ctl_elem_list *list)
 {
@@ -1988,6 +2019,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
 		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
 	case SNDRV_CTL_IOCTL_CARD_INFO:
 		return snd_ctl_card_info(card, ctl, cmd, argp);
+	case SNDRV_CTL_IOCTL_CARD_COMPONENTS:
+		return snd_ctl_card_components(card, argp);
 	case SNDRV_CTL_IOCTL_ELEM_LIST:
 		return snd_ctl_elem_list_user(card, argp);
 	case SNDRV_CTL_IOCTL_ELEM_INFO:
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 4ad571087ff5..d325f9d0b8a1 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -456,6 +456,8 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
 	case SNDRV_CTL_IOCTL_TLV_WRITE:
 	case SNDRV_CTL_IOCTL_TLV_COMMAND:
 		return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
+	case SNDRV_CTL_IOCTL_CARD_COMPONENTS:
+		return snd_ctl_card_components(ctl->card, argp);
 	case SNDRV_CTL_IOCTL_ELEM_LIST32:
 		return snd_ctl_elem_list_compat(ctl->card, argp);
 	case SNDRV_CTL_IOCTL_ELEM_INFO32:
diff --git a/sound/core/init.c b/sound/core/init.c
index 593c05895e11..426ed8916aa9 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -590,6 +590,8 @@ static int snd_card_do_free(struct snd_card *card)
 		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
 #endif
 	snd_device_free_all(card);
+	kfree(card->components_ptr);
+	card->components_ptr_alloc_size = 0;
 	if (card->private_free)
 		card->private_free(card);
 #ifdef CONFIG_SND_CTL_DEBUG
@@ -1036,19 +1038,40 @@ int snd_component_add(struct snd_card *card, const char *component)
 {
 	char *ptr;
 	int len = strlen(component);
+	unsigned int cur_len, need_len;

-	ptr = strstr(card->components, component);
-	if (ptr != NULL) {
-		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
-			return 1;
+	if (card->components_ptr) {
+		ptr = strstr(card->components_ptr, component);
+		if (ptr) {
+			if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
+				return 1;
+		}
+		cur_len = strlen(card->components_ptr) + 1;
+	} else {
+		cur_len = 0;
 	}
-	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
+
+	need_len = cur_len + len + 1;
+	if (need_len > 512) {
 		snd_BUG();
 		return -ENOMEM;
 	}
-	if (card->components[0] != '\0')
-		strcat(card->components, " ");
-	strcat(card->components, component);
+
+	if (need_len > card->components_ptr_alloc_size) {
+		unsigned int new_alloc = roundup(need_len, 32);
+
+		ptr = krealloc(card->components_ptr, new_alloc, GFP_KERNEL);
+		if (!ptr)
+			return -ENOMEM;
+		if (!card->components_ptr)
+			ptr[0] = '\0';
+		card->components_ptr = ptr;
+		card->components_ptr_alloc_size = new_alloc;
+	}
+
+	if (card->components_ptr[0] != '\0')
+		strcat(card->components_ptr, " ");
+	strcat(card->components_ptr, component);
 	return 0;
 }
 EXPORT_SYMBOL(snd_component_add);
--
2.48.1


  reply	other threads:[~2026-03-03 15:02 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-03 14:57 [PATCH v3 1/2] ALSA: control: tidy up whitespaces Maciej Strozek
2026-03-03 14:58 ` Maciej Strozek [this message]
2026-03-03 15:47   ` [PATCH v3 2/2] ALSA: control: add ioctl to retrieve full card components Takashi Iwai
2026-03-03 19:23     ` Jaroslav Kysela
2026-03-04 10:28       ` Takashi Iwai
2026-03-05  9:54     ` Maciej Strozek
2026-03-05 10:04       ` Takashi Iwai
2026-03-05 10:11         ` Maciej Strozek
2026-03-05 10:18           ` Takashi Iwai
2026-03-06  9:39             ` Jaroslav Kysela
2026-03-06 10:43               ` Takashi Iwai
2026-03-09  9:44                 ` Jaroslav Kysela
2026-03-09 10:02                   ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260303145815.9930-2-mstrozek@opensource.cirrus.com \
    --to=mstrozek@opensource.cirrus.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=patches@opensource.cirrus.com \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox