ALSA: usb-audio: parse more format descriptors with structs
[safe/jmp/linux-2.6] / sound / usb / format.c
index cbfe0c2..caaa3ef 100644 (file)
  */
 
 #include <linux/init.h>
+#include <linux/slab.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
  * @format: the format tag (wFormatTag)
  * @fmt: the format type descriptor
  */
-static int parse_audio_format_i_type(struct snd_usb_audio *chip,
+static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
                                     struct audioformat *fp,
                                     int format, void *_fmt,
                                     int protocol)
 {
-       int pcm_format, i;
        int sample_width, sample_bytes;
+       u64 pcm_formats;
 
        switch (protocol) {
        case UAC_VERSION_1: {
                struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
                sample_width = fmt->bBitResolution;
                sample_bytes = fmt->bSubframeSize;
+               format = 1 << format;
                break;
        }
 
@@ -57,24 +60,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
                struct uac_format_type_i_ext_descriptor *fmt = _fmt;
                sample_width = fmt->bBitResolution;
                sample_bytes = fmt->bSubslotSize;
-
-               /*
-                * FIXME
-                * USB audio class v2 devices specify a bitmap of possible
-                * audio formats rather than one fix value. For now, we just
-                * pick one of them and report that as the only possible
-                * value for this setting.
-                * The bit allocation map is in fact compatible to the
-                * wFormatTag of the v1 AS streaming descriptors, which is why
-                * we can simply map the matrix.
-                */
-
-               for (i = 0; i < 5; i++)
-                       if (format & (1UL << i)) {
-                               format = i + 1;
-                               break;
-                       }
-
+               format <<= 1;
                break;
        }
 
@@ -82,15 +68,15 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
                return -EINVAL;
        }
 
-       /* FIXME: correct endianess and sign? */
-       pcm_format = -1;
+       pcm_formats = 0;
 
-       switch (format) {
-       case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */
+       if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
+               /* some devices don't define this correctly... */
                snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
                            chip->dev->devnum, fp->iface, fp->altsetting);
-               /* fall-through */
-       case UAC_FORMAT_TYPE_I_PCM:
+               format = 1 << UAC_FORMAT_TYPE_I_PCM;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
                if (sample_width > sample_bytes * 8) {
                        snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
                                   chip->dev->devnum, fp->iface, fp->altsetting,
@@ -99,22 +85,22 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
                /* check the format byte size */
                switch (sample_bytes) {
                case 1:
-                       pcm_format = SNDRV_PCM_FORMAT_S8;
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S8;
                        break;
                case 2:
                        if (snd_usb_is_big_endian_format(chip, fp))
-                               pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */
                        else
-                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE;
                        break;
                case 3:
                        if (snd_usb_is_big_endian_format(chip, fp))
-                               pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */
                        else
-                               pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE;
                        break;
                case 4:
-                       pcm_format = SNDRV_PCM_FORMAT_S32_LE;
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
                        break;
                default:
                        snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
@@ -122,30 +108,29 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
                                   sample_width, sample_bytes);
                        break;
                }
-               break;
-       case UAC_FORMAT_TYPE_I_PCM8:
-               pcm_format = SNDRV_PCM_FORMAT_U8;
-
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
                /* Dallas DS4201 workaround: it advertises U8 format, but really
                   supports S8. */
                if (chip->usb_id == USB_ID(0x04fa, 0x4201))
-                       pcm_format = SNDRV_PCM_FORMAT_S8;
-               break;
-       case UAC_FORMAT_TYPE_I_IEEE_FLOAT:
-               pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
-               break;
-       case UAC_FORMAT_TYPE_I_ALAW:
-               pcm_format = SNDRV_PCM_FORMAT_A_LAW;
-               break;
-       case UAC_FORMAT_TYPE_I_MULAW:
-               pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
-               break;
-       default:
-               snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S8;
+               else
+                       pcm_formats |= SNDRV_PCM_FMTBIT_U8;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
+       }
+       if (format & ~0x3f) {
+               snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
                           chip->dev->devnum, fp->iface, fp->altsetting, format);
-               break;
        }
-       return pcm_format;
+       return pcm_formats;
 }
 
 
@@ -234,7 +219,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
        /* get the number of sample rates first by only fetching 2 bytes */
        ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                             0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000);
+                             UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                             tmp, sizeof(tmp), 1000);
 
        if (ret < 0) {
                snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
@@ -252,7 +238,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
        /* now get the full information */
        ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
                               USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                              0x0100, chip->clock_id << 8, data, data_size, 1000);
+                              UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                              data, data_size, 1000);
 
        if (ret < 0) {
                snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
@@ -291,12 +278,11 @@ err:
  * parse the format type I and III descriptors
  */
 static int parse_audio_format_i(struct snd_usb_audio *chip,
-                               struct audioformat *fp,
-                               int format, void *_fmt,
+                               struct audioformat *fp, int format,
+                               struct uac_format_type_i_continuous_descriptor *fmt,
                                struct usb_host_interface *iface)
 {
        struct usb_interface_descriptor *altsd = get_iface_desc(iface);
-       struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
        int protocol = altsd->bInterfaceProtocol;
        int pcm_format, ret;
 
@@ -317,14 +303,14 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
                default:
                        pcm_format = SNDRV_PCM_FORMAT_S16_LE;
                }
+               fp->formats = 1uLL << pcm_format;
        } else {
-               pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol);
-               if (pcm_format < 0)
+               fp->formats = parse_audio_format_i_type(chip, fp, format,
+                                                       fmt, protocol);
+               if (!fp->formats)
                        return -1;
        }
 
-       fp->format = pcm_format;
-
        /* gather possible sample rates */
        /* audio class v1 reports possible sample rates as part of the
         * proprietary class specific descriptor.
@@ -333,7 +319,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
        switch (protocol) {
        case UAC_VERSION_1:
                fp->channels = fmt->bNrChannels;
-               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
+               ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
                break;
        case UAC_VERSION_2:
                /* fp->channels is already set in this case */
@@ -365,16 +351,16 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
        switch (format) {
        case UAC_FORMAT_TYPE_II_AC3:
                /* FIXME: there is no AC3 format defined yet */
-               // fp->format = SNDRV_PCM_FORMAT_AC3;
-               fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */
+               // fp->formats = SNDRV_PCM_FMTBIT_AC3;
+               fp->formats = SNDRV_PCM_FMTBIT_U8; /* temporary hack to receive byte streams */
                break;
        case UAC_FORMAT_TYPE_II_MPEG:
-               fp->format = SNDRV_PCM_FORMAT_MPEG;
+               fp->formats = SNDRV_PCM_FMTBIT_MPEG;
                break;
        default:
                snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
                           chip->dev->devnum, fp->iface, fp->altsetting, format);
-               fp->format = SNDRV_PCM_FORMAT_MPEG;
+               fp->formats = SNDRV_PCM_FMTBIT_MPEG;
                break;
        }
 
@@ -405,12 +391,12 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
 }
 
 int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
-                      int format, unsigned char *fmt, int stream,
-                      struct usb_host_interface *iface)
+                              int format, struct uac_format_type_i_continuous_descriptor *fmt,
+                              int stream, struct usb_host_interface *iface)
 {
        int err;
 
-       switch (fmt[3]) {
+       switch (fmt->bFormatType) {
        case UAC_FORMAT_TYPE_I:
        case UAC_FORMAT_TYPE_III:
                err = parse_audio_format_i(chip, fp, format, fmt, iface);
@@ -420,10 +406,11 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *f
                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
@@ -434,7 +421,7 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *f
        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] == UAC_FORMAT_TYPE_I &&
+               if (fmt->bFormatType == UAC_FORMAT_TYPE_I &&
                    fp->rates != SNDRV_PCM_RATE_48000 &&
                    fp->rates != SNDRV_PCM_RATE_96000)
                        return -1;