[alsa-devel] [PATCH 1/4] ALSA: usbaudio: parse USB descriptors with structs

Daniel Mack daniel at caiaq.de
Mon Feb 22 17:28:54 CET 2010


In preparation of support for v2.0 audio class, introduce structs 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 at caiaq.de>
---
 sound/usb/usbaudio.c |  194 ++++++++++++++++++++++++++++++--------------------
 sound/usb/usbaudio.h |   83 +++++++++++++++++++++
 sound/usb/usbmixer.c |   37 +++++-----
 3 files changed, 218 insertions(+), 96 deletions(-)

diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 4208b44..8925879 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -2421,15 +2421,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 usb_format_type_i *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 +2444,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 +2465,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 +2566,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 usb_format_type_i *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 +2593,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 usb_format_type_ii *fmt = fmt_raw;
+
 	switch (format) {
 	case USB_AUDIO_FORMAT_AC3:
 		/* FIXME: there is no AC3 format defined yet */
@@ -2622,20 +2629,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 usb_format_type_i *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 +2657,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 +2671,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 +2720,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 		num = 4;
 
 	for (i = 0; i < num; i++) {
+		struct usb_as_interface_v1 *as;
+
 		alts = &iface->altsetting[i];
 		altsd = get_iface_desc(alts);
 		/* skip invalid one */
@@ -2734,20 +2748,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 +2890,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 +2956,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 usb_ac_intf_header_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;
 }
 
@@ -3581,7 +3622,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/usbaudio.h b/sound/usb/usbaudio.h
index 9d8cea4..c466ccc 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -117,6 +117,89 @@
 #define USB_ID_PRODUCT(id) ((u16)(id))
 
 /*
+ * class-specific AC header descriptors
+ */
+
+struct usb_ac_intf_header_v1 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u16 bcdADC;
+	__u16 wTotalLength;
+	__u8 bInCollection;
+	__u8 baInterfaceNr[0];
+} __attribute__((packed));
+
+/*
+ * class specific format type descriptors
+ */
+
+struct usb_format_type_i {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatType;
+	__u8 bNrChannels;
+	__u8 bSubframeSize;
+	__u8 bBitResolution;
+	__u8 bSamFreqType;
+	__u8 samplerates[0];
+} __attribute__((packed));
+
+struct usb_format_type_ii {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatType;
+	__u16 wMaxBitRate;
+	__u16 wSamplesPerFrame;
+	__u8 bSamFreqType;
+	__u8 samplerates[0];
+} __attribute__((packed));
+
+/*
+ * class specific AS interface descriptors
+ */
+
+struct usb_as_interface_v1 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalLink;
+	__u8 bDelay;
+	__u16 wFormatTag;
+} __attribute__((packed));
+
+/*
+ * class specific feature unit descriptors
+ */
+
+struct usb_feature_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bSourceID;
+	__u8 bControlSize;
+	__u8 controls[0]; /* variable length */
+} __attribute__((packed));
+
+/*
+ * class specific terminal types
+ */
+
+struct usb_output_terminal_v1 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bSourceID;
+	__u8 iTerminal;
+} __attribute__((packed));
+
+/*
  */
 
 struct snd_usb_audio {
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index dd0c1d7..93d3339 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -278,7 +278,6 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un
 {
 	unsigned char *p;
 
-	p = NULL;
 	while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
 				      USB_DT_CS_INTERFACE)) != NULL) {
 		if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit)
@@ -1083,29 +1082,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 usb_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):
@@ -1116,21 +1116,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;
@@ -1777,7 +1777,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 usb_output_terminal_v1 *desc;
 	struct mixer_build state;
 	int err;
 	const struct usbmix_ctl_map *map;
@@ -1800,15 +1800,14 @@ 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;
 	}
-- 
1.6.6.2



More information about the Alsa-devel mailing list