diff options
author | Daniel Mack <daniel@caiaq.de> | 2010-02-22 23:49:09 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-02-23 08:40:12 +0100 |
commit | 28e1b773083d349d5223f586a39fa30f5d0f1c36 (patch) | |
tree | 4793eb1d87a094cf3642600ac3b201f81bab7e54 /sound | |
parent | 40717382e0c1f572553e4fdefb489db4b95a5e7e (diff) | |
download | linux-28e1b773083d349d5223f586a39fa30f5d0f1c36.tar.bz2 |
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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/usb/usbaudio.c | 198 | ||||
-rw-r--r-- | sound/usb/usbmixer.c | 37 |
2 files changed, 140 insertions, 95 deletions
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index c6b9c8cac59e..f833dea60180 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -46,6 +46,8 @@ #include <linux/usb.h> #include <linux/moduleparam.h> #include <linux/mutex.h> +#include <linux/usb/audio.h> + #include <sound/core.h> #include <sound/info.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 */ 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 sample_width, sample_bytes; + struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw; /* FIXME: correct endianess and sign? */ pcm_format = -1; - sample_width = fmt[6]; - sample_bytes = fmt[5]; + sample_width = fmt->bBitResolution; + sample_bytes = fmt->bSubframeSize; + switch (format) { 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", @@ -2442,7 +2446,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor sample_width, sample_bytes); } /* check the format byte size */ - switch (fmt[5]) { + switch (sample_bytes) { case 1: pcm_format = SNDRV_PCM_FORMAT_S8; break; @@ -2463,8 +2467,8 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor break; default: snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", - chip->dev->devnum, fp->iface, - fp->altsetting, sample_width, sample_bytes); + chip->dev->devnum, fp->iface, fp->altsetting, + sample_width, sample_bytes); 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 */ 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; + 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 * but we give normal PCM format to get the existing * apps working... @@ -2590,23 +2595,27 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat * if (pcm_format < 0) return -1; } + fp->format = pcm_format; - fp->channels = fmt[4]; + fp->channels = fmt->bNrChannels; + if (fp->channels < 1) { snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", chip->dev->devnum, fp->iface, fp->altsetting, fp->channels); 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, - int format, unsigned char *fmt) + int format, void *fmt_raw) { int brate, framesize; + struct uac_format_type_ii_discrete_descriptor *fmt = fmt_raw; + switch (format) { case USB_AUDIO_FORMAT_AC3: /* 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; break; } + 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); 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, - int format, unsigned char *fmt, int stream) + int format, void *fmt_raw, int stream) { 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_III: 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; default: 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; } - fp->fmt_type = fmt[3]; + fp->fmt_type = fmt->bFormatType; if (err < 0) return err; #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) || chip->usb_id == USB_ID(0x041e, 0x3020) || 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_96000) return -1; @@ -2708,6 +2722,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) num = 4; for (i = 0; i < num; i++) { + struct uac_as_header_descriptor_v1 *as; + alts = &iface->altsetting[i]; altsd = get_iface_desc(alts); /* 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) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; altno = altsd->bAlternateSetting; - + /* audiophile usb: skip altsets incompatible with device_setup */ 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; /* get audio formats */ - fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL); - if (!fmt) { + as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL); + + if (!as) { snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n", dev->devnum, iface_no, altno); continue; } - if (fmt[0] < 7) { + if (as->bLength < sizeof(*as)) { snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n", dev->devnum, iface_no, altno); continue; } - format = (fmt[6] << 8) | fmt[5]; /* remember the format value */ + format = le16_to_cpu(as->wFormatTag); /* remember the format value */ /* get 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 */ @@ -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_host_interface *host_iface; - struct usb_interface *iface; - unsigned char *p1; - int i, j; + struct uac_ac_header_descriptor_v1 *h1; + void *control_header; + int i; /* find audiocontrol interface */ 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"); 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; } - /* - * parse all USB audio streaming interfaces - */ - 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); - } + if (h1->bLength < sizeof(*h1) + h1->bInCollection) { + snd_printk(KERN_ERR "invalid HEADER (v1)\n"); + return -EINVAL; } + for (i = 0; i < h1->bInCollection; i++) + snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]); + return 0; } @@ -3607,7 +3650,6 @@ static void *snd_usb_audio_probe(struct usb_device *dev, ifnum = get_iface_desc(alts)->bInterfaceNumber; id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); - if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) goto __err_val; diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 35b4830fb0c4..11636a6112d5 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -32,6 +32,8 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/usb.h> +#include <linux/usb/audio.h> + #include <sound/core.h> #include <sound/control.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. */ -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; struct usb_audio_term iterm; unsigned int master_bits, first_ch_bits; 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); return -EINVAL; } /* parse the source unit */ - if ((err = parse_audio_unit(state, ftr[4])) < 0) + if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0) return err; /* 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; - 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 */ switch (state->chip->usb_id) { case USB_ID(0x08bb, 0x2702): @@ -1119,21 +1122,21 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unsig break; } 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 first_ch_bits = 0; /* check all control types */ for (i = 0; i < 10; i++) { unsigned int ch_bits = 0; 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)) ch_bits |= (1 << j); } 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)) - build_feature_ctl(state, ftr, 0, i, &iterm, unitid); + build_feature_ctl(state, _ftr, 0, i, &iterm, unitid); } 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) { - unsigned char *desc; + struct uac_output_terminal_descriptor_v1 *desc; struct mixer_build state; int err; const struct usbmix_ctl_map *map; @@ -1805,13 +1808,13 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) desc = 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? */ - set_bit(desc[3], state.unitbitmap); /* mark terminal ID as visited */ - state.oterm.id = desc[3]; - state.oterm.type = combine_word(&desc[4]); - state.oterm.name = desc[8]; - err = parse_audio_unit(&state, desc[7]); + set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ + state.oterm.id = desc->bTerminalID; + state.oterm.type = le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); if (err < 0) return err; } |