Update of /cvsroot/alsa/alsa-kernel/usb
In directory usw-pr-cvs1:/tmp/cvs-serv7257/usb

Modified Files:
        usbaudio.c 
Log Message:
clean up and fixed the parser of audio streams.

now a pcm stream is created per endpoint instead of per interface.
either a playback or a capture is created at each time.
(for this, snd_pcm_stream_new() is exported now.)

m-audio devices will have less pcm streams than the older version,
but this is CORRECT.  don't worry :)



Index: usbaudio.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/usb/usbaudio.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- usbaudio.c  10 Sep 2002 13:03:59 -0000      1.6
+++ usbaudio.c  12 Sep 2002 15:24:05 -0000      1.7
@@ -105,6 +105,7 @@
        struct list_head list;
        snd_pcm_format_t format;        /* format type */
        int channels;                   /* # channels */
+       int iface;                      /* interface number */
        unsigned char altsetting;       /* corresponding alternate setting */
        unsigned char altset_idx;       /* array index of altenate setting */
        unsigned char attributes;       /* corresponding attributes of cs endpoint */
@@ -136,7 +137,9 @@
        snd_usb_stream_t *stream;
        struct usb_device *dev;
        snd_pcm_substream_t *pcm_substream;
-       int interface;           /* Interface number, -1 means not used */
+       int direction;  /* playback or capture */
+       int interface;  /* current interface */
+       int endpoint;   /* assigned endpoint */
        unsigned int format;     /* USB data format */
        unsigned int datapipe;   /* the data i/o pipe */
        unsigned int syncpipe;   /* 1 - async out or adaptive in */
@@ -492,6 +495,23 @@
 
 
 /*
+ */
+static struct snd_urb_ops audio_urb_ops[2] = {
+       {
+               .prepare =      prepare_playback_urb,
+               .retire =       retire_playback_urb,
+               .prepare_sync = prepare_playback_sync_urb,
+               .retire_sync =  retire_playback_sync_urb,
+       },
+       {
+               .prepare =      prepare_capture_urb,
+               .retire =       retire_capture_urb,
+               .prepare_sync = prepare_capture_sync_urb,
+               .retire_sync =  retire_capture_sync_urb,
+       },
+};
+
+/*
  * complete callback from data urb
  */
 static void snd_complete_urb(struct urb *urb)
@@ -738,7 +758,7 @@
 static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
 {
        int maxsize, n, i;
-       int is_playback = subs->pcm_substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
        int npacks[MAX_URBS], total_packs;
 
        /* calculate the frequency in 10.14 format */
@@ -907,7 +927,7 @@
        struct audioformat *fmt;
        unsigned int ep, attr;
        unsigned char data[3];
-       int is_playback = subs->pcm_substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
        int err;
 
        fmt = find_format(subs, runtime);
@@ -917,7 +937,7 @@
                return -EINVAL;
        }
 
-       iface = &config->interface[subs->interface];
+       iface = &config->interface[fmt->iface];
        alts = &iface->altsetting[fmt->altset_idx];
        snd_assert(alts->bAlternateSetting == fmt->altsetting, return -EINVAL);
 
@@ -927,6 +947,7 @@
                subs->datapipe = usb_sndisocpipe(dev, ep);
        else
                subs->datapipe = usb_rcvisocpipe(dev, ep);
+       subs->interface = fmt->iface;
        subs->format = fmt->altset_idx;
        subs->syncpipe = subs->syncinterval = 0;
        subs->maxpacksize = alts->endpoint[0].wMaxPacketSize;
@@ -1126,6 +1147,7 @@
        snd_pcm_runtime_t *runtime = substream->runtime;
        snd_usb_substream_t *subs = &as->substream[direction];
 
+       subs->interface = -1;
        runtime->hw = *hw;
        runtime->private_data = subs;
        subs->pcm_substream = substream;
@@ -1139,7 +1161,8 @@
        snd_usb_stream_t *as = snd_pcm_substream_chip(substream);
        snd_usb_substream_t *subs = &as->substream[direction];
        release_substream_urbs(subs);
-       usb_set_interface(subs->dev, subs->interface, 0);
+       if (subs->interface >= 0)
+               usb_set_interface(subs->dev, subs->interface, 0);
        subs->pcm_substream = NULL;
        return 0;
 }
@@ -1286,290 +1309,6 @@
 
 
 /*
- * intialize the substream instance.
- */
-
-static void init_substream(snd_usb_stream_t *stream, snd_usb_substream_t *subs,
-                          int iface_no, int is_input,
-                          unsigned char *buffer, int buflen)
-{
-       struct usb_device *dev;
-       struct usb_config_descriptor *config;
-       struct usb_interface *iface;
-       struct usb_interface_descriptor *alts;
-       int i, pcm_format, altno;
-       int format, channels, format_type, nr_rates;
-       struct audioformat *fp;
-       unsigned char *fmt, *csep;
-
-       dev = stream->chip->dev;
-       config = dev->actconfig;
-
-       subs->stream = stream;
-       subs->dev = dev;
-       subs->interface = iface_no;
-       INIT_LIST_HEAD(&subs->fmt_list);
-       spin_lock_init(&subs->lock);
-       if (is_input) {
-               subs->ops.prepare = prepare_capture_urb;
-               subs->ops.retire = retire_capture_urb;
-               subs->ops.prepare_sync = prepare_capture_sync_urb;
-               subs->ops.retire_sync = retire_capture_sync_urb;
-       } else {
-               subs->ops.prepare = prepare_playback_urb;
-               subs->ops.retire = retire_playback_urb;
-               subs->ops.prepare_sync = prepare_playback_sync_urb;
-               subs->ops.retire_sync = retire_playback_sync_urb;
-       }
-
-       if (iface_no < 0)
-               return;
-
-       /* parse the interface's altsettings */
-       iface = &config->interface[iface_no];
-       for (i = 0; i < iface->num_altsetting; i++) {
-               alts = &iface->altsetting[i];
-               /* skip invalid one */
-               if (alts->bInterfaceClass != USB_CLASS_AUDIO ||
-                   alts->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING ||
-                   alts->bNumEndpoints < 1)
-                       continue;
-               /* must be isochronous */
-               if ((alts->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
-                   USB_ENDPOINT_XFER_ISOC)
-                       continue;
-               /* check direction */
-               if (alts->endpoint[0].bEndpointAddress & USB_DIR_IN) {
-                       if (! is_input)
-                               continue;
-               } else {
-                       if (is_input)
-                               continue;
-               }
-
-               altno = alts->bAlternateSetting;
-
-               /* get audio formats */
-               fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, AS_GENERAL, 
iface_no, altno);
-               if (!fmt) {
-                       snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not 
found\n",
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-
-               if (fmt[0] < 7) {
-                       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 */
-                       
-               /* get format type */
-               fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, FORMAT_TYPE, 
iface_no, altno);
-               if (!fmt) {
-                       snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n", 
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-               if (fmt[0] < 8) {
-                       snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-
-               format_type = fmt[3];
-               /* FIXME: needed support for TYPE II and III */
-               if (format_type != USB_FORMAT_TYPE_I) {
-                       snd_printd(KERN_INFO "%d:%u:%d : format type %d is not 
supported yet\n",
-                                  dev->devnum, iface_no, altno, format_type);
-                       continue;
-               }
-
-               nr_rates = fmt[7];
-               if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) {
-                       snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-
-               /* FIXME: correct endianess and sign? */
-               pcm_format = -1;
-               switch (format) {
-               case USB_AUDIO_FORMAT_PCM:
-                       /* check the format byte size */
-                       switch (fmt[6]) {
-                       case 8:
-                               subs->formats |= SNDRV_PCM_FMTBIT_U8;
-                               pcm_format = SNDRV_PCM_FORMAT_U8;
-                               break;
-                       case 16:
-                               subs->formats |= SNDRV_PCM_FMTBIT_S16_LE;
-                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-                               break;
-                       case 18:
-                       case 20:
-                               if (fmt[5] == 3) {
-                                       subs->formats |= SNDRV_PCM_FMTBIT_S24_3LE;
-                                       pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
-                               } else {
-                                       snd_printk(KERN_ERR "%d:%u:%d : non-supported 
sample bit %d in %d bytes\n",
-                                                  dev->devnum, iface_no, altno, 
fmt[6], fmt[5]);
-                               }
-                               break;
-                       case 24:
-                               if (fmt[5] == 4) {
-                                       /* FIXME: correct?  or S32_LE? */
-                                       subs->formats |= SNDRV_PCM_FMTBIT_S24_LE;
-                                       pcm_format = SNDRV_PCM_FORMAT_S24_LE;
-                               } else if (fmt[5] == 3) {
-                                       subs->formats |= SNDRV_PCM_FMTBIT_S24_3LE;
-                                       pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
-                               } else {
-                                       snd_printk(KERN_ERR "%d:%u:%d : non-supported 
sample bit %d in %d bytes\n",
-                                                  dev->devnum, iface_no, altno, 
format, fmt[5]);
-                               }
-                               break;
-                       case 32:
-                               subs->formats |= SNDRV_PCM_FMTBIT_S32_LE;
-                               pcm_format = SNDRV_PCM_FORMAT_S32_LE;
-                               break;
-                       default:
-                               snd_printk(KERN_INFO "%d:%u:%d : unsupported sample 
bitwidth %d in %d bytes\n",
-                                          dev->devnum, iface_no, altno, fmt[6], 
fmt[5]);
-                               break;
-                       }
-                       break;
-               case USB_AUDIO_FORMAT_PCM8:
-                       /* Dallas DS4201 workaround */
-                       if (dev->descriptor.idVendor == 0x04fa && 
dev->descriptor.idProduct == 0x4201) {
-                               subs->formats |= ~SNDRV_PCM_FMTBIT_S8;
-                               pcm_format = SNDRV_PCM_FORMAT_S8;
-                       } else {
-                               subs->formats |= SNDRV_PCM_FMTBIT_U8;
-                               pcm_format = SNDRV_PCM_FORMAT_U8;
-                       }
-                       break;
-               case USB_AUDIO_FORMAT_IEEE_FLOAT:
-                       subs->formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
-                       pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
-                       break;
-               case USB_AUDIO_FORMAT_ALAW:
-                       subs->formats |= SNDRV_PCM_FMTBIT_A_LAW;
-                       pcm_format = SNDRV_PCM_FORMAT_A_LAW;
-                       break;
-               case USB_AUDIO_FORMAT_MU_LAW:
-                       subs->formats |= SNDRV_PCM_FMTBIT_MU_LAW;
-                       pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
-                       break;
-               default:
-                       snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
-                                  dev->devnum, iface_no, altno, format);
-                       break;
-               }
-
-               if (pcm_format < 0)
-                       continue;
-
-               channels = fmt[4];
-               if (channels < 1) {
-                       snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
-                                  dev->devnum, iface_no, altno, channels);
-                       continue;
-               }
-
-               csep = snd_usb_find_desc(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, 
iface_no, altno);
-               if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
-                       snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific 
endpoint descriptor\n", 
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-
-               fp = kmalloc(sizeof(*fp), GFP_KERNEL);
-               if (! fp) {
-                       snd_printk(KERN_ERR "cannot malloc\n");
-                       break;
-               }
-
-               memset(fp, 0, sizeof(*fp));
-               fp->format = pcm_format;
-               fp->altsetting = altno;
-               fp->altset_idx = i;
-               fp->endpoint = alts->endpoint[0].bEndpointAddress;
-               fp->ep_attr = alts->endpoint[0].bmAttributes;
-               fp->channels = channels;
-               fp->attributes = csep[3];
-
-               if (nr_rates) {
-                       /*
-                        * build the rate table and bitmap flags
-                        */
-                       int r, idx, c;
-                       /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
-                       static int conv_rates[] = {
-                               5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
-                               64000, 88200, 96000, 176400, 192000
-                       };
-                       fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
-                       if (fp->rate_table == NULL) {
-                               snd_printk(KERN_ERR "cannot malloc\n");
-                               kfree(fp);
-                               break;
-                       }
-
-                       fp->nr_rates = nr_rates;
-                       fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
-                       for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) {
-                               int rate = fp->rate_table[r] = 
combine_triple(&fmt[idx]);
-                               if (rate < fp->rate_min)
-                                       fp->rate_min = rate;
-                               else if (rate > fp->rate_max)
-                                       fp->rate_max = rate;
-                               for (c = 0; c < 13; c++) {
-                                       if (rate == conv_rates[c]) {
-                                               fp->rates |= (1 << c);
-                                               break;
-                                       }
-                               }
-#if 0 // FIXME - we need to define constraint
-                               if (c >= 13)
-                                       fp->rates |= SNDRV_PCM_KNOT; /* unconventional 
rate */
-#endif
-                       }
-
-               } else {
-                       /* continuous rates */
-                       fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
-                       fp->rate_min = combine_triple(&fmt[8]);
-                       fp->rate_max = combine_triple(&fmt[11]);
-               }
-
-               list_add_tail(&fp->list, &subs->fmt_list);
-               subs->num_formats++;
-       }
-}
-
-
-/*
- * free a substream
- */
-static void free_substream(snd_usb_substream_t *subs)
-{
-       struct list_head *p, *n;
-
-       if (subs->interface < 0)
-               return;
-
-       list_for_each_safe(p, n, &subs->fmt_list) {
-               struct audioformat *fp = list_entry(p, struct audioformat, list);
-               if (fp->rate_table)
-                       kfree(fp->rate_table);
-               kfree(fp);
-       }
-}
-
-
-/*
  * proc interface for list the supported pcm formats
  */
 static void proc_dump_substream_formats(snd_usb_substream_t *subs, snd_info_buffer_t 
*buffer)
@@ -1665,6 +1404,50 @@
 
 
 /*
+ * intialize the substream instance.
+ */
+
+static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat *fp)
+{
+       snd_usb_substream_t *subs = &as->substream[stream];
+
+       INIT_LIST_HEAD(&subs->fmt_list);
+       spin_lock_init(&subs->lock);
+
+       subs->stream = as;
+       subs->direction = stream;
+       subs->dev = as->chip->dev;
+       subs->ops = audio_urb_ops[stream];
+       snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream,
+                                     64 * 1024, 128 * 1024, GFP_ATOMIC);
+       snd_pcm_set_ops(as->pcm, stream,
+                       stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                       &snd_usb_playback_ops : &snd_usb_capture_ops);
+
+       list_add_tail(&fp->list, &subs->fmt_list);
+       subs->formats |= 1ULL << fp->format;
+       subs->endpoint = fp->endpoint;
+       subs->num_formats++;
+}
+
+
+/*
+ * free a substream
+ */
+static void free_substream(snd_usb_substream_t *subs)
+{
+       struct list_head *p, *n;
+
+       list_for_each_safe(p, n, &subs->fmt_list) {
+               struct audioformat *fp = list_entry(p, struct audioformat, list);
+               if (fp->rate_table)
+                       kfree(fp->rate_table);
+               kfree(fp);
+       }
+}
+
+
+/*
  * free a usb stream instance
  */
 static void snd_usb_audio_stream_free(snd_usb_stream_t *stream)
@@ -1689,57 +1472,71 @@
        }
 }
 
-static int snd_usb_audio_stream_new(snd_usb_audio_t *chip, unsigned char *buffer, int 
buflen, int asifin, int asifout)
+
+/*
+ * add this endpoint to the chip instance.
+ * if a stream with the same endpoint already exists, append to it.
+ * if not, create a new pcm stream.
+ */
+static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audioformat 
+*fp)
 {
+       struct list_head *p;
        snd_usb_stream_t *as;
+       snd_usb_substream_t *subs;
        snd_pcm_t *pcm;
-       char name[32];
        int err;
 
-       as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL);
-       if (as == NULL) {
-               snd_printk(KERN_ERR "cannot malloc\n");
-               return -ENOMEM;
+       list_for_each(p, &chip->pcm_list) {
+               as = list_entry(p, snd_usb_stream_t, list);
+               subs = &as->substream[stream];
+               if (! subs->endpoint)
+                       break;
+               if (subs->endpoint == fp->endpoint) {
+                       list_add_tail(&fp->list, &subs->fmt_list);
+                       subs->num_formats++;
+                       subs->formats |= 1ULL << fp->format;
+                       return 0;
+               }
        }
-       memset(as, 0, sizeof(*as));
-       as->chip = chip;
-       INIT_LIST_HEAD(&as->list);
-
-       init_substream(as, &as->substream[SNDRV_PCM_STREAM_PLAYBACK], asifout, 0, 
buffer, buflen);
-       init_substream(as, &as->substream[SNDRV_PCM_STREAM_CAPTURE], asifin, 1, 
buffer, buflen);
-
-       if (as->substream[0].num_formats == 0 && as->substream[1].num_formats == 0) {
-               snd_usb_audio_stream_free(as);
+       /* look for an empty stream */
+       list_for_each(p, &chip->pcm_list) {
+               as = list_entry(p, snd_usb_stream_t, list);
+               subs = &as->substream[stream];
+               if (subs->endpoint)
+                       continue;
+               err = snd_pcm_new_stream(as->pcm, stream, 1);
+               if (err < 0)
+                       return err;
+               init_substream(as, stream, fp);
                return 0;
        }
 
-       if (chip->pcm_devs > 0)
-               sprintf(name, "USB Audio #%d", chip->pcm_devs);
-       else
-               strcpy(name, "USB Audio");
+       /* create a new pcm */
+       as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL);
+       if (! as)
+               return -ENOMEM;
+       memset(as, 0, sizeof(*as));
+       as->pcm_index = chip->pcm_devs;
+       as->chip = chip;
        err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
-                         as->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats ? 1 : 0,
-                         as->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats ? 1 : 0,
+                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
+                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
                          &pcm);
        if (err < 0) {
-               snd_usb_audio_stream_free(as);
+               snd_magic_kfree(as);
                return err;
        }
-
        as->pcm = pcm;
-       as->pcm_index = chip->pcm_devs;
-       if (as->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback_ops);
-       if (as->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture_ops);
-
        pcm->private_data = as;
        pcm->private_free = snd_usb_audio_pcm_free;
        pcm->info_flags = 0;
+       if (chip->pcm_devs > 0)
+               sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+       else
+               strcpy(pcm->name, "USB Audio");
 
-       strcpy(pcm->name, name);
+       init_substream(as, stream, fp);
 
-       snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 128*1024, GFP_ATOMIC);
        list_add(&as->list, &chip->pcm_list);
        chip->pcm_devs++;
 
@@ -1750,6 +1547,246 @@
 
 
 /*
+ * parse the audio format type descriptor
+ * and returns the corresponding pcm format
+ */
+static int parse_audio_format_type(struct usb_device *dev, int iface_no, int altno,
+                                  int format, unsigned char *fmt)
+{
+       int format_type = fmt[3];
+       int pcm_format;
+
+       /* FIXME: needed support for TYPE II and III */
+       if (format_type != USB_FORMAT_TYPE_I) {
+               snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported 
+yet\n",
+                          dev->devnum, iface_no, altno, format_type);
+               return -1;
+       }
+       
+       /* FIXME: correct endianess and sign? */
+       pcm_format = -1;
+       switch (format) {
+       case USB_AUDIO_FORMAT_PCM:
+               /* check the format byte size */
+               switch (fmt[6]) {
+               case 8:
+                       pcm_format = SNDRV_PCM_FORMAT_U8;
+                       break;
+               case 16:
+                       pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+                       break;
+               case 18:
+               case 20:
+                       if (fmt[5] == 3)
+                               pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
+                       else
+                               snd_printk(KERN_ERR "%d:%u:%d : non-supported sample 
+bit %d in %d bytes\n",
+                                          dev->devnum, iface_no, altno, fmt[6], 
+fmt[5]);
+                       break;
+               case 24:
+                       if (fmt[5] == 4)
+                               /* FIXME: correct?  or S32_LE? */
+                               pcm_format = SNDRV_PCM_FORMAT_S24_LE;
+                       else if (fmt[5] == 3)
+                               pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
+                       else
+                               snd_printk(KERN_ERR "%d:%u:%d : non-supported sample 
+bit %d in %d bytes\n",
+                                          dev->devnum, iface_no, altno, format, 
+fmt[5]);
+                       break;
+               case 32:
+                       pcm_format = SNDRV_PCM_FORMAT_S32_LE;
+                       break;
+               default:
+                       snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth 
+%d in %d bytes\n",
+                                  dev->devnum, iface_no, altno, fmt[6], fmt[5]);
+                       break;
+               }
+               break;
+       case USB_AUDIO_FORMAT_PCM8:
+               /* Dallas DS4201 workaround */
+               if (dev->descriptor.idVendor == 0x04fa && dev->descriptor.idProduct == 
+0x4201)
+                       pcm_format = SNDRV_PCM_FORMAT_S8;
+               else
+                       pcm_format = SNDRV_PCM_FORMAT_U8;
+               break;
+       case USB_AUDIO_FORMAT_IEEE_FLOAT:
+               pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
+               break;
+       case USB_AUDIO_FORMAT_ALAW:
+               pcm_format = SNDRV_PCM_FORMAT_A_LAW;
+               break;
+       case USB_AUDIO_FORMAT_MU_LAW:
+               pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
+               break;
+       default:
+               snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
+                          dev->devnum, iface_no, altno, format);
+               break;
+       }
+       return pcm_format;
+}
+
+
+static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, int 
+buflen, int iface_no)
+{
+       struct usb_device *dev;
+       struct usb_config_descriptor *config;
+       struct usb_interface *iface;
+       struct usb_interface_descriptor *alts;
+       int i, altno, err, stream;
+       int channels, nr_rates, pcm_format, format;
+       struct audioformat *fp;
+       unsigned char *fmt, *csep;
+
+       dev = chip->dev;
+       config = dev->actconfig;
+
+       /* parse the interface's altsettings */
+       iface = &config->interface[iface_no];
+       for (i = 0; i < iface->num_altsetting; i++) {
+               alts = &iface->altsetting[i];
+               /* skip invalid one */
+               if (alts->bInterfaceClass != USB_CLASS_AUDIO ||
+                   alts->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING ||
+                   alts->bNumEndpoints < 1)
+                       continue;
+               /* must be isochronous */
+               if ((alts->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+                   USB_ENDPOINT_XFER_ISOC)
+                       continue;
+               /* check direction */
+               stream = (alts->endpoint[0].bEndpointAddress & USB_DIR_IN) ?
+                       SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+               altno = alts->bAlternateSetting;
+
+               /* get audio formats */
+               fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, AS_GENERAL, 
+iface_no, altno);
+               if (!fmt) {
+                       snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not 
+found\n",
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+
+               if (fmt[0] < 7) {
+                       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 */
+                       
+               /* get format type */
+               fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, FORMAT_TYPE, 
+iface_no, altno);
+               if (!fmt) {
+                       snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n", 
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+               if (fmt[0] < 8) {
+                       snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+
+               pcm_format = parse_audio_format_type(dev, iface_no, altno, format, 
+fmt);
+               if (pcm_format < 0)
+                       continue;
+               
+               channels = fmt[4];
+               if (channels < 1) {
+                       snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
+                                  dev->devnum, iface_no, altno, channels);
+                       continue;
+               }
+
+               nr_rates = fmt[7];
+               if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) {
+                       snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+
+               csep = snd_usb_find_desc(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, 
+iface_no, altno);
+               if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
+                       snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific 
+endpoint descriptor\n", 
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+
+               fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+               if (! fp) {
+                       snd_printk(KERN_ERR "cannot malloc\n");
+                       return -ENOMEM;
+               }
+
+               memset(fp, 0, sizeof(*fp));
+               fp->iface = iface_no;
+               fp->altsetting = altno;
+               fp->altset_idx = i;
+               fp->format = pcm_format;
+               fp->endpoint = alts->endpoint[0].bEndpointAddress;
+               fp->ep_attr = alts->endpoint[0].bmAttributes;
+               fp->channels = channels;
+               fp->attributes = csep[3];
+
+               if (nr_rates) {
+                       /*
+                        * build the rate table and bitmap flags
+                        */
+                       int r, idx, c;
+                       /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
+                       static int conv_rates[] = {
+                               5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+                               64000, 88200, 96000, 176400, 192000
+                       };
+                       fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+                       if (fp->rate_table == NULL) {
+                               snd_printk(KERN_ERR "cannot malloc\n");
+                               kfree(fp);
+                               break;
+                       }
+
+                       fp->nr_rates = nr_rates;
+                       fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
+                       for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) {
+                               int rate = fp->rate_table[r] = 
+combine_triple(&fmt[idx]);
+                               if (rate < fp->rate_min)
+                                       fp->rate_min = rate;
+                               else if (rate > fp->rate_max)
+                                       fp->rate_max = rate;
+                               for (c = 0; c < 13; c++) {
+                                       if (rate == conv_rates[c]) {
+                                               fp->rates |= (1 << c);
+                                               break;
+                                       }
+                               }
+#if 0 // FIXME - we need to define constraint
+                               if (c >= 13)
+                                       fp->rates |= SNDRV_PCM_KNOT; /* unconventional 
+rate */
+#endif
+                       }
+
+               } else {
+                       /* continuous rates */
+                       fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+                       fp->rate_min = combine_triple(&fmt[8]);
+                       fp->rate_max = combine_triple(&fmt[11]);
+               }
+
+               
+               err = add_audio_endpoint(chip, stream, fp);
+               if (err < 0) {
+                       if (fp->rate_table)
+                               kfree(fp->rate_table);
+                       kfree(fp);
+                       return err;
+               }
+       }
+       return 0;
+}
+
+
+/*
  * parse audio control descriptor and create pcm/midi streams
  */
 
@@ -1763,9 +1800,7 @@
        struct usb_config_descriptor *config;
        struct usb_interface *iface;
        unsigned char *p1;
-       unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES];
-       int numifin = 0, numifout = 0;
-       int i, j, k;
+       int i, j;
 
        /* find audiocontrol interface */
        if (!(p1 = snd_usb_find_csint_desc(buffer, buflen, NULL, HEADER, ctrlif, -1))) 
{
@@ -1804,52 +1839,9 @@
                        /* skip non-supported classes */
                        continue;
                }
-               if (iface->num_altsetting < 2) {
-                       snd_printdd(KERN_ERR "%d:%u:%d: skipping - no valid 
interface.\n",
-                                   dev->devnum, ctrlif, j);
-                       continue;
-               }
-               if (iface->altsetting[0].bNumEndpoints > 0) {
-                       /* Check all endpoints; should they all have a bandwidth of 0 
? */
-                       for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) {
-                               if (iface->altsetting[0].endpoint[k].wMaxPacketSize > 
0) {
-                                       snd_printk(KERN_ERR "%d:%u:%d ep%d : have no 
bandwith at alt[0]\n", dev->devnum, ctrlif, j, k);
-                                       break;
-                               }
-                       }
-                       if (k < iface->altsetting[0].bNumEndpoints)
-                               continue;
-               }
-               if (iface->altsetting[1].bNumEndpoints < 1) {
-                       snd_printk(KERN_ERR "%d:%u:%d : has no endpoint\n",
-                                  dev->devnum, ctrlif, j);
-                       continue;
-               }
-               /* note: this requires the data endpoint to be ep0 and
-                * the optional sync ep to be ep1, which seems to be the case
-                */
-               if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) {
-                       if (numifin < USB_MAXINTERFACES) {
-                               snd_printdd(KERN_INFO "adding an input interface 
%d:%u:%d\n", dev->devnum, ctrlif, j);
-                               ifin[numifin++] = j;
-                               usb_driver_claim_interface(&usb_audio_driver, iface, 
(void *)-1);
-                       }
-               } else {
-                       if (numifout < USB_MAXINTERFACES) {
-                               snd_printdd(KERN_INFO "adding an output interface 
%d:%u:%d\n", dev->devnum, ctrlif, j);
-                               ifout[numifout++] = j;
-                               usb_driver_claim_interface(&usb_audio_driver, iface, 
(void *)-1);
-                       }
-               }
+               parse_audio_endpoints(chip, buffer, buflen, j);
+               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
        }
-
-       /* all endpoints are parsed.  now create pcm streams */
-       for (i = 0; i < numifin && i < numifout; i++)
-               snd_usb_audio_stream_new(chip, buffer, buflen, ifin[i], ifout[i]);
-       for (j = i; j < numifin; j++)
-               snd_usb_audio_stream_new(chip, buffer, buflen, ifin[i], -1);
-       for (j = i; j < numifout; j++)
-               snd_usb_audio_stream_new(chip, buffer, buflen, -1, ifout[i]);
 
        return 0;
 }



-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
Alsa-cvslog mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-cvslog

Reply via email to