summaryrefslogtreecommitdiff
path: root/sound/usb/usbaudio.c
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2010-02-23 01:49:09 +0300
committerTakashi Iwai <tiwai@suse.de>2010-02-23 10:40:12 +0300
commit28e1b773083d349d5223f586a39fa30f5d0f1c36 (patch)
tree4793eb1d87a094cf3642600ac3b201f81bab7e54 /sound/usb/usbaudio.c
parent40717382e0c1f572553e4fdefb489db4b95a5e7e (diff)
downloadlinux-28e1b773083d349d5223f586a39fa30f5d0f1c36.tar.xz
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/usb/usbaudio.c')
-rw-r--r--sound/usb/usbaudio.c198
1 files changed, 120 insertions, 78 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;