* [PATCH v6 1/2] ALSA: control: tidy up whitespaces
@ 2026-07-20 10:35 Maciej Strozek
2026-07-20 10:35 ` [PATCH v6 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
2026-08-05 7:30 ` [PATCH v6 1/2] ALSA: control: tidy up whitespaces Takashi Iwai
0 siblings, 2 replies; 4+ messages in thread
From: Maciej Strozek @ 2026-07-20 10:35 UTC (permalink / raw)
To: Takashi Iwai, Jaroslav Kysela
Cc: linux-sound, linux-kernel, alsa-devel, patches, Maciej Strozek
Clean up trailing whitespace in preparation for the card components
changes.
Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
---
added in v3, no changes since then
---
sound/core/control_compat.c | 2 +-
sound/core/init.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 16bc80555f26..4ad571087ff5 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -417,7 +417,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
break;
}
return snd_ctl_elem_add(file, data, replace);
-}
+}
enum {
SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
diff --git a/sound/core/init.c b/sound/core/init.c
index 8c5850ce08a0..0372756048cd 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -721,7 +721,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
int len, loops;
bool is_default = false;
char *id;
-
+
copy_valid_id_string(card, src, nid);
id = card->id;
@@ -1030,7 +1030,7 @@ int __init snd_card_info_init(void)
*
* Return: Zero otherwise a negative error code.
*/
-
+
int snd_component_add(struct snd_card *card, const char *component)
{
char *ptr;
--
2.48.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v6 2/2] ALSA: control: add ioctl to retrieve full card components
2026-07-20 10:35 [PATCH v6 1/2] ALSA: control: tidy up whitespaces Maciej Strozek
@ 2026-07-20 10:35 ` Maciej Strozek
2026-07-31 10:59 ` Takashi Iwai
2026-08-05 7:30 ` [PATCH v6 1/2] ALSA: control: tidy up whitespaces Takashi Iwai
1 sibling, 1 reply; 4+ messages in thread
From: Maciej Strozek @ 2026-07-20 10:35 UTC (permalink / raw)
To: Takashi Iwai, Jaroslav Kysela
Cc: linux-sound, linux-kernel, alsa-devel, patches, Maciej Strozek
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 SNDRV_CTL_IOCTL_CARD_BYTES that carries a variable-length payload
selected by a type discriminator. The first defined type
SND_CTL_CARD_BTYPE_COMPONENTS returns the full components string. The
ioctl is designed to be reused for other variable-length card payloads
in the future.
The user-space caller may set data_allocated == 0 (or data == NULL) to
query the required length; otherwise the kernel copies the payload into
the user buffer and writes back the actual length in data_len.
When the legacy components field in struct snd_ctl_card_info is
truncated, '>' is written just before the NUL terminator to signal to
user-space that the full string is available via the new ioctl.
card->components is now dynamically allocated and grown in 32 byte
increments via krealloc(), capped at 512 bytes.
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 v6:
- take the snd_ioctl_rwsem write lock in snd_component_add()
Changes for v5:
- use __u64 and u64_to_user_ptr() instead of 32bit compat layer
- handle a NULL card->components
Changes for v4:
- replaced snd_ctl_card_components with snd_ctl_card_bytes for possible
future extensions
- support query mode (data_allocated == 0 or data == NULL)
- added compat 32-bit
- renamed card->components_ptr / components_ptr_alloc_size to
card->components / components_alloc_size
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/control.h | 3 ++
include/sound/core.h | 4 +--
include/uapi/sound/asound.h | 22 ++++++++++++-
sound/core/control.c | 64 +++++++++++++++++++++++++++++++++++--
sound/core/control_compat.c | 1 +
sound/core/init.c | 36 ++++++++++++++++++---
6 files changed, 120 insertions(+), 10 deletions(-)
diff --git a/include/sound/control.h b/include/sound/control.h
index e07f6b960641..909db0d0485d 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -7,6 +7,7 @@
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
+#include <linux/rwsem.h>
#include <linux/wait.h>
#include <linux/nospec.h>
#include <sound/asound.h>
@@ -167,6 +168,8 @@ snd_ctl_find_id_mixer(struct snd_card *card, const char *name)
int snd_ctl_create(struct snd_card *card);
+extern struct rw_semaphore snd_ioctl_rwsem;
+
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn);
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn);
#ifdef CONFIG_COMPAT
diff --git a/include/sound/core.h b/include/sound/core.h
index 404785b7d885..2ca24ac7e37f 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -108,8 +108,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; /* card components, space-delimited */
+ unsigned int components_alloc_size; /* current allocation size of components */
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..500599213f93 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,25 @@ 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_BYTES with type SND_CTL_CARD_BTYPE_COMPONENTS
+ * to retrieve the full string.
+ */
+
+/* Type values for struct snd_ctl_card_bytes::type */
+enum {
+ SND_CTL_CARD_BTYPE_COMPONENTS = 1, /* full card components string */
+};
+
+struct snd_ctl_card_bytes {
+ __u32 type; /* SND_CTL_CARD_BTYPE_* */
+ __u32 data_allocated; /* size of @data buffer in bytes */
+ __u32 data_len; /* in/out: actual data length in bytes */
+ __u32 reserved; /* explicit pad */
+ __u64 data; /* user buffer (pointer stored as __u64) */
+};
+
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 +1217,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_BYTES _IOWR('U', 0x02, struct snd_ctl_card_bytes)
#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 1116a40d11ae..78ce7bc936d2 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -38,7 +38,7 @@ struct snd_kctl_ioctl {
snd_kctl_ioctl_func_t fioctl;
};
-static DECLARE_RWSEM(snd_ioctl_rwsem);
+DECLARE_RWSEM(snd_ioctl_rwsem);
static DECLARE_RWSEM(snd_ctl_layer_rwsem);
static LIST_HEAD(snd_control_ioctls);
#ifdef CONFIG_COMPAT
@@ -872,23 +872,81 @@ 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) {
+ const char *components = card->components;
+
+ if (!components)
+ components = "";
+
info->card = card->number;
strscpy(info->id, card->id, sizeof(info->id));
strscpy(info->driver, card->driver, sizeof(info->driver));
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, components, 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_bytes(struct snd_card *card,
+ struct snd_ctl_card_bytes *info,
+ unsigned int __user *data_len_out)
+{
+ unsigned int data_len;
+
+ switch (info->type) {
+ case SND_CTL_CARD_BTYPE_COMPONENTS:
+ scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+ const char *components = card->components;
+
+ if (!components)
+ components = "";
+
+ data_len = strlen(components) + 1;
+
+ if (!info->data || info->data_allocated == 0)
+ break;
+
+ if (info->data_allocated < data_len)
+ return -ENOMEM;
+
+ if (copy_to_user(u64_to_user_ptr(info->data), components, data_len))
+ return -EFAULT;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (put_user(data_len, data_len_out))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int snd_ctl_card_bytes_user(struct snd_card *card,
+ struct snd_ctl_card_bytes __user *_info)
+{
+ struct snd_ctl_card_bytes info;
+
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+
+ return snd_ctl_card_bytes(card, &info, &_info->data_len);
+}
+
static int snd_ctl_elem_list(struct snd_card *card,
struct snd_ctl_elem_list *list)
{
@@ -1986,6 +2044,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_BYTES:
+ return snd_ctl_card_bytes_user(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..f14d9f5e94be 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -446,6 +446,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
case SNDRV_CTL_IOCTL_CARD_INFO:
+ case SNDRV_CTL_IOCTL_CARD_BYTES:
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
case SNDRV_CTL_IOCTL_POWER:
case SNDRV_CTL_IOCTL_POWER_STATE:
diff --git a/sound/core/init.c b/sound/core/init.c
index 0372756048cd..19ec68db561b 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -589,6 +589,9 @@ 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);
+ card->components = NULL;
+ card->components_alloc_size = 0;
if (card->private_free)
card->private_free(card);
#ifdef CONFIG_SND_CTL_DEBUG
@@ -1035,16 +1038,39 @@ 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;
+ guard(rwsem_write)(&snd_ioctl_rwsem);
+
+ if (card->components) {
+ ptr = strstr(card->components, component);
+ if (ptr) {
+ if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */
+ return 1;
+ }
+ cur_len = strlen(card->components) + 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 (need_len > card->components_alloc_size) {
+ unsigned int new_alloc = roundup(need_len, 32);
+
+ ptr = krealloc(card->components, new_alloc, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+ if (!card->components)
+ ptr[0] = '\0';
+ card->components = ptr;
+ card->components_alloc_size = new_alloc;
+ }
+
if (card->components[0] != '\0')
strcat(card->components, " ");
strcat(card->components, component);
--
2.48.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v6 2/2] ALSA: control: add ioctl to retrieve full card components
2026-07-20 10:35 ` [PATCH v6 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
@ 2026-07-31 10:59 ` Takashi Iwai
0 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-07-31 10:59 UTC (permalink / raw)
To: Jaroslav Kysela
Cc: Maciej Strozek, Takashi Iwai, linux-sound, linux-kernel,
alsa-devel, patches
On Mon, 20 Jul 2026 12:35:05 +0200,
Maciej Strozek wrote:
>
> 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 SNDRV_CTL_IOCTL_CARD_BYTES that carries a variable-length payload
> selected by a type discriminator. The first defined type
> SND_CTL_CARD_BTYPE_COMPONENTS returns the full components string. The
> ioctl is designed to be reused for other variable-length card payloads
> in the future.
>
> The user-space caller may set data_allocated == 0 (or data == NULL) to
> query the required length; otherwise the kernel copies the payload into
> the user buffer and writes back the actual length in data_len.
>
> When the legacy components field in struct snd_ctl_card_info is
> truncated, '>' is written just before the NUL terminator to signal to
> user-space that the full string is available via the new ioctl.
>
> card->components is now dynamically allocated and grown in 32 byte
> increments via krealloc(), capped at 512 bytes.
>
> 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 v6:
> - take the snd_ioctl_rwsem write lock in snd_component_add()
> Changes for v5:
> - use __u64 and u64_to_user_ptr() instead of 32bit compat layer
> - handle a NULL card->components
> Changes for v4:
> - replaced snd_ctl_card_components with snd_ctl_card_bytes for possible
> future extensions
> - support query mode (data_allocated == 0 or data == NULL)
> - added compat 32-bit
> - renamed card->components_ptr / components_ptr_alloc_size to
> card->components / components_alloc_size
> 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()
Looks OK for me. Jaroslav, could you review, too?
If nothing obvious is found, I'm going to take for 7.3.
thanks,
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v6 1/2] ALSA: control: tidy up whitespaces
2026-07-20 10:35 [PATCH v6 1/2] ALSA: control: tidy up whitespaces Maciej Strozek
2026-07-20 10:35 ` [PATCH v6 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
@ 2026-08-05 7:30 ` Takashi Iwai
1 sibling, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-08-05 7:30 UTC (permalink / raw)
To: Maciej Strozek
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
alsa-devel, patches
On Mon, 20 Jul 2026 12:35:04 +0200,
Maciej Strozek wrote:
>
> Clean up trailing whitespace in preparation for the card components
> changes.
>
> Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
> ---
> added in v3, no changes since then
Now I applied both to for-next branch.
Please submit the corresponding alsa-lib update (either alsa-devel ML
or github PR).
thanks,
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-05 7:30 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20 10:35 [PATCH v6 1/2] ALSA: control: tidy up whitespaces Maciej Strozek
2026-07-20 10:35 ` [PATCH v6 2/2] ALSA: control: add ioctl to retrieve full card components Maciej Strozek
2026-07-31 10:59 ` Takashi Iwai
2026-08-05 7:30 ` [PATCH v6 1/2] ALSA: control: tidy up whitespaces Takashi Iwai
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.