Commit 28e1b773 authored by Daniel Mack's avatar Daniel Mack Committed by Takashi Iwai

ALSA: usbaudio: parse USB descriptors with structs

In preparation of support for v2.0 audio class, use the structs from
linux/usb/audio.h and add some new ones to describe the fields that are
actually parsed by the descriptor decoders.

Also, factor out code from usb_create_streams(). This makes it easier to
adopt the new iteration logic needed for v2.0.
Signed-off-by: default avatarDaniel Mack <daniel@caiaq.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 40717382
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
/* Terminal Control Selectors */ /* Terminal Control Selectors */
/* 4.3.2 Class-Specific AC Interface Descriptor */ /* 4.3.2 Class-Specific AC Interface Descriptor */
struct uac_ac_header_descriptor { struct uac_ac_header_descriptor_v1 {
__u8 bLength; /* 8 + n */ __u8 bLength; /* 8 + n */
__u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
__u8 bDescriptorSubtype; /* UAC_MS_HEADER */ __u8 bDescriptorSubtype; /* UAC_MS_HEADER */
...@@ -95,7 +95,7 @@ struct uac_ac_header_descriptor { ...@@ -95,7 +95,7 @@ struct uac_ac_header_descriptor {
/* As above, but more useful for defining your own descriptors: */ /* As above, but more useful for defining your own descriptors: */
#define DECLARE_UAC_AC_HEADER_DESCRIPTOR(n) \ #define DECLARE_UAC_AC_HEADER_DESCRIPTOR(n) \
struct uac_ac_header_descriptor_##n { \ struct uac_ac_header_descriptor_v1_##n { \
__u8 bLength; \ __u8 bLength; \
__u8 bDescriptorType; \ __u8 bDescriptorType; \
__u8 bDescriptorSubtype; \ __u8 bDescriptorSubtype; \
...@@ -131,7 +131,7 @@ struct uac_input_terminal_descriptor { ...@@ -131,7 +131,7 @@ struct uac_input_terminal_descriptor {
#define UAC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206 #define UAC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206
/* 4.3.2.2 Output Terminal Descriptor */ /* 4.3.2.2 Output Terminal Descriptor */
struct uac_output_terminal_descriptor { struct uac_output_terminal_descriptor_v1 {
__u8 bLength; /* in bytes: 9 */ __u8 bLength; /* in bytes: 9 */
__u8 bDescriptorType; /* CS_INTERFACE descriptor type */ __u8 bDescriptorType; /* CS_INTERFACE descriptor type */
__u8 bDescriptorSubtype; /* OUTPUT_TERMINAL descriptor subtype */ __u8 bDescriptorSubtype; /* OUTPUT_TERMINAL descriptor subtype */
...@@ -171,7 +171,7 @@ struct uac_feature_unit_descriptor_##ch { \ ...@@ -171,7 +171,7 @@ struct uac_feature_unit_descriptor_##ch { \
} __attribute__ ((packed)) } __attribute__ ((packed))
/* 4.5.2 Class-Specific AS Interface Descriptor */ /* 4.5.2 Class-Specific AS Interface Descriptor */
struct uac_as_header_descriptor { struct uac_as_header_descriptor_v1 {
__u8 bLength; /* in bytes: 7 */ __u8 bLength; /* in bytes: 7 */
__u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
__u8 bDescriptorSubtype; /* AS_GENERAL */ __u8 bDescriptorSubtype; /* AS_GENERAL */
...@@ -232,6 +232,19 @@ struct uac_format_type_i_discrete_descriptor_##n { \ ...@@ -232,6 +232,19 @@ struct uac_format_type_i_discrete_descriptor_##n { \
#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3)) #define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
/* Formats - Audio Data Format Type I Codes */
struct uac_format_type_ii_discrete_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bFormatType;
__le16 wMaxBitRate;
__le16 wSamplesPerFrame;
__u8 bSamFreqType;
__u8 tSamFreq[][3];
} __attribute__((packed));
/* Formats - A.2 Format Type Codes */ /* Formats - A.2 Format Type Codes */
#define UAC_FORMAT_TYPE_UNDEFINED 0x0 #define UAC_FORMAT_TYPE_UNDEFINED 0x0
#define UAC_FORMAT_TYPE_I 0x1 #define UAC_FORMAT_TYPE_I 0x1
...@@ -253,6 +266,17 @@ struct uac_iso_endpoint_descriptor { ...@@ -253,6 +266,17 @@ struct uac_iso_endpoint_descriptor {
#define UAC_EP_CS_ATTR_FILL_MAX 0x80 #define UAC_EP_CS_ATTR_FILL_MAX 0x80
/* A.10.2 Feature Unit Control Selectors */ /* A.10.2 Feature Unit Control Selectors */
struct uac_feature_unit_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bUnitID;
__u8 bSourceID;
__u8 bControlSize;
__u8 controls[0]; /* variable length */
} __attribute__((packed));
#define UAC_FU_CONTROL_UNDEFINED 0x00 #define UAC_FU_CONTROL_UNDEFINED 0x00
#define UAC_MUTE_CONTROL 0x01 #define UAC_MUTE_CONTROL 0x01
#define UAC_VOLUME_CONTROL 0x02 #define UAC_VOLUME_CONTROL 0x02
......
This diff is collapsed.
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/audio.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/hwdep.h> #include <sound/hwdep.h>
...@@ -1086,29 +1088,30 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -1086,29 +1088,30 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
* *
* most of controlls are defined here. * most of controlls are defined here.
*/ */
static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unsigned char *ftr) static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void *_ftr)
{ {
int channels, i, j; int channels, i, j;
struct usb_audio_term iterm; struct usb_audio_term iterm;
unsigned int master_bits, first_ch_bits; unsigned int master_bits, first_ch_bits;
int err, csize; int err, csize;
struct uac_feature_unit_descriptor *ftr = _ftr;
if (ftr[0] < 7 || ! (csize = ftr[5]) || ftr[0] < 7 + csize) { if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) {
snd_printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); snd_printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid);
return -EINVAL; return -EINVAL;
} }
/* parse the source unit */ /* parse the source unit */
if ((err = parse_audio_unit(state, ftr[4])) < 0) if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0)
return err; return err;
/* determine the input source type and name */ /* determine the input source type and name */
if (check_input_term(state, ftr[4], &iterm) < 0) if (check_input_term(state, ftr->bSourceID, &iterm) < 0)
return -EINVAL; return -EINVAL;
channels = (ftr[0] - 7) / csize - 1; channels = (ftr->bLength - 7) / csize - 1;
master_bits = snd_usb_combine_bytes(ftr + 6, csize); master_bits = snd_usb_combine_bytes(ftr->controls, csize);
/* master configuration quirks */ /* master configuration quirks */
switch (state->chip->usb_id) { switch (state->chip->usb_id) {
case USB_ID(0x08bb, 0x2702): case USB_ID(0x08bb, 0x2702):
...@@ -1119,21 +1122,21 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unsig ...@@ -1119,21 +1122,21 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unsig
break; break;
} }
if (channels > 0) if (channels > 0)
first_ch_bits = snd_usb_combine_bytes(ftr + 6 + csize, csize); first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize);
else else
first_ch_bits = 0; first_ch_bits = 0;
/* check all control types */ /* check all control types */
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
unsigned int ch_bits = 0; unsigned int ch_bits = 0;
for (j = 0; j < channels; j++) { for (j = 0; j < channels; j++) {
unsigned int mask = snd_usb_combine_bytes(ftr + 6 + csize * (j+1), csize); unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize);
if (mask & (1 << i)) if (mask & (1 << i))
ch_bits |= (1 << j); ch_bits |= (1 << j);
} }
if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
build_feature_ctl(state, ftr, ch_bits, i, &iterm, unitid); build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid);
if (master_bits & (1 << i)) if (master_bits & (1 << i))
build_feature_ctl(state, ftr, 0, i, &iterm, unitid); build_feature_ctl(state, _ftr, 0, i, &iterm, unitid);
} }
return 0; return 0;
...@@ -1780,7 +1783,7 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) ...@@ -1780,7 +1783,7 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
*/ */
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
{ {
unsigned char *desc; struct uac_output_terminal_descriptor_v1 *desc;
struct mixer_build state; struct mixer_build state;
int err; int err;
const struct usbmix_ctl_map *map; const struct usbmix_ctl_map *map;
...@@ -1805,13 +1808,13 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) ...@@ -1805,13 +1808,13 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
desc = NULL; desc = NULL;
while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) { while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) {
if (desc[0] < 9) if (desc->bLength < 9)
continue; /* invalid descriptor? */ continue; /* invalid descriptor? */
set_bit(desc[3], state.unitbitmap); /* mark terminal ID as visited */ set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */
state.oterm.id = desc[3]; state.oterm.id = desc->bTerminalID;
state.oterm.type = combine_word(&desc[4]); state.oterm.type = le16_to_cpu(desc->wTerminalType);
state.oterm.name = desc[8]; state.oterm.name = desc->iTerminal;
err = parse_audio_unit(&state, desc[7]); err = parse_audio_unit(&state, desc->bSourceID);
if (err < 0) if (err < 0)
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment