include/sound/core.h | 2 +- include/uapi/sound/asound.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
The components field of snd_card can run out of space in new systems which
use many audio devices, hence increase its size to 256 bytes.
Along with this change, bump the CTL protocol version to 2.0.10
Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
---
include/sound/core.h | 2 +-
include/uapi/sound/asound.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/sound/core.h b/include/sound/core.h
index 64327e971122..0eb2e3ee0dd5 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -87,7 +87,7 @@ 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
+ char components[256]; /* card components delimited with
space */
struct module *module; /* top-level module */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 5a049eeaecce..c302698fb685 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 */
@@ -1069,7 +1069,7 @@ struct snd_ctl_card_info {
unsigned char longname[80]; /* name + info text about soundcard */
unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */
unsigned char mixername[80]; /* visual mixer identification */
- unsigned char components[128]; /* card components / fine identification, delimited with one space (AC97 etc..) */
+ unsigned char components[256]; /* card components / fine identification, delimited with one space (AC97 etc..) */
};
typedef int __bitwise snd_ctl_elem_type_t;
--
2.48.1
On 10/23/25 11:27, Maciej Strozek wrote:
> The components field of snd_card can run out of space in new systems which
> use many audio devices, hence increase its size to 256 bytes.
> @@ -1069,7 +1069,7 @@ struct snd_ctl_card_info {
> unsigned char longname[80]; /* name + info text about soundcard */
> unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */
> unsigned char mixername[80]; /* visual mixer identification */
> - unsigned char components[128]; /* card components / fine identification, delimited with one space (AC97 etc..) */
> + unsigned char components[256]; /* card components / fine identification, delimited with one space (AC97 etc..) */
Unfortunately, this change will introduce kABI breakage (ioctl number change -
structure size).
You can probably define another struct snd_ctl_card_info and
SNDRV_CTL_IOCTL_CARD_INFO and update alsa-lib to use it depending the protocol
version.
Or, we may introduce a separate ioctl for the components string. The stripped
components string in struct snd_ctl_card_info may have a special ASCII mark
like '>' at the end of string specifying the availability of the complete
string through another ioctl. I would prefer this solution.
Also, the components string may be dynamic in the kernel structure (pointer)
to save some space. 256 bytes is not small number.
Jaroslav
--
Jaroslav Kysela <perex@perex.cz>
Linux Sound Maintainer; ALSA Project; Red Hat, Inc.
On Thu, 23 Oct 2025 13:56:26 +0200,
Jaroslav Kysela wrote:
>
> On 10/23/25 11:27, Maciej Strozek wrote:
> > The components field of snd_card can run out of space in new systems which
> > use many audio devices, hence increase its size to 256 bytes.
>
> > @@ -1069,7 +1069,7 @@ struct snd_ctl_card_info {
> > unsigned char longname[80]; /* name + info text about soundcard */
> > unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */
> > unsigned char mixername[80]; /* visual mixer identification */
> > - unsigned char components[128]; /* card components / fine identification, delimited with one space (AC97 etc..) */
> > + unsigned char components[256]; /* card components / fine identification, delimited with one space (AC97 etc..) */
> Unfortunately, this change will introduce kABI breakage (ioctl number
> change - structure size).
>
> You can probably define another struct snd_ctl_card_info and
> SNDRV_CTL_IOCTL_CARD_INFO and update alsa-lib to use it depending the
> protocol version.
>
> Or, we may introduce a separate ioctl for the components string. The
> stripped components string in struct snd_ctl_card_info may have a
> special ASCII mark like '>' at the end of string specifying the
> availability of the complete string through another ioctl. I would
> prefer this solution.
>
> Also, the components string may be dynamic in the kernel structure
> (pointer) to save some space. 256 bytes is not small number.
As Jaroslav suggested, we need a different solution to keep the
compatibility.
My gut feeling is for the option to provide a new ioctl as it can be
most straightforward, but we can discuss further which is the good
choice.
thanks,
Takashi
W dniu pon, 27.10.2025 o godzinie 10∶04 +0100, użytkownik Takashi Iwai
napisał:
> On Thu, 23 Oct 2025 13:56:26 +0200,
> Jaroslav Kysela wrote:
> >
> > Or, we may introduce a separate ioctl for the components string.
> > The
> > stripped components string in struct snd_ctl_card_info may have a
> > special ASCII mark like '>' at the end of string specifying the
> > availability of the complete string through another ioctl. I would
> > prefer this solution.
> >
> > Also, the components string may be dynamic in the kernel structure
> > (pointer) to save some space. 256 bytes is not small number.
>
> As Jaroslav suggested, we need a different solution to keep the
> compatibility.
>
> My gut feeling is for the option to provide a new ioctl as it can be
> most straightforward, but we can discuss further which is the good
> choice.
>
Thank you for the advice! Not sure if I understand Jaroslav's new ioctl
approach properly, can you have a look at the quick draft below and say
if this is (more or less) what you would expect? If it is OK then I
will prepare a proper V2 patch here with a corresponding change for
alsa-lib repo too.
diff --git a/include/sound/core.h b/include/sound/core.h
index 0eb2e3ee0dd5..c4e52fb1704b 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -88,7 +88,9 @@ struct snd_card {
char irq_descr[32]; /* Interrupt description */
char mixername[80]; /* mixer name */
char components[256]; /* card components delimited
with
- space
*/
+ space */
+ char *components_pointer; /* full components string */
+ size_t components_pointer_len; /* length of full components
string */
struct module *module; /* top-level module */
void *private_data; /* private data for soundcard
*/
@@ -297,6 +299,7 @@ int snd_card_info_init(void);
int snd_card_add_dev_attr(struct snd_card *card,
const struct attribute_group *group);
int snd_component_add(struct snd_card *card, const char *component);
+int snd_card_add_components(struct snd_card *card, const char
*component);
int snd_card_file_add(struct snd_card *card, struct file *file);
int snd_card_file_remove(struct snd_card *card, struct file *file);
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index c302698fb685..7d53f6da59e2 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -1069,7 +1069,12 @@ struct snd_ctl_card_info {
unsigned char longname[80]; /* name + info text about
soundcard */
unsigned char reserved_[16]; /* reserved for future (was ID
of mixer) */
unsigned char mixername[80]; /* visual mixer identification
*/
- unsigned char components[256]; /* card components / fine
identification, delimited with one space (AC97 etc..) */
+ unsigned char components[128]; /* card components / fine
identification, delimited with one space (AC97 etc..) */
+};
+
+struct snd_ctl_card_components {
+ int len;
+ unsigned char *components;
};
typedef int __bitwise snd_ctl_elem_type_t;
@@ -1198,6 +1203,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 9c3fd5113a61..5a96a2ed4a45 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -886,6 +886,23 @@ static int snd_ctl_card_info(struct snd_card
*card, struct snd_ctl_file * ctl,
return 0;
}
+static int snd_ctl_card_components(struct snd_card *card, void __user
*arg)
+{
+ struct snd_ctl_card_components *info __free(kfree) = NULL;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+ strscpy(info->components, card->components, info->len);
+ }
+
+ if (copy_to_user(arg, info, sizeof(struct
snd_ctl_card_components)))
+ return -EFAULT;
+ return 0;
+}
+
static int snd_ctl_elem_list(struct snd_card *card,
struct snd_ctl_elem_list *list)
{
@@ -1914,6 +1931,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 6459809ed364..edb7b28d8177 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -416,7 +416,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),
@@ -445,6 +445,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_COMPONENTS:
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 c372b3228785..a7ac62ace1dc 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -587,6 +587,9 @@ static int snd_card_do_free(struct snd_card *card)
snd_device_free_all(card);
if (card->private_free)
card->private_free(card);
+ kfree(card->components_pointer);
+ card->components_pointer = NULL;
+ card->components_pointer_len = 0;
if (snd_info_card_free(card) < 0) {
dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */
@@ -714,7 +717,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;
@@ -1023,11 +1026,13 @@ 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;
int len = strlen(component);
+ int new_len;
+ char *buffer;
ptr = strstr(card->components, component);
if (ptr != NULL) {
@@ -1035,8 +1040,28 @@ int snd_component_add(struct snd_card *card,
const char *component)
return 1;
}
if (strlen(card->components) + 1 + len + 1 > sizeof(card-
>components)) {
- snd_BUG();
- return -ENOMEM;
+ if (card->components[sizeof(card->components) - 2] !=
'>') {
+ // TODO: figure out if it is not deleting a
character from an existing component
+ card->components[sizeof(card->components) - 2]
= '>';
+ card->components[sizeof(card->components) - 1]
= '\0';
+ }
+
+ if (card->components_pointer) {
+ new_len = strlen(card->components_pointer) + 1
+ len;
+ buffer = krealloc(card->components_pointer,
new_len, GFP_KERNEL);
+ } else {
+ new_len = strlen(card->components) + 1 + len;
+ buffer = kmalloc(new_len, GFP_KERNEL);
+ memcpy(buffer, card->components, strlen(card-
>components));
+ }
+ if (!buffer)
+ return -ENOMEM;
+
+ strcat(buffer, component);
+
+ card->components_pointer = buffer;
+ card->components_pointer_len = new_len;
+
}
if (card->components[0] != '\0')
strcat(card->components, " ");
--
Regards,
Maciej
On 10/27/25 12:50, Maciej Strozek wrote:
> W dniu pon, 27.10.2025 o godzinie 10∶04 +0100, użytkownik Takashi Iwai
> napisał:
>> On Thu, 23 Oct 2025 13:56:26 +0200,
>> Jaroslav Kysela wrote:
>>>
>>> Or, we may introduce a separate ioctl for the components string.
>>> The
>>> stripped components string in struct snd_ctl_card_info may have a
>>> special ASCII mark like '>' at the end of string specifying the
>>> availability of the complete string through another ioctl. I would
>>> prefer this solution.
>>>
>>> Also, the components string may be dynamic in the kernel structure
>>> (pointer) to save some space. 256 bytes is not small number.
>>
>> As Jaroslav suggested, we need a different solution to keep the
>> compatibility.
>>
>> My gut feeling is for the option to provide a new ioctl as it can be
>> most straightforward, but we can discuss further which is the good
>> choice.
>>
>
> Thank you for the advice! Not sure if I understand Jaroslav's new ioctl
> approach properly, can you have a look at the quick draft below and say
> if this is (more or less) what you would expect? If it is OK then I
> will prepare a proper V2 patch here with a corresponding change for
> alsa-lib repo too.
>
>
> diff --git a/include/sound/core.h b/include/sound/core.h
> index 0eb2e3ee0dd5..c4e52fb1704b 100644
> --- a/include/sound/core.h
> +++ b/include/sound/core.h
> @@ -88,7 +88,9 @@ struct snd_card {
> char irq_descr[32]; /* Interrupt description */
> char mixername[80]; /* mixer name */
> char components[256]; /* card components delimited
> with
> - space
> */
> + space */
> + char *components_pointer; /* full components string */
Keep only this, remove the array and don't use the length member (null
terminated string).
> + size_t components_pointer_len; /* length of full components
> string */
> struct module *module; /* top-level module */
>
> void *private_data; /* private data for soundcard
> */
> @@ -297,6 +299,7 @@ int snd_card_info_init(void);
> int snd_card_add_dev_attr(struct snd_card *card,
> const struct attribute_group *group);
> int snd_component_add(struct snd_card *card, const char *component);
> +int snd_card_add_components(struct snd_card *card, const char
> *component);
> int snd_card_file_add(struct snd_card *card, struct file *file);
> int snd_card_file_remove(struct snd_card *card, struct file *file);
>
> diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
> index c302698fb685..7d53f6da59e2 100644
> --- a/include/uapi/sound/asound.h
> +++ b/include/uapi/sound/asound.h
...
> +struct snd_ctl_card_components {
> + int len;
> + unsigned char *components;
> };
Use array here. It makes things simpler. Perhaps, we can use 512 bytes /
characters here to avoid future problems.
> int snd_component_add(struct snd_card *card, const char *component)
> {
> char *ptr;
> int len = strlen(component);
> + int new_len;
> + char *buffer;
>
> ptr = strstr(card->components, component);
> if (ptr != NULL) {
> @@ -1035,8 +1040,28 @@ int snd_component_add(struct snd_card *card,
> const char *component)
> return 1;
> }
> if (strlen(card->components) + 1 + len + 1 > sizeof(card-
>> components)) {
I don't think that it's the right place to do the striping here. Just keep the
full string in snd_card structure and handle the split/trim in
snd_ctl_card_info().
The new ioctl should return the full components string.
Jaroslav
--
Jaroslav Kysela <perex@perex.cz>
Linux Sound Maintainer; ALSA Project; Red Hat, Inc.
Hi,
On Mon, Oct 27, 2025 at 11:50:32AM +0000, Maciej Strozek wrote:
> diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
> index c302698fb685..7d53f6da59e2 100644
> --- a/include/uapi/sound/asound.h
> +++ b/include/uapi/sound/asound.h
> @@ -1069,7 +1069,12 @@ struct snd_ctl_card_info {
> unsigned char longname[80];
> unsigned char reserved_[16];
> unsigned char mixername[80];
> - unsigned char components[256];
> + unsigned char components[128];
> +};
Any approach to change the result of 'sizeof(struct snd_ctl_card_info)'
breaks ABI to userspace application. This is the reason that Jaroslav
addresses to the new PCM ioctl command. Unless, the userspace
application build with the UAPI header provided by the existing version
of Linux kernel does not work as expected in your version of Linux
kernel, in respect to the sound subsystem.
Regards
Takashi Sakamoto
W dniu wto, 28.10.2025 o godzinie 09∶42 +0900, użytkownik Takashi
Sakamoto napisał:
> Hi,
>
> On Mon, Oct 27, 2025 at 11:50:32AM +0000, Maciej Strozek wrote:
> > diff --git a/include/uapi/sound/asound.h
> > b/include/uapi/sound/asound.h
> > index c302698fb685..7d53f6da59e2 100644
> > --- a/include/uapi/sound/asound.h
> > +++ b/include/uapi/sound/asound.h
> > @@ -1069,7 +1069,12 @@ struct snd_ctl_card_info {
> > unsigned char longname[80];
> > unsigned char reserved_[16];
> > unsigned char mixername[80];
> > - unsigned char components[256];
> > + unsigned char components[128];
> > +};
>
> Any approach to change the result of 'sizeof(struct
> snd_ctl_card_info)'
> breaks ABI to userspace application. This is the reason that Jaroslav
> addresses to the new PCM ioctl command. Unless, the userspace
> application build with the UAPI header provided by the existing
> version
> of Linux kernel does not work as expected in your version of Linux
> kernel, in respect to the sound subsystem.
>
Apologies for the confusion, the draft was based on the initial patch
that increased the size of the components, so this snippet is actually
bringing back the original size. This will not be present in V2.
>
> Regards
>
> Takashi Sakamoto
--
Regards,
Maciej
On 27/10/2025 11:50 am, Maciej Strozek wrote:
> W dniu pon, 27.10.2025 o godzinie 10∶04 +0100, użytkownik Takashi Iwai
> napisał:
>> On Thu, 23 Oct 2025 13:56:26 +0200,
>> Jaroslav Kysela wrote:
>>>
>>> Or, we may introduce a separate ioctl for the components string.
>>> The
>>> stripped components string in struct snd_ctl_card_info may have a
>>> special ASCII mark like '>' at the end of string specifying the
>>> availability of the complete string through another ioctl. I would
>>> prefer this solution.
>>>
>>> Also, the components string may be dynamic in the kernel structure
>>> (pointer) to save some space. 256 bytes is not small number.
>>
>> As Jaroslav suggested, we need a different solution to keep the
>> compatibility.
>>
>> My gut feeling is for the option to provide a new ioctl as it can be
>> most straightforward, but we can discuss further which is the good
>> choice.
>>
>
> Thank you for the advice! Not sure if I understand Jaroslav's new ioctl
> approach properly, can you have a look at the quick draft below and say
> if this is (more or less) what you would expect? If it is OK then I
> will prepare a proper V2 patch here with a corresponding change for
> alsa-lib repo too.
>
>
> diff --git a/include/sound/core.h b/include/sound/core.h
> index 0eb2e3ee0dd5..c4e52fb1704b 100644
> --- a/include/sound/core.h
> +++ b/include/sound/core.h
> @@ -88,7 +88,9 @@ struct snd_card {
> char irq_descr[32]; /* Interrupt description */
> char mixername[80]; /* mixer name */
> char components[256]; /* card components delimited
> with
> - space
> */
> + space */
> + char *components_pointer; /* full components string */
> + size_t components_pointer_len; /* length of full components
> string */
You don't need components_pointer_len because you can mandate that
components_pointer must point to a NUL-terminated string.
I expect the new ioctl should require the caller to provide a pointer to
a buffer into which the string will be copied, and the length of that
buffer. It would return an error if the provided buffer is not long
enough to copy the content of components_pointer. Usual protocol is
that it can pass a NULL buffer pointer to retrieve the length of the
string so it knows how big a buffer to allocate.
I assume the ideal API would be that the new ioctl will return the
contents of the old components[] string if there isn't a new
components_pointer string. So if the kernel and user code both support
the new ioctl the user code only needs to call that new ioctl.
© 2016 - 2026 Red Hat, Inc.