1
0
Fork 0

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: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
hifive-unleashed-5.1
Daniel Mack 2010-02-22 23:49:09 +01:00 committed by Takashi Iwai
parent 40717382e0
commit 28e1b77308
3 changed files with 168 additions and 99 deletions

View File

@ -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 {
/* 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 {
#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 { \
} __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 { \
#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 {
#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

View File

@ -46,6 +46,8 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/usb/audio.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/pcm.h> #include <sound/pcm.h>
@ -2421,15 +2423,17 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
* @fmt: the format type descriptor * @fmt: the format type descriptor
*/ */
static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp, static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp,
int format, unsigned char *fmt) int format, void *fmt_raw)
{ {
int pcm_format; int pcm_format;
int sample_width, sample_bytes; int sample_width, sample_bytes;
struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
/* FIXME: correct endianess and sign? */ /* FIXME: correct endianess and sign? */
pcm_format = -1; pcm_format = -1;
sample_width = fmt[6]; sample_width = fmt->bBitResolution;
sample_bytes = fmt[5]; sample_bytes = fmt->bSubframeSize;
switch (format) { switch (format) {
case 0: /* some devices don't define this correctly... */ case 0: /* some devices don't define this correctly... */
snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
@ -2442,7 +2446,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
sample_width, sample_bytes); sample_width, sample_bytes);
} }
/* check the format byte size */ /* check the format byte size */
switch (fmt[5]) { switch (sample_bytes) {
case 1: case 1:
pcm_format = SNDRV_PCM_FORMAT_S8; pcm_format = SNDRV_PCM_FORMAT_S8;
break; break;
@ -2463,8 +2467,8 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
break; break;
default: default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
chip->dev->devnum, fp->iface, chip->dev->devnum, fp->iface, fp->altsetting,
fp->altsetting, sample_width, sample_bytes); sample_width, sample_bytes);
break; break;
} }
break; break;
@ -2564,11 +2568,12 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
* parse the format type I and III descriptors * parse the format type I and III descriptors
*/ */
static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *fp, static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *fp,
int format, unsigned char *fmt) int format, void *fmt_raw)
{ {
int pcm_format; int pcm_format;
struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
if (fmt[3] == USB_FORMAT_TYPE_III) { if (fmt->bFormatType == USB_FORMAT_TYPE_III) {
/* FIXME: the format type is really IECxxx /* FIXME: the format type is really IECxxx
* but we give normal PCM format to get the existing * but we give normal PCM format to get the existing
* apps working... * apps working...
@ -2590,23 +2595,27 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *
if (pcm_format < 0) if (pcm_format < 0)
return -1; return -1;
} }
fp->format = pcm_format; fp->format = pcm_format;
fp->channels = fmt[4]; fp->channels = fmt->bNrChannels;
if (fp->channels < 1) { if (fp->channels < 1) {
snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
chip->dev->devnum, fp->iface, fp->altsetting, fp->channels); chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
return -1; return -1;
} }
return parse_audio_format_rates(chip, fp, fmt, 7); return parse_audio_format_rates(chip, fp, fmt_raw, 7);
} }
/* /*
* prase the format type II descriptor * parse the format type II descriptor
*/ */
static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat *fp, static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat *fp,
int format, unsigned char *fmt) int format, void *fmt_raw)
{ {
int brate, framesize; int brate, framesize;
struct uac_format_type_ii_discrete_descriptor *fmt = fmt_raw;
switch (format) { switch (format) {
case USB_AUDIO_FORMAT_AC3: case USB_AUDIO_FORMAT_AC3:
/* FIXME: there is no AC3 format defined yet */ /* FIXME: there is no AC3 format defined yet */
@ -2622,20 +2631,25 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat
fp->format = SNDRV_PCM_FORMAT_MPEG; fp->format = SNDRV_PCM_FORMAT_MPEG;
break; break;
} }
fp->channels = 1; fp->channels = 1;
brate = combine_word(&fmt[4]); /* fmt[4,5] : wMaxBitRate (in kbps) */
framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */ brate = le16_to_cpu(fmt->wMaxBitRate);
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
fp->frame_size = framesize; fp->frame_size = framesize;
return parse_audio_format_rates(chip, fp, fmt, 8); /* fmt[8..] sample rates */ return parse_audio_format_rates(chip, fp, fmt_raw, 8); /* fmt[8..] sample rates */
} }
static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp, static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
int format, unsigned char *fmt, int stream) int format, void *fmt_raw, int stream)
{ {
int err; int err;
/* we only parse the common header of all format types here,
* so it is safe to take a type_i struct */
struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
switch (fmt[3]) { switch (fmt->bFormatType) {
case USB_FORMAT_TYPE_I: case USB_FORMAT_TYPE_I:
case USB_FORMAT_TYPE_III: case USB_FORMAT_TYPE_III:
err = parse_audio_format_i(chip, fp, format, fmt); err = parse_audio_format_i(chip, fp, format, fmt);
@ -2645,10 +2659,10 @@ static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp
break; break;
default: default:
snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n", snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]); chip->dev->devnum, fp->iface, fp->altsetting, fmt->bFormatType);
return -1; return -1;
} }
fp->fmt_type = fmt[3]; fp->fmt_type = fmt->bFormatType;
if (err < 0) if (err < 0)
return err; return err;
#if 1 #if 1
@ -2659,7 +2673,7 @@ static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp
if (chip->usb_id == USB_ID(0x041e, 0x3000) || if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
chip->usb_id == USB_ID(0x041e, 0x3020) || chip->usb_id == USB_ID(0x041e, 0x3020) ||
chip->usb_id == USB_ID(0x041e, 0x3061)) { chip->usb_id == USB_ID(0x041e, 0x3061)) {
if (fmt[3] == USB_FORMAT_TYPE_I && if (fmt->bFormatType == USB_FORMAT_TYPE_I &&
fp->rates != SNDRV_PCM_RATE_48000 && fp->rates != SNDRV_PCM_RATE_48000 &&
fp->rates != SNDRV_PCM_RATE_96000) fp->rates != SNDRV_PCM_RATE_96000)
return -1; return -1;
@ -2708,6 +2722,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
num = 4; num = 4;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
struct uac_as_header_descriptor_v1 *as;
alts = &iface->altsetting[i]; alts = &iface->altsetting[i];
altsd = get_iface_desc(alts); altsd = get_iface_desc(alts);
/* skip invalid one */ /* skip invalid one */
@ -2726,7 +2742,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ? stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
altno = altsd->bAlternateSetting; altno = altsd->bAlternateSetting;
/* audiophile usb: skip altsets incompatible with device_setup /* audiophile usb: skip altsets incompatible with device_setup
*/ */
if (chip->usb_id == USB_ID(0x0763, 0x2003) && if (chip->usb_id == USB_ID(0x0763, 0x2003) &&
@ -2734,20 +2750,21 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
continue; continue;
/* get audio formats */ /* get audio formats */
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL); as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL);
if (!fmt) {
if (!as) {
snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n", snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
dev->devnum, iface_no, altno); dev->devnum, iface_no, altno);
continue; continue;
} }
if (fmt[0] < 7) { if (as->bLength < sizeof(*as)) {
snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n", snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
dev->devnum, iface_no, altno); dev->devnum, iface_no, altno);
continue; continue;
} }
format = (fmt[6] << 8) | fmt[5]; /* remember the format value */ format = le16_to_cpu(as->wFormatTag); /* remember the format value */
/* get format type */ /* get format type */
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, FORMAT_TYPE); fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, FORMAT_TYPE);
@ -2875,6 +2892,65 @@ static void snd_usb_stream_disconnect(struct list_head *head)
} }
} }
static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
{
struct usb_device *dev = chip->dev;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
if (!iface) {
snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
dev->devnum, ctrlif, interface);
return -EINVAL;
}
if (usb_interface_claimed(iface)) {
snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
dev->devnum, ctrlif, interface);
return -EINVAL;
}
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
int err = snd_usbmidi_create(chip->card, iface,
&chip->midi_list, NULL);
if (err < 0) {
snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n",
dev->devnum, ctrlif, interface);
return -EINVAL;
}
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
return 0;
}
if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) {
snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
/* skip non-supported classes */
return -EINVAL;
}
if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
snd_printk(KERN_ERR "low speed audio streaming not supported\n");
return -EINVAL;
}
if (! parse_audio_endpoints(chip, interface)) {
usb_set_interface(dev, interface, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
return -EINVAL;
}
return 0;
}
/* /*
* parse audio control descriptor and create pcm/midi streams * parse audio control descriptor and create pcm/midi streams
*/ */
@ -2882,69 +2958,36 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
{ {
struct usb_device *dev = chip->dev; struct usb_device *dev = chip->dev;
struct usb_host_interface *host_iface; struct usb_host_interface *host_iface;
struct usb_interface *iface; struct uac_ac_header_descriptor_v1 *h1;
unsigned char *p1; void *control_header;
int i, j; int i;
/* find audiocontrol interface */ /* find audiocontrol interface */
host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
if (!(p1 = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, NULL, HEADER))) { control_header = snd_usb_find_csint_desc(host_iface->extra,
host_iface->extralen,
NULL, HEADER);
if (!control_header) {
snd_printk(KERN_ERR "cannot find HEADER\n"); snd_printk(KERN_ERR "cannot find HEADER\n");
return -EINVAL; return -EINVAL;
} }
if (! p1[7] || p1[0] < 8 + p1[7]) {
snd_printk(KERN_ERR "invalid HEADER\n"); h1 = control_header;
if (!h1->bInCollection) {
snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
return -EINVAL; return -EINVAL;
} }
/* if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
* parse all USB audio streaming interfaces snd_printk(KERN_ERR "invalid HEADER (v1)\n");
*/ return -EINVAL;
for (i = 0; i < p1[7]; i++) {
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
j = p1[8 + i];
iface = usb_ifnum_to_if(dev, j);
if (!iface) {
snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
dev->devnum, ctrlif, j);
continue;
}
if (usb_interface_claimed(iface)) {
snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n", dev->devnum, ctrlif, j);
continue;
}
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
int err = snd_usbmidi_create(chip->card, iface,
&chip->midi_list, NULL);
if (err < 0) {
snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j);
continue;
}
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
continue;
}
if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) {
snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", dev->devnum, ctrlif, j, altsd->bInterfaceClass);
/* skip non-supported classes */
continue;
}
if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
snd_printk(KERN_ERR "low speed audio streaming not supported\n");
continue;
}
if (! parse_audio_endpoints(chip, j)) {
usb_set_interface(dev, j, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
}
} }
for (i = 0; i < h1->bInCollection; i++)
snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
return 0; return 0;
} }
@ -3607,7 +3650,6 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
ifnum = get_iface_desc(alts)->bInterfaceNumber; ifnum = get_iface_desc(alts)->bInterfaceNumber;
id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); le16_to_cpu(dev->descriptor.idProduct));
if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
goto __err_val; goto __err_val;

View File

@ -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,
* *
* 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
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)
*/ */
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)
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;
} }