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
next prev parent 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.