Skip to content

Commit d90ae37

Browse files
committed
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] link to v3 Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
1 parent 75ed5f0 commit d90ae37

3 files changed

Lines changed: 62 additions & 3 deletions

File tree

include/local.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@
101101
#define _snd_pcm_sw_params snd_pcm_sw_params
102102
#define _snd_pcm_status snd_pcm_status
103103

104-
#define _snd_ctl_card_info snd_ctl_card_info
105104
#define _snd_ctl_elem_id snd_ctl_elem_id
106105
#define _snd_ctl_elem_list snd_ctl_elem_list
107106
#define _snd_ctl_elem_info snd_ctl_elem_info
@@ -226,6 +225,20 @@
226225
#include "seq_midi_event.h"
227226
#include "list.h"
228227

228+
/* V2 structure - increased components string from 128 bytes to 512 bytes */
229+
struct _snd_ctl_card_info {
230+
int card; /* card number */
231+
int pad; /* reserved for future (was type) */
232+
unsigned char id[16]; /* ID of card (user selectable) */
233+
unsigned char driver[16]; /* Driver name */
234+
unsigned char name[32]; /* Short name of soundcard */
235+
unsigned char longname[80]; /* name + info text about soundcard */
236+
unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */
237+
unsigned char mixername[80]; /* visual mixer identification */
238+
unsigned char components[512]; /* card components / fine identification, delimited with one space (AC97 etc..) */
239+
};
240+
241+
229242
struct _snd_async_handler {
230243
enum {
231244
SND_ASYNC_HANDLER_GENERIC,

include/sound/uapi/asound.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ struct snd_timer_tread {
10441044
* *
10451045
****************************************************************************/
10461046

1047-
#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9)
1047+
#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10)
10481048

10491049
struct snd_ctl_card_info {
10501050
int card; /* card number */
@@ -1058,6 +1058,16 @@ struct snd_ctl_card_info {
10581058
unsigned char components[128]; /* card components / fine identification, delimited with one space (AC97 etc..) */
10591059
};
10601060

1061+
/*
1062+
* Card components can exceed the fixed 128 bytes in snd_ctl_card_info.
1063+
* Use SNDRV_CTL_IOCTL_CARD_COMPONENTS to retrieve the full string.
1064+
*/
1065+
struct snd_ctl_card_components {
1066+
int card; /* card number */
1067+
unsigned int length; /* returned length of components string for ioctl query */
1068+
unsigned char components[512]; /* card components / fine identification, delimited with one space (AC97 etc..) */
1069+
};
1070+
10611071
typedef int __bitwise snd_ctl_elem_type_t;
10621072
#define SNDRV_CTL_ELEM_TYPE_NONE ((snd_ctl_elem_type_t) 0) /* invalid */
10631073
#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((snd_ctl_elem_type_t) 1) /* boolean type */
@@ -1184,6 +1194,7 @@ struct snd_ctl_tlv {
11841194

11851195
#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)
11861196
#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info)
1197+
#define SNDRV_CTL_IOCTL_CARD_COMPONENTS _IOWR('U', 0x02, struct snd_ctl_card_components)
11871198
#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list)
11881199
#define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info)
11891200
#define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value)

src/control/control_hw.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,45 @@ static int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe)
129129
static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
130130
{
131131
snd_ctl_hw_t *hw = handle->private_data;
132-
if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, info) < 0) {
132+
struct snd_ctl_card_info kinfo;
133+
struct snd_ctl_card_components comp;
134+
135+
if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, &kinfo) < 0) {
133136
snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_INFO failed");
134137
return -errno;
135138
}
139+
140+
snd_ctl_card_info_clear(info);
141+
info->card = kinfo.card;
142+
info->pad = kinfo.pad;
143+
memcpy(info->id, kinfo.id, sizeof(kinfo.id));
144+
memcpy(info->driver, kinfo.driver, sizeof(kinfo.driver));
145+
memcpy(info->name, kinfo.name, sizeof(kinfo.name));
146+
memcpy(info->longname, kinfo.longname, sizeof(kinfo.longname));
147+
memcpy(info->reserved_, kinfo.reserved_, sizeof(kinfo.reserved_));
148+
memcpy(info->mixername, kinfo.mixername, sizeof(kinfo.mixername));
149+
if (strnlen((char *)kinfo.components, sizeof(kinfo.components)) > 0 &&
150+
kinfo.components[sizeof(kinfo.components) - 2] == '>') {
151+
memset(&comp, 0, sizeof(comp));
152+
if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_COMPONENTS, &comp) < 0) {
153+
snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_COMPONENTS query failed");
154+
return -errno;
155+
}
156+
if (comp.length == 0 || comp.length > sizeof(comp.components)) {
157+
snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_COMPONENTS unexpected length %u",
158+
comp.length);
159+
return -errno;
160+
}
161+
comp.length = sizeof(comp.components);
162+
if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_COMPONENTS, &comp) < 0) {
163+
snd_errornum(CONTROL, "SNDRV_CTL_IOCTL_CARD_COMPONENTS read failed");
164+
return -errno;
165+
}
166+
memcpy(info->components, comp.components, sizeof(info->components));
167+
} else {
168+
memcpy(info->components, kinfo.components, sizeof(info->components));
169+
}
170+
136171
return 0;
137172
}
138173

0 commit comments

Comments
 (0)