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

Commit 53ee98fe authored by Daniel Mack's avatar Daniel Mack Committed by Takashi Iwai
Browse files

ALSA: usbaudio: implement basic set of class v2.0 parser



This adds a number of parsers for audio class v2.0. In particular, the
following internals are different and now handled by the code:

* the number of streaming interfaces is now reported by an interface
  association descriptor. The old approach using a proprietary
  descriptor is deprecated.

* The number of channels per interface is now stored in the AS_GENERAL
  descriptor (used to be part of the FORMAT_TYPE descriptor).

* The list of supported sample rates is no longer stored in a variable
  length appendix of the format_type descriptor but is retrieved from
  the device using a class specific GET_RANGE command.

* Supported sample formats are now reported as 32bit bitmap rather than
  a fixed value. For now, this is worked around by choosing just one of
  them.

* A devices needs to have at least one CLOCK_SOURCE descriptor which
  denotes a clockID that is needed im the class request command.

* Many descriptors (format_type, ...) have changed their layout. Handle
  this by casting the descriptors to the appropriate structs.

Signed-off-by: default avatarDaniel Mack <daniel@caiaq.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 8fee4aff
Loading
Loading
Loading
Loading
+289 −63
Original line number Diff line number Diff line
@@ -2422,17 +2422,53 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
 * @format: the format tag (wFormatTag)
 * @fmt: the format type descriptor
 */
static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp,
				     int format, void *fmt_raw)
static int parse_audio_format_i_type(struct snd_usb_audio *chip,
				     struct audioformat *fp,
				     int format, void *_fmt,
				     int protocol)
{
	int pcm_format;
	int pcm_format, i;
	int sample_width, sample_bytes;
	struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;

	/* FIXME: correct endianess and sign? */
	pcm_format = -1;
	switch (protocol) {
	case UAC_VERSION_1: {
		struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
		sample_width = fmt->bBitResolution;
		sample_bytes = fmt->bSubframeSize;
		break;
	}

	case UAC_VERSION_2: {
		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;
			}

		break;
	}

	default:
		return -EINVAL;
	}

	/* FIXME: correct endianess and sign? */
	pcm_format = -1;

	switch (format) {
	case 0: /* some devices don't define this correctly... */
@@ -2446,6 +2482,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
				   sample_width, sample_bytes);
		}
		/* check the format byte size */
		printk(" XXXXX SAMPLE BYTES %d\n", sample_bytes);
		switch (sample_bytes) {
		case 1:
			pcm_format = SNDRV_PCM_FORMAT_S8;
@@ -2500,7 +2537,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor

/*
 * parse the format descriptor and stores the possible sample rates
 * on the audioformat table.
 * on the audioformat table (audio class v1).
 *
 * @dev: usb device
 * @fp: audioformat record
@@ -2508,7 +2545,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
 * @offset: the start offset of descriptor pointing the rate type
 *          (7 for type I and II, 8 for type II)
 */
static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioformat *fp,
static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp,
				       unsigned char *fmt, int offset)
{
	int nr_rates = fmt[offset];
@@ -2564,14 +2601,86 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
	return 0;
}

/*
 * parse the format descriptor and stores the possible sample rates
 * on the audioformat table (audio class v2).
 */
static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
				       struct audioformat *fp,
				       struct usb_host_interface *iface)
{
	struct usb_device *dev = chip->dev;
	unsigned char tmp[2], *data;
	int i, nr_rates, data_size, ret = 0;

	/* get the number of sample rates first by only fetching 2 bytes */
	ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), CS_RANGE,
			       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
			       0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000);

	if (ret < 0) {
		snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
		goto err;
	}

	nr_rates = (tmp[1] << 8) | tmp[0];
	data_size = 2 + 12 * nr_rates;
	data = kzalloc(data_size, GFP_KERNEL);
	if (!data) {
		ret = -ENOMEM;
		goto err;
	}

	/* now get the full information */
	ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), CS_RANGE,
			       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
			       0x0100, chip->clock_id << 8, data, data_size, 1000);

	if (ret < 0) {
		snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
		ret = -EINVAL;
		goto err_free;
	}

	fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
	if (!fp->rate_table) {
		ret = -ENOMEM;
		goto err_free;
	}

	fp->nr_rates = 0;
	fp->rate_min = fp->rate_max = 0;

	for (i = 0; i < nr_rates; i++) {
		int rate = combine_quad(&data[2 + 12 * i]);

		fp->rate_table[fp->nr_rates] = rate;
		if (!fp->rate_min || rate < fp->rate_min)
			fp->rate_min = rate;
		if (!fp->rate_max || rate > fp->rate_max)
			fp->rate_max = rate;
		fp->rates |= snd_pcm_rate_to_rate_bit(rate);
		fp->nr_rates++;
	}

err_free:
	kfree(data);
err:
	return ret;
}

/*
 * 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_raw)
static int parse_audio_format_i(struct snd_usb_audio *chip,
				struct audioformat *fp,
				int format, void *_fmt,
				struct usb_host_interface *iface)
{
	int pcm_format;
	struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
	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;

	if (fmt->bFormatType == USB_FORMAT_TYPE_III) {
		/* FIXME: the format type is really IECxxx
@@ -2591,30 +2700,49 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *
			pcm_format = SNDRV_PCM_FORMAT_S16_LE;
		}
	} else {
		pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
		pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol);
		if (pcm_format < 0)
			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.
	 * audio class v2 uses class specific EP0 range requests for that.
	 */
	switch (protocol) {
	case UAC_VERSION_1:
		fp->channels = fmt->bNrChannels;
		ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
		break;
	case UAC_VERSION_2:
		/* fp->channels is already set in this case */
		ret = parse_audio_format_rates_v2(chip, fp, iface);
		break;
	}

	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_raw, 7);

	return ret;
}

/*
 * parse the format type II descriptor
 */
static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat *fp,
				 int format, void *fmt_raw)
static int parse_audio_format_ii(struct snd_usb_audio *chip,
				 struct audioformat *fp,
				 int format, void *_fmt,
				 struct usb_host_interface *iface)
{
	int brate, framesize;
	struct uac_format_type_ii_discrete_descriptor *fmt = fmt_raw;
	int brate, framesize, ret;
	struct usb_interface_descriptor *altsd = get_iface_desc(iface);
	int protocol = altsd->bInterfaceProtocol;

	switch (format) {
	case USB_AUDIO_FORMAT_AC3:
@@ -2634,35 +2762,50 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat

	fp->channels = 1;

	switch (protocol) {
	case UAC_VERSION_1: {
		struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
		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;
		ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
		break;
	}
	case UAC_VERSION_2: {
		struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
		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_raw, 8); /* fmt[8..] sample rates */
		ret = parse_audio_format_rates_v2(chip, fp, iface);
		break;
	}
	}

	return ret;
}

static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
			      int format, void *fmt_raw, int stream)
			      int format, unsigned char *fmt, int stream,
			      struct usb_host_interface *iface)
{
	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->bFormatType) {
	switch (fmt[3]) {
	case USB_FORMAT_TYPE_I:
	case USB_FORMAT_TYPE_III:
		err = parse_audio_format_i(chip, fp, format, fmt);
		err = parse_audio_format_i(chip, fp, format, fmt, iface);
		break;
	case USB_FORMAT_TYPE_II:
		err = parse_audio_format_ii(chip, fp, format, fmt);
		err = parse_audio_format_ii(chip, fp, format, fmt, iface);
		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->bFormatType);
			   chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
		return -1;
	}
	fp->fmt_type = fmt->bFormatType;
	fp->fmt_type = fmt[3];
	if (err < 0)
		return err;
#if 1
@@ -2673,7 +2816,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->bFormatType == USB_FORMAT_TYPE_I &&
		if (fmt[3] == USB_FORMAT_TYPE_I &&
		    fp->rates != SNDRV_PCM_RATE_48000 &&
		    fp->rates != SNDRV_PCM_RATE_96000)
			return -1;
@@ -2702,10 +2845,10 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
	struct usb_host_interface *alts;
	struct usb_interface_descriptor *altsd;
	int i, altno, err, stream;
	int format;
	int format = 0, num_channels = 0;
	struct audioformat *fp = NULL;
	unsigned char *fmt, *csep;
	int num;
	int num, protocol;

	dev = chip->dev;

@@ -2722,10 +2865,9 @@ 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);
		protocol = altsd->bInterfaceProtocol;
		/* skip invalid one */
		if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
		     altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
@@ -2750,7 +2892,10 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
			continue;

		/* get audio formats */
		as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL);
		switch (protocol) {
		case UAC_VERSION_1: {
			struct uac_as_header_descriptor_v1 *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",
@@ -2765,6 +2910,36 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
			}

			format = le16_to_cpu(as->wFormatTag); /* remember the format value */
			break;
		}

		case UAC_VERSION_2: {
			struct uac_as_header_descriptor_v2 *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 (as->bLength < sizeof(*as)) {
				snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
					   dev->devnum, iface_no, altno);
				continue;
			}

			num_channels = as->bNrChannels;
			format = le32_to_cpu(as->bmFormats);

			break;
		}

		default:
			snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
				   dev->devnum, iface_no, altno, protocol);
			continue;
		}

		/* get format type */
		fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, FORMAT_TYPE);
@@ -2773,7 +2948,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
				   dev->devnum, iface_no, altno);
			continue;
		}
		if (fmt[0] < 8) {
		if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
		    ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
			snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
				   dev->devnum, iface_no, altno);
			continue;
@@ -2787,6 +2963,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
		if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
		    fp && fp->altsetting == 1 && fp->channels == 1 &&
		    fp->format == SNDRV_PCM_FORMAT_S16_LE &&
		    protocol == UAC_VERSION_1 &&
		    le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
							fp->maxpacksize * 2)
			continue;
@@ -2815,6 +2992,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
		fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
		fp->datainterval = parse_datainterval(chip, alts);
		fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
		/* num_channels is only set for v2 interfaces */
		fp->channels = num_channels;
		if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
			fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
					* (fp->maxpacksize & 0x7ff);
@@ -2850,7 +3029,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
		}

		/* ok, let's parse further... */
		if (parse_audio_format(chip, fp, format, fmt, stream) < 0) {
		if (parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
			kfree(fp->rate_table);
			kfree(fp);
			continue;
@@ -2958,22 +3137,26 @@ 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;
	struct usb_interface_descriptor *altsd;
	void *control_header;
	int i;
	int i, protocol;

	/* 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);
	altsd = get_iface_desc(host_iface);
	protocol = altsd->bInterfaceProtocol;

	if (!control_header) {
		snd_printk(KERN_ERR "cannot find HEADER\n");
		return -EINVAL;
	}

	h1 = control_header;
	switch (protocol) {
	case UAC_VERSION_1: {
		struct uac_ac_header_descriptor_v1 *h1 = control_header;

		if (!h1->bInCollection) {
			snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
@@ -2988,6 +3171,49 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
		for (i = 0; i < h1->bInCollection; i++)
			snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);

		break;
	}

	case UAC_VERSION_2: {
		struct uac_clock_source_descriptor *cs;
		struct usb_interface_assoc_descriptor *assoc =
			usb_ifnum_to_if(dev, ctrlif)->intf_assoc;

		if (!assoc) {
			snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
			return -EINVAL;
		}

		/* FIXME: for now, we expect there is at least one clock source
		 * descriptor and we always take the first one.
		 * We should properly support devices with multiple clock sources,
		 * clock selectors and sample rate conversion units. */

		cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
						NULL, CLOCK_SOURCE);

		if (!cs) {
			snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
			return -EINVAL;
		}

		chip->clock_id = cs->bClockID;

		for (i = 0; i < assoc->bInterfaceCount; i++) {
			int intf = assoc->bFirstInterface + i;

			if (intf != ctrlif)
				snd_usb_create_stream(chip, ctrlif, intf);
		}

		break;
	}

	default:
		snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
		return -EINVAL;
	}

	return 0;
}

+3 −0
Original line number Diff line number Diff line
@@ -142,6 +142,9 @@ struct snd_usb_audio {
	int num_interfaces;
	int num_suspended_intf;

	/* for audio class v2 */
	int clock_id;

	struct list_head pcm_list;	/* list of pcm streams */
	int pcm_devs;