From ac4f15b5cd3aa188017f8c16a1d2dd4b75420b4d Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Thu, 19 Feb 2026 13:46:20 +0000 Subject: [PATCH] control: ucm: add ioctl to retrieve full card components The fixed-size components field in SNDRV_CTL_IOCTL_CARD_INFO can be too small on systems with many audio devices. The kernel [1] will provide a new ioctl to read the full string while truncating the original in card_info if it grows too big. Make sure alsa-lib can read the full string if the original is truncated. [1] https://lore.kernel.org/all/20260430110302.3745917-2-mstrozek@opensource.cirrus.com/ Signed-off-by: Maciej Strozek --- include/local.h | 15 ++++++++++++++- include/sound/uapi/asound.h | 21 ++++++++++++++++++++- src/control/cards.c | 6 +++--- src/control/control_hw.c | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/include/local.h b/include/local.h index a69967593..6eaa1c695 100644 --- a/include/local.h +++ b/include/local.h @@ -101,7 +101,6 @@ #define _snd_pcm_sw_params snd_pcm_sw_params #define _snd_pcm_status snd_pcm_status -#define _snd_ctl_card_info snd_ctl_card_info #define _snd_ctl_elem_id snd_ctl_elem_id #define _snd_ctl_elem_list snd_ctl_elem_list #define _snd_ctl_elem_info snd_ctl_elem_info @@ -226,6 +225,20 @@ #include "seq_midi_event.h" #include "list.h" +/* V2 structure - increased components string from 128 bytes to 512 bytes */ +struct _snd_ctl_card_info { + int card; /* card number */ + int pad; /* reserved for future (was type) */ + unsigned char id[16]; /* ID of card (user selectable) */ + unsigned char driver[16]; /* Driver name */ + unsigned char name[32]; /* Short name of soundcard */ + 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 */ + char components[512]; +}; + + struct _snd_async_handler { enum { SND_ASYNC_HANDLER_GENERIC, diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index bdc9a05ee..8df934036 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -1044,7 +1044,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 */ @@ -1058,6 +1058,24 @@ 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 { + unsigned int type; /* SND_CTL_CARD_BTYPE_* */ + unsigned int data_allocated; /* size of @data buffer in bytes */ + unsigned int data_len; /* in/out: actual data length in bytes */ + unsigned char *data; /* user buffer */ +}; + typedef int __bitwise snd_ctl_elem_type_t; #define SNDRV_CTL_ELEM_TYPE_NONE ((snd_ctl_elem_type_t) 0) /* invalid */ #define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((snd_ctl_elem_type_t) 1) /* boolean type */ @@ -1184,6 +1202,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/src/control/cards.c b/src/control/cards.c index 49e343a9b..9822850cc 100644 --- a/src/control/cards.c +++ b/src/control/cards.c @@ -42,17 +42,17 @@ static int snd_card_load2(const char *control) { int open_dev; - snd_ctl_card_info_t info; + struct snd_ctl_card_info kinfo; open_dev = snd_open_device(control, O_RDONLY); if (open_dev >= 0) { - if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) { + if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &kinfo) < 0) { int err = -errno; close(open_dev); return err; } close(open_dev); - return info.card; + return kinfo.card; } else { return -errno; } diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 962cd7969..e81ea4322 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -129,10 +129,42 @@ static int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe) static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info) { snd_ctl_hw_t *hw = handle->private_data; - if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, info) < 0) { + struct snd_ctl_card_info kinfo; + struct snd_ctl_card_bytes bytes; + + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, &kinfo) < 0) { snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_INFO failed"); return -errno; } + + snd_ctl_card_info_clear(info); + info->card = kinfo.card; + info->pad = kinfo.pad; + snd_strlcpy((char *)info->id, (char *)kinfo.id, sizeof(info->id)); + snd_strlcpy((char *)info->driver, (char *)kinfo.driver, sizeof(info->driver)); + snd_strlcpy((char *)info->name, (char *)kinfo.name, sizeof(info->name)); + snd_strlcpy((char *)info->longname, (char *)kinfo.longname, sizeof(info->longname)); + snd_strlcpy((char *)info->reserved_, (char *)kinfo.reserved_, sizeof(info->reserved_)); + snd_strlcpy((char *)info->mixername, (char *)kinfo.mixername, sizeof(info->mixername)); + if (strnlen((char *)kinfo.components, sizeof(kinfo.components)) == sizeof(kinfo.components) - 1 && + kinfo.components[sizeof(kinfo.components) - 2] == '>') { + bytes.type = SND_CTL_CARD_BTYPE_COMPONENTS; + bytes.data = (unsigned char *)info->components; + bytes.data_allocated = sizeof(info->components); + bytes.data_len = 0; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_BYTES, &bytes) < 0) { + snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_BYTES failed"); + return -errno; + } + if (bytes.data_len != strnlen((char *)bytes.data, sizeof(info->components)) + 1) { + snd_error(CONTROL, "SNDRV_CTL_IOCTL_CARD_BYTES unexpected length %u, should be %zu", + bytes.data_len, strnlen((char *)bytes.data, sizeof(info->components)) + 1); + return -errno; + } + } else { + snd_strlcpy(info->components, (char *)kinfo.components, sizeof(info->components)); + } + return 0; }