From d4cf5da490cad4fa3094f8e51d37b3437cb29812 Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Thu, 22 Jan 2026 09:14:17 +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 the code falls back to original if the new ioctl is not supported. [1]: https://lore.kernel.org/all/20260122111249.67319-1-mstrozek@opensource.cirrus.com/ Signed-off-by: Maciej Strozek --- aserver/aserver.c | 3 ++ include/aserver.h | 1 + include/control.h | 22 +++++++++++++ include/local.h | 1 + include/mixer_abst.h | 1 + include/sound/uapi/asound.h | 15 ++++++++- src/control/control.c | 61 +++++++++++++++++++++++++++++++++++++ src/control/control_ext.c | 10 ++++++ src/control/control_hw.c | 11 +++++++ src/control/control_local.h | 1 + src/control/control_remap.c | 7 +++++ src/control/control_shm.c | 10 ++++++ src/mixer/simple_abst.c | 22 +++++++++++-- src/ucm/ucm_local.h | 1 + src/ucm/ucm_subs.c | 2 ++ src/ucm/utils.c | 10 ++++++ test/control.c | 7 ++++- 17 files changed, 180 insertions(+), 5 deletions(-) diff --git a/aserver/aserver.c b/aserver/aserver.c index 1ef6ef56..ce23f103 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -647,6 +647,9 @@ static int ctl_shm_cmd(client_t *client) case SNDRV_CTL_IOCTL_CARD_INFO: ctrl->result = snd_ctl_card_info(ctl, &ctrl->u.card_info); break; + case SNDRV_CTL_IOCTL_CARD_COMPONENTS: + ctrl->result = snd_ctl_card_components(ctl, &ctrl->u.card_components); + break; case SNDRV_CTL_IOCTL_ELEM_LIST: { size_t maxsize = CTL_SHM_DATA_MAXLEN; diff --git a/include/aserver.h b/include/aserver.h index dc31706c..d24485f9 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -123,6 +123,7 @@ typedef struct { int device; int subscribe_events; snd_ctl_card_info_t card_info; + snd_ctl_card_components_t card_components; snd_ctl_elem_list_t element_list; snd_ctl_elem_info_t element_info; snd_ctl_elem_value_t element_read; diff --git a/include/control.h b/include/control.h index e96cc068..fbb3574c 100644 --- a/include/control.h +++ b/include/control.h @@ -84,6 +84,11 @@ typedef struct snd_aes_iec958 { */ typedef struct _snd_ctl_card_info snd_ctl_card_info_t; +/** + * \brief CTL card components container. + */ +typedef struct _snd_ctl_card_components snd_ctl_card_components_t; + /** CTL element identifier container */ typedef struct _snd_ctl_elem_id snd_ctl_elem_id_t; @@ -394,6 +399,7 @@ int snd_ctl_poll_descriptors(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int s int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe); int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info); +int snd_ctl_card_components(snd_ctl_t *ctl, snd_ctl_card_components_t *components); int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list); int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info); int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *data); @@ -508,6 +514,22 @@ const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj); const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj); const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj); +size_t snd_ctl_card_components_sizeof(void); +/** \hideinitializer + * \brief allocate an invalid #snd_ctl_card_components_t using standard alloca + * \param ptr returned pointer + */ +#define snd_ctl_card_components_alloca(ptr) __snd_alloca(ptr, snd_ctl_card_components) +int snd_ctl_card_components_malloc(snd_ctl_card_components_t **ptr); +void snd_ctl_card_components_free(snd_ctl_card_components_t *obj); +void snd_ctl_card_components_clear(snd_ctl_card_components_t *obj); +void snd_ctl_card_components_copy(snd_ctl_card_components_t *dst, const snd_ctl_card_components_t *src); +int snd_ctl_card_components_get_card(const snd_ctl_card_components_t *obj); +unsigned int snd_ctl_card_components_get_length(const snd_ctl_card_components_t *obj); +const char *snd_ctl_card_components_get_string(const snd_ctl_card_components_t *obj); + +size_t snd_ctl_elem_list_sizeof(void); + size_t snd_ctl_event_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ctl_event_t using standard alloca diff --git a/include/local.h b/include/local.h index a6996759..b6019a3b 100644 --- a/include/local.h +++ b/include/local.h @@ -102,6 +102,7 @@ #define _snd_pcm_status snd_pcm_status #define _snd_ctl_card_info snd_ctl_card_info +#define _snd_ctl_card_components snd_ctl_card_components #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 diff --git a/include/mixer_abst.h b/include/mixer_abst.h index 8807eae3..fa3aae93 100644 --- a/include/mixer_abst.h +++ b/include/mixer_abst.h @@ -75,6 +75,7 @@ typedef struct _sm_class_basic { snd_ctl_t *ctl; snd_hctl_t *hctl; snd_ctl_card_info_t *info; + snd_ctl_card_components_t *card_components; } sm_class_basic_t; struct sm_elem_ops { diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index bdc9a05e..769e7864 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,18 @@ 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. + */ +#define SNDRV_CTL_COMPONENTS_LEN 512 + +struct snd_ctl_card_components { + int card; /* card number */ + unsigned int length; /* returned length of components string */ + unsigned char components[SNDRV_CTL_COMPONENTS_LEN]; +}; + 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 +1196,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/control.c b/src/control/control.c index a08ffea6..5fd72f27 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -388,6 +388,19 @@ int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) return ctl->ops->card_info(ctl, info); } +/** + * \brief Get information about the card components. + * \param ctl The CTL handle. + * \param components The card components information is stored here. + * \return 0 on success, otherwise a negative error code. + */ +int snd_ctl_card_components(snd_ctl_t *ctl, snd_ctl_card_components_t *components) +{ + assert(ctl && components); + return ctl->ops->card_components(ctl, components); +} + + /** * \brief Get a list of element identifiers * @@ -2285,6 +2298,54 @@ const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj) return (const char *)obj->components; } +size_t snd_ctl_card_components_sizeof() +{ + return sizeof(snd_ctl_card_components_t); +} + +int snd_ctl_card_components_malloc(snd_ctl_card_components_t **ptr) +{ + assert(ptr); + *ptr = calloc(1, sizeof(snd_ctl_card_components_t)); + if (!*ptr) + return -ENOMEM; + return 0; +} + +void snd_ctl_card_components_free(snd_ctl_card_components_t *obj) +{ + free(obj); +} + +void snd_ctl_card_components_clear(snd_ctl_card_components_t *obj) +{ + memset(obj, 0, sizeof(snd_ctl_card_components_t)); +} + +void snd_ctl_card_components_copy(snd_ctl_card_components_t *dst, const snd_ctl_card_components_t *src) +{ + assert(dst && src); + *dst = *src; +} + +int snd_ctl_card_components_get_card(const snd_ctl_card_components_t *obj) +{ + assert(obj); + return obj->card; +} + +unsigned int snd_ctl_card_components_get_length(const snd_ctl_card_components_t *obj) +{ + assert(obj); + return obj->length; +} + +const char *snd_ctl_card_components_get_string(const snd_ctl_card_components_t *obj) +{ + assert(obj); + return (const char *)obj->components; +} + /** * \brief get size of #snd_ctl_event_t * \return size in bytes diff --git a/src/control/control_ext.c b/src/control/control_ext.c index f3e91501..fe163a03 100644 --- a/src/control/control_ext.c +++ b/src/control/control_ext.c @@ -89,6 +89,15 @@ static int snd_ctl_ext_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info) return 0; } +static int snd_ctl_ext_card_components(snd_ctl_t *handle, snd_ctl_card_components_t *components) +{ + snd_ctl_ext_t *ext = handle->private_data; + + memset(components, 0, sizeof(*components)); + components->card = ext->card_idx; + return 0; +} + static int snd_ctl_ext_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list) { snd_ctl_ext_t *ext = handle->private_data; @@ -469,6 +478,7 @@ static const snd_ctl_ops_t snd_ctl_ext_ops = { .async = snd_ctl_ext_async, .subscribe_events = snd_ctl_ext_subscribe_events, .card_info = snd_ctl_ext_card_info, + .card_components = snd_ctl_ext_card_components, .element_list = snd_ctl_ext_elem_list, .element_info = snd_ctl_ext_elem_info, .element_add = snd_ctl_ext_elem_add, diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 962cd796..5761c0da 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -136,6 +136,16 @@ static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info) return 0; } +static int snd_ctl_hw_card_components(snd_ctl_t *handle, snd_ctl_card_components_t *components) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 10)) + return -ENXIO; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_COMPONENTS, components) < 0) + return -errno; + return 0; +} + static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list) { snd_ctl_hw_t *hw = handle->private_data; @@ -389,6 +399,7 @@ static const snd_ctl_ops_t snd_ctl_hw_ops = { .async = snd_ctl_hw_async, .subscribe_events = snd_ctl_hw_subscribe_events, .card_info = snd_ctl_hw_card_info, + .card_components = snd_ctl_hw_card_components, .element_list = snd_ctl_hw_elem_list, .element_info = snd_ctl_hw_elem_info, .element_add = snd_ctl_hw_elem_add, diff --git a/src/control/control_local.h b/src/control/control_local.h index 02530e89..e8aa37fd 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -28,6 +28,7 @@ typedef struct _snd_ctl_ops { int (*async)(snd_ctl_t *handle, int sig, pid_t pid); int (*subscribe_events)(snd_ctl_t *handle, int subscribe); int (*card_info)(snd_ctl_t *handle, snd_ctl_card_info_t *info); + int (*card_components)(snd_ctl_t *handle, snd_ctl_card_components_t *components); int (*element_list)(snd_ctl_t *handle, snd_ctl_elem_list_t *list); int (*element_info)(snd_ctl_t *handle, snd_ctl_elem_info_t *info); int (*element_add)(snd_ctl_t *handle, snd_ctl_elem_info_t *info); diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 7d90d7ad..1cf2b5c7 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -466,6 +466,12 @@ static int snd_ctl_remap_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) return snd_ctl_card_info(priv->child, info); } +static int snd_ctl_remap_card_components(snd_ctl_t *ctl, snd_ctl_card_components_t *components) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_card_components(priv->child, components); +} + static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list) { snd_ctl_remap_t *priv = ctl->private_data; @@ -1185,6 +1191,7 @@ static const snd_ctl_ops_t snd_ctl_remap_ops = { .async = snd_ctl_remap_async, .subscribe_events = snd_ctl_remap_subscribe_events, .card_info = snd_ctl_remap_card_info, + .card_components = snd_ctl_remap_card_components, .element_list = snd_ctl_remap_elem_list, .element_info = snd_ctl_remap_elem_info, .element_read = snd_ctl_remap_elem_read, diff --git a/src/control/control_shm.c b/src/control/control_shm.c index 44f0a7bc..5bf3f679 100644 --- a/src/control/control_shm.c +++ b/src/control/control_shm.c @@ -150,6 +150,15 @@ static int snd_ctl_shm_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) return err; } +static int snd_ctl_shm_card_components(snd_ctl_t *ctl, snd_ctl_card_components_t *components) +{ + snd_ctl_shm_t *shm = ctl->private_data; + volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl; + ctrl->cmd = SNDRV_CTL_IOCTL_CARD_COMPONENTS; + ctrl->u.card_components = *components; + return snd_ctl_shm_action(ctl); +} + static int snd_ctl_shm_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list) { snd_ctl_shm_t *shm = ctl->private_data; @@ -391,6 +400,7 @@ static const snd_ctl_ops_t snd_ctl_shm_ops = { .async = snd_ctl_shm_async, .subscribe_events = snd_ctl_shm_subscribe_events, .card_info = snd_ctl_shm_card_info, + .card_components = snd_ctl_shm_card_components, .element_list = snd_ctl_shm_elem_list, .element_info = snd_ctl_shm_elem_info, .element_read = snd_ctl_shm_elem_read, diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c index 4dd21e90..b12cd65b 100644 --- a/src/mixer/simple_abst.c +++ b/src/mixer/simple_abst.c @@ -48,6 +48,7 @@ typedef struct _class_priv { snd_hctl_t *hctl; int attach_flag; snd_ctl_card_info_t *info; + snd_ctl_card_components_t *card_components; void *dlhandle; void *private_data; void (*private_free)(snd_mixer_class_t *class); @@ -161,7 +162,12 @@ static int match(snd_mixer_class_t *class, const char *lib, const char *searchl) if (searchl == NULL) return try_open(class, lib); - components = snd_ctl_card_info_get_components(priv->info); + + if (priv->card_components) + components = snd_ctl_card_components_get_string(priv->card_components); + else + components = snd_ctl_card_info_get_components(priv->info); + while (*components != '\0') { if (!strncmp(components, searchl, strlen(searchl))) return try_open(class, lib); @@ -248,6 +254,8 @@ static void private_free(snd_mixer_class_t *class) priv->private_free(class); if (priv->dlhandle) snd_dlclose(priv->dlhandle); + if (priv->card_components) + snd_ctl_card_components_free(priv->card_components); if (priv->info) snd_ctl_card_info_free(priv->info); if (priv->hctl) { @@ -335,8 +343,15 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer, err = snd_ctl_card_info(priv->ctl, priv->info); if (err < 0) goto __error; - if (err >= 0) - err = find_module(class, top); + err = snd_ctl_card_components_malloc(&priv->card_components); + if (err >= 0) { + err = snd_ctl_card_components(priv->ctl, priv->card_components); + if (err < 0) { + snd_ctl_card_components_free(priv->card_components); + priv->card_components = NULL; + } + } + err = find_module(class, top); if (err >= 0) err = snd_mixer_attach_hctl(mixer, priv->hctl); if (err >= 0) { @@ -374,6 +389,7 @@ int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info info->ctl = priv->ctl; info->hctl = priv->hctl; info->info = priv->info; + info->card_components = priv->card_components; return 0; } diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 8b3da74f..3b63dcb4 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -121,6 +121,7 @@ struct ctl_list { struct list_head dev_list; snd_ctl_t *ctl; snd_ctl_card_info_t *ctl_info; + snd_ctl_card_components_t *ctl_components; int slave; int ucm_group; }; diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index 73230a2a..5b310b0e 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -156,6 +156,8 @@ static char *rval_card_components(snd_use_case_mgr_t *uc_mgr) ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list == NULL) return NULL; + if (ctl_list->ctl_components) + return strdup(snd_ctl_card_components_get_string(ctl_list->ctl_components)); return strdup(snd_ctl_card_info_get_components(ctl_list->ctl_info)); } diff --git a/src/ucm/utils.c b/src/ucm/utils.c index dcb5c6e1..24fa0756 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -152,6 +152,8 @@ static void uc_mgr_free_ctl(struct ctl_list *ctl_list) free(ctl_dev->device); free(ctl_dev); } + if (ctl_list->ctl_components) + snd_ctl_card_components_free(ctl_list->ctl_components); snd_ctl_card_info_free(ctl_list->ctl_info); free(ctl_list); } @@ -219,6 +221,14 @@ static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr, return -ENOMEM; } snd_ctl_card_info_copy(cl->ctl_info, info); + if (snd_ctl_card_components_malloc(&cl->ctl_components) >= 0) { + if (snd_ctl_card_components(ctl, cl->ctl_components) < 0) { + snd_ctl_card_components_free(cl->ctl_components); + cl->ctl_components = NULL; + } + } else { + cl->ctl_components = NULL; + } cl->slave = slave; *ctl_list = cl; } else { diff --git a/test/control.c b/test/control.c index ccb389e7..d7e434d2 100644 --- a/test/control.c +++ b/test/control.c @@ -8,11 +8,13 @@ int main(void) int idx, dev, err; snd_ctl_t *handle; snd_ctl_card_info_t *info; + snd_ctl_card_components_t *components; snd_pcm_info_t *pcminfo; snd_rawmidi_info_t *rawmidiinfo; char str[128]; snd_ctl_card_info_alloca(&info); + snd_ctl_card_components_alloca(&components); snd_pcm_info_alloca(&pcminfo); snd_rawmidi_info_alloca(&rawmidiinfo); @@ -40,7 +42,10 @@ int main(void) printf(" name - '%s'\n", snd_ctl_card_info_get_name(info)); printf(" longname - '%s'\n", snd_ctl_card_info_get_longname(info)); printf(" mixername - '%s'\n", snd_ctl_card_info_get_mixername(info)); - printf(" components - '%s'\n", snd_ctl_card_info_get_components(info)); + if (snd_ctl_card_components(handle, components) >= 0) + printf(" components - '%s'\n", snd_ctl_card_components_get_string(components)); + else + printf(" components - '%s'\n", snd_ctl_card_info_get_components(info)); dev = -1; while (1) { snd_pcm_sync_id_t sync;