Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 28e1b773 authored by Daniel Mack's avatar Daniel Mack Committed by Takashi Iwai
Browse files

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: default avatarDaniel Mack <daniel@caiaq.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 40717382
Loading
Loading
Loading
Loading
+28 −4
Original line number Diff line number Diff line
@@ -81,7 +81,7 @@

/* Terminal Control Selectors */
/* 4.3.2  Class-Specific AC Interface Descriptor */
struct uac_ac_header_descriptor {
struct uac_ac_header_descriptor_v1 {
	__u8  bLength;			/* 8 + n */
	__u8  bDescriptorType;		/* USB_DT_CS_INTERFACE */
	__u8  bDescriptorSubtype;	/* UAC_MS_HEADER */
@@ -95,7 +95,7 @@ struct uac_ac_header_descriptor {

/* As above, but more useful for defining your own descriptors: */
#define DECLARE_UAC_AC_HEADER_DESCRIPTOR(n) 			\
struct uac_ac_header_descriptor_##n {				\
struct uac_ac_header_descriptor_v1_##n {			\
	__u8  bLength;						\
	__u8  bDescriptorType;					\
	__u8  bDescriptorSubtype;				\
@@ -131,7 +131,7 @@ struct uac_input_terminal_descriptor {
#define UAC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY	0x206

/* 4.3.2.2 Output Terminal Descriptor */
struct uac_output_terminal_descriptor {
struct uac_output_terminal_descriptor_v1 {
	__u8  bLength;			/* in bytes: 9 */
	__u8  bDescriptorType;		/* CS_INTERFACE descriptor type */
	__u8  bDescriptorSubtype;	/* OUTPUT_TERMINAL descriptor subtype */
@@ -171,7 +171,7 @@ struct uac_feature_unit_descriptor_##ch { \
} __attribute__ ((packed))

/* 4.5.2 Class-Specific AS Interface Descriptor */
struct uac_as_header_descriptor {
struct uac_as_header_descriptor_v1 {
	__u8  bLength;			/* in bytes: 7 */
	__u8  bDescriptorType;		/* USB_DT_CS_INTERFACE */
	__u8  bDescriptorSubtype;	/* AS_GENERAL */
@@ -232,6 +232,19 @@ struct uac_format_type_i_discrete_descriptor_##n { \

#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n)	(8 + (n * 3))

/* Formats - Audio Data Format Type I Codes */

struct uac_format_type_ii_discrete_descriptor {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubtype;
	__u8 bFormatType;
	__le16 wMaxBitRate;
	__le16 wSamplesPerFrame;
	__u8 bSamFreqType;
	__u8 tSamFreq[][3];
} __attribute__((packed));

/* Formats - A.2 Format Type Codes */
#define UAC_FORMAT_TYPE_UNDEFINED	0x0
#define UAC_FORMAT_TYPE_I		0x1
@@ -253,6 +266,17 @@ struct uac_iso_endpoint_descriptor {
#define UAC_EP_CS_ATTR_FILL_MAX		0x80

/* A.10.2 Feature Unit Control Selectors */

struct uac_feature_unit_descriptor {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubtype;
	__u8 bUnitID;
	__u8 bSourceID;
	__u8 bControlSize;
	__u8 controls[0]; /* variable length */
} __attribute__((packed));

#define UAC_FU_CONTROL_UNDEFINED	0x00
#define UAC_MUTE_CONTROL		0x01
#define UAC_VOLUME_CONTROL		0x02
+120 −78
Original line number Diff line number Diff line
@@ -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 */
@@ -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,45 +2892,25 @@ static void snd_usb_stream_disconnect(struct list_head *head)
	}
}

/*
 * parse audio control descriptor and create pcm/midi streams
 */
static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
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 *host_iface;
	struct usb_interface *iface;
	unsigned char *p1;
	int i, j;

	/* 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))) {
		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");
		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);
	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, j);
			continue;
			   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, j);
			continue;
		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 ||
@@ -2922,29 +2919,75 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
		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;
			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);
			continue;

		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, j, altsd->bInterfaceClass);
		snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
					dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
		/* skip non-supported classes */
			continue;
		return -EINVAL;
	}

	if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
		snd_printk(KERN_ERR "low speed audio streaming not supported\n");
			continue;
		return -EINVAL;
	}
		if (! parse_audio_endpoints(chip, j)) {
			usb_set_interface(dev, j, 0); /* reset the current interface */

	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
 */
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 uac_ac_header_descriptor_v1 *h1;
	void *control_header;
	int i;

	/* find audiocontrol interface */
	host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
	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;
	}

	h1 = control_header;

	if (!h1->bInCollection) {
		snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
		return -EINVAL;
	}

	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;

+20 −17
Original line number Diff line number Diff line
@@ -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;
	}