/*
 * parse the format type II descriptor
 */
static int parse_audio_format_ii(struct snd_usb_audio *chip,
				 struct audioformat *fp,
				 int format, void *_fmt)
{
	int brate, framesize, ret;

	switch (format) {
	case UAC_FORMAT_TYPE_II_AC3:
		/* FIXME: there is no AC3 format defined yet */
		// 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->formats = SNDRV_PCM_FMTBIT_MPEG;
		break;
	default:
		usb_audio_info(chip,
			 "%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
			 fp->iface, fp->altsetting, format);
		fp->formats = SNDRV_PCM_FMTBIT_MPEG;
		break;
	}

	fp->channels = 1;

	switch (fp->protocol) {
	default:
	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);
		usb_audio_info(chip, "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);
		usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
		fp->frame_size = framesize;
		ret = parse_audio_format_rates_v2(chip, fp);
		break;
	}
	}

	return ret;
}
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
			       struct audioformat *fp, unsigned int format,
			       struct uac_format_type_i_continuous_descriptor *fmt,
			       int stream)
{
	int err;

	switch (fmt->bFormatType) {
	case UAC_FORMAT_TYPE_I:
	case UAC_FORMAT_TYPE_III:
		err = parse_audio_format_i(chip, fp, format, fmt);
		break;
	case UAC_FORMAT_TYPE_II:
		err = parse_audio_format_ii(chip, fp, format, fmt);
		break;
	default:
		usb_audio_info(chip,
			 "%u:%d : format type %d is not supported yet\n",
			 fp->iface, fp->altsetting,
			 fmt->bFormatType);
		return -ENOTSUPP;
	}
	fp->fmt_type = fmt->bFormatType;
	if (err < 0)
		return err;
#if 1
	/* FIXME: temporary hack for extigy/audigy 2 nx/zs */
	/* extigy apparently supports sample rates other than 48k
	 * but not in ordinary way.  so we enable only 48k atm.
	 */
	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 == UAC_FORMAT_TYPE_I &&
		    fp->rates != SNDRV_PCM_RATE_48000 &&
		    fp->rates != SNDRV_PCM_RATE_96000)
			return -ENOTSUPP;
	}
#endif
	return 0;
}
/*
 * parse the audio format type I descriptor
 * and returns the corresponding pcm format
 *
 * @dev: usb device
 * @fp: audioformat record
 * @format: the format tag (wFormatTag)
 * @fmt: the format type descriptor
 */
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
				     struct audioformat *fp,
				     unsigned int format, void *_fmt)
{
	int sample_width, sample_bytes;
	u64 pcm_formats = 0;

	switch (fp->protocol) {
	case UAC_VERSION_1:
	default: {
		struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
		sample_width = fmt->bBitResolution;
		sample_bytes = fmt->bSubframeSize;
		format = 1 << format;
		break;
	}

	case UAC_VERSION_2: {
		struct uac_format_type_i_ext_descriptor *fmt = _fmt;
		sample_width = fmt->bBitResolution;
		sample_bytes = fmt->bSubslotSize;

		if (format & UAC2_FORMAT_TYPE_I_RAW_DATA)
			pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;

		format <<= 1;
		break;
	}
	}

	if ((pcm_formats == 0) &&
	    (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) {
		/* some devices don't define this correctly... */
		usb_audio_info(chip, "%u:%d : format type 0 is detected, processed as PCM\n",
			fp->iface, fp->altsetting);
		format = 1 << UAC_FORMAT_TYPE_I_PCM;
	}
	if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
		if (chip->usb_id == USB_ID(0x0582, 0x0016) /* Edirol SD-90 */ &&
		    sample_width == 24 && sample_bytes == 2)
			sample_bytes = 3;
		else if (sample_width > sample_bytes * 8) {
			usb_audio_info(chip, "%u:%d : sample bitwidth %d in over sample bytes %d\n",
				 fp->iface, fp->altsetting,
				 sample_width, sample_bytes);
		}
		/* check the format byte size */
		switch (sample_bytes) {
		case 1:
			pcm_formats |= SNDRV_PCM_FMTBIT_S8;
			break;
		case 2:
			if (snd_usb_is_big_endian_format(chip, fp))
				pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */
			else
				pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE;
			break;
		case 3:
			if (snd_usb_is_big_endian_format(chip, fp))
				pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */
			else
				pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE;
			break;
		case 4:
			pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
			break;
		default:
			usb_audio_info(chip,
				 "%u:%d : unsupported sample bitwidth %d in %d bytes\n",
				 fp->iface, fp->altsetting,
				 sample_width, sample_bytes);
			break;
		}
	}
	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_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) {
		usb_audio_info(chip,
			 "%u:%d : unsupported format bits %#x\n",
			 fp->iface, fp->altsetting, format);
	}

	pcm_formats |= snd_usb_interface_dsd_format_quirks(chip, fp, sample_bytes);

	return pcm_formats;
}
Example #4
0
static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
				   unsigned long *visited, bool validate)
{
	struct uac3_clock_source_descriptor *source;
	struct uac3_clock_selector_descriptor *selector;
	struct uac3_clock_multiplier_descriptor *multiplier;

	entity_id &= 0xff;

	if (test_and_set_bit(entity_id, visited)) {
		usb_audio_warn(chip,
			 "%s(): recursive clock topology detected, id %d.\n",
			 __func__, entity_id);
		return -EINVAL;
	}

	/* first, see if the ID we're looking for is a clock source already */
	source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
	if (source) {
		entity_id = source->bClockID;
		if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3,
								entity_id)) {
			usb_audio_err(chip,
				"clock source %d is not valid, cannot use\n",
				entity_id);
			return -ENXIO;
		}
		return entity_id;
	}

	selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id);
	if (selector) {
		int ret, i, cur;

		/* the entity ID we are looking for is a selector.
		 * find out what it currently selects */
		ret = uac_clock_selector_get_val(chip, selector->bClockID);
		if (ret < 0)
			return ret;

		/* Selector values are one-based */

		if (ret > selector->bNrInPins || ret < 1) {
			usb_audio_err(chip,
				"%s(): selector reported illegal value, id %d, ret %d\n",
				__func__, selector->bClockID, ret);

			return -EINVAL;
		}

		cur = ret;
		ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1],
					       visited, validate);
		if (!validate || ret > 0 || !chip->autoclock)
			return ret;

		/* The current clock source is invalid, try others. */
		for (i = 1; i <= selector->bNrInPins; i++) {
			int err;

			if (i == cur)
				continue;

			ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
				visited, true);
			if (ret < 0)
				continue;

			err = uac_clock_selector_set_val(chip, entity_id, i);
			if (err < 0)
				continue;

			usb_audio_info(chip,
				 "found and selected valid clock source %d\n",
				 ret);
			return ret;
		}

		return -ENXIO;
	}

	/* FIXME: multipliers only act as pass-thru element for now */
	multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
						      entity_id);
	if (multiplier)
		return __uac3_clock_find_source(chip, multiplier->bCSourceID,
						visited, validate);

	return -EINVAL;
}