From 22c3701630352be8a8f31a18d7617412eb9ec67a 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/20260303145815.9930-1-mstrozek@opensource.cirrus.com/ Signed-off-by: Maciej Strozek --- include/local.h | 15 ++++++++++++++- include/sound/uapi/asound.h | 14 +++++++++++++- src/control/cards.c | 6 +++--- src/control/control_hw.c | 32 +++++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/include/local.h b/include/local.h index a6996759..6eaa1c69 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 bdc9a05e..4c8328b6 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,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; /* card number */ + unsigned int length; /* returned length of components string for ioctl query */ + unsigned char *components; /* user buffer for components string */ +}; + 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 +1195,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/src/control/cards.c b/src/control/cards.c index 49e343a9..9822850c 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 962cd796..068e178e 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -129,10 +129,40 @@ 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_components comp; + + 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] == '>') { + comp.components = (unsigned char *)info->components; + comp.length = sizeof(info->components); + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_COMPONENTS, &comp) < 0) { + snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_COMPONENTS failed"); + return -errno; + } + if (comp.length != strnlen((char *)comp.components, sizeof(info->components)) + 1 ) { + snd_error(CONTROL, "SNDRV_CTL_IOCTL_CARD_COMPONENTS unexpected length %u, should be %u", + comp.length, strnlen((char *)comp.components, sizeof(info->components)) + 1); + return -errno; + } + } else { + snd_strlcpy(info->components, (char *)kinfo.components, sizeof(info->components)); + } + return 0; }