⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbaudio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		}		ep &= USB_ENDPOINT_NUMBER_MASK;		if (is_playback)			subs->syncpipe = usb_rcvisocpipe(dev, ep);		else			subs->syncpipe = usb_sndisocpipe(dev, ep);		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&		    get_endpoint(alts, 1)->bRefresh >= 1 &&		    get_endpoint(alts, 1)->bRefresh <= 9)			subs->syncinterval = get_endpoint(alts, 1)->bRefresh;		else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)			subs->syncinterval = 1;		else if (get_endpoint(alts, 1)->bInterval >= 1 &&			 get_endpoint(alts, 1)->bInterval <= 16)			subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;		else			subs->syncinterval = 3;	}	/* always fill max packet size */	if (fmt->attributes & EP_CS_ATTR_FILL_MAX)		subs->fill_max = 1;	if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0)		return err;	subs->cur_audiofmt = fmt;#if 0	printk("setting done: format = %d, rate = %d, channels = %d\n",	       fmt->format, fmt->rate, fmt->channels);	printk("  datapipe = 0x%0x, syncpipe = 0x%0x\n",	       subs->datapipe, subs->syncpipe);#endif	return 0;}/* * hw_params callback * * allocate a buffer and set the given audio format. * * so far we use a physically linear buffer although packetize transfer * doesn't need a continuous area. * if sg buffer is supported on the later version of alsa, we'll follow * that. */static int snd_usb_hw_params(struct snd_pcm_substream *substream,			     struct snd_pcm_hw_params *hw_params){	struct snd_usb_substream *subs = substream->runtime->private_data;	struct audioformat *fmt;	unsigned int channels, rate, format;	int ret, changed;	ret = snd_pcm_alloc_vmalloc_buffer(substream,					   params_buffer_bytes(hw_params));	if (ret < 0)		return ret;	format = params_format(hw_params);	rate = params_rate(hw_params);	channels = params_channels(hw_params);	fmt = find_format(subs, format, rate, channels);	if (! fmt) {		snd_printd(KERN_DEBUG "cannot set format: format = 0x%x, rate = %d, channels = %d\n",			   format, rate, channels);		return -EINVAL;	}	changed = subs->cur_audiofmt != fmt ||		subs->period_bytes != params_period_bytes(hw_params) ||		subs->cur_rate != rate;	if ((ret = set_format(subs, fmt)) < 0)		return ret;	if (subs->cur_rate != rate) {		struct usb_host_interface *alts;		struct usb_interface *iface;		iface = usb_ifnum_to_if(subs->dev, fmt->iface);		alts = &iface->altsetting[fmt->altset_idx];		ret = init_usb_sample_rate(subs->dev, subs->interface, alts, fmt, rate);		if (ret < 0)			return ret;		subs->cur_rate = rate;	}	if (changed) {		/* format changed */		release_substream_urbs(subs, 0);		/* influenced: period_bytes, channels, rate, format, */		ret = init_substream_urbs(subs, params_period_bytes(hw_params),					  params_rate(hw_params),					  snd_pcm_format_physical_width(params_format(hw_params)) * params_channels(hw_params));	}	return ret;}/* * hw_free callback * * reset the audio format and release the buffer */static int snd_usb_hw_free(struct snd_pcm_substream *substream){	struct snd_usb_substream *subs = substream->runtime->private_data;	subs->cur_audiofmt = NULL;	subs->cur_rate = 0;	subs->period_bytes = 0;	if (!subs->stream->chip->shutdown)		release_substream_urbs(subs, 0);	return snd_pcm_free_vmalloc_buffer(substream);}/* * prepare callback * * only a few subtle things... */static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream){	struct snd_pcm_runtime *runtime = substream->runtime;	struct snd_usb_substream *subs = runtime->private_data;	if (! subs->cur_audiofmt) {		snd_printk(KERN_ERR "usbaudio: no format is specified!\n");		return -ENXIO;	}	/* some unit conversions in runtime */	subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);	subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);	/* reset the pointer */	subs->hwptr_done = 0;	subs->transfer_done = 0;	subs->phase = 0;	/* clear urbs (to be sure) */	deactivate_urbs(subs, 0, 1);	wait_clear_urbs(subs);	/* for playback, submit the URBs now; otherwise, the first hwptr_done	 * updates for all URBs would happen at the same time when starting */	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {		subs->ops.prepare = prepare_nodata_playback_urb;		return start_urbs(subs, runtime);	} else		return 0;}static struct snd_pcm_hardware snd_usb_hardware ={	.info =			SNDRV_PCM_INFO_MMAP |				SNDRV_PCM_INFO_MMAP_VALID |				SNDRV_PCM_INFO_BATCH |				SNDRV_PCM_INFO_INTERLEAVED |				SNDRV_PCM_INFO_BLOCK_TRANSFER |				SNDRV_PCM_INFO_PAUSE,	.buffer_bytes_max =	1024 * 1024,	.period_bytes_min =	64,	.period_bytes_max =	512 * 1024,	.periods_min =		2,	.periods_max =		1024,};/* * h/w constraints */#ifdef HW_CONST_DEBUG#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)#else#define hwc_debug(fmt, args...) /**/#endifstatic int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audioformat *fp){	struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);	struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);	struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	/* check the format */	if (! snd_mask_test(fmts, fp->format)) {		hwc_debug("   > check: no supported format %d\n", fp->format);		return 0;	}	/* check the channels */	if (fp->channels < ct->min || fp->channels > ct->max) {		hwc_debug("   > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);		return 0;	}	/* check the rate is within the range */	if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {		hwc_debug("   > check: rate_min %d > max %d\n", fp->rate_min, it->max);		return 0;	}	if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {		hwc_debug("   > check: rate_max %d < min %d\n", fp->rate_max, it->min);		return 0;	}	return 1;}static int hw_rule_rate(struct snd_pcm_hw_params *params,			struct snd_pcm_hw_rule *rule){	struct snd_usb_substream *subs = rule->private;	struct list_head *p;	struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);	unsigned int rmin, rmax;	int changed;	hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);	changed = 0;	rmin = rmax = 0;	list_for_each(p, &subs->fmt_list) {		struct audioformat *fp;		fp = list_entry(p, struct audioformat, list);		if (! hw_check_valid_format(params, fp))			continue;		if (changed++) {			if (rmin > fp->rate_min)				rmin = fp->rate_min;			if (rmax < fp->rate_max)				rmax = fp->rate_max;		} else {			rmin = fp->rate_min;			rmax = fp->rate_max;		}	}	if (! changed) {		hwc_debug("  --> get empty\n");		it->empty = 1;		return -EINVAL;	}	changed = 0;	if (it->min < rmin) {		it->min = rmin;		it->openmin = 0;		changed = 1;	}	if (it->max > rmax) {		it->max = rmax;		it->openmax = 0;		changed = 1;	}	if (snd_interval_checkempty(it)) {		it->empty = 1;		return -EINVAL;	}	hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);	return changed;}static int hw_rule_channels(struct snd_pcm_hw_params *params,			    struct snd_pcm_hw_rule *rule){	struct snd_usb_substream *subs = rule->private;	struct list_head *p;	struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);	unsigned int rmin, rmax;	int changed;	hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);	changed = 0;	rmin = rmax = 0;	list_for_each(p, &subs->fmt_list) {		struct audioformat *fp;		fp = list_entry(p, struct audioformat, list);		if (! hw_check_valid_format(params, fp))			continue;		if (changed++) {			if (rmin > fp->channels)				rmin = fp->channels;			if (rmax < fp->channels)				rmax = fp->channels;		} else {			rmin = fp->channels;			rmax = fp->channels;		}	}	if (! changed) {		hwc_debug("  --> get empty\n");		it->empty = 1;		return -EINVAL;	}	changed = 0;	if (it->min < rmin) {		it->min = rmin;		it->openmin = 0;		changed = 1;	}	if (it->max > rmax) {		it->max = rmax;		it->openmax = 0;		changed = 1;	}	if (snd_interval_checkempty(it)) {		it->empty = 1;		return -EINVAL;	}	hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);	return changed;}static int hw_rule_format(struct snd_pcm_hw_params *params,			  struct snd_pcm_hw_rule *rule){	struct snd_usb_substream *subs = rule->private;	struct list_head *p;	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	u64 fbits;	u32 oldbits[2];	int changed;	hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);	fbits = 0;	list_for_each(p, &subs->fmt_list) {		struct audioformat *fp;		fp = list_entry(p, struct audioformat, list);		if (! hw_check_valid_format(params, fp))			continue;		fbits |= (1ULL << fp->format);	}	oldbits[0] = fmt->bits[0];	oldbits[1] = fmt->bits[1];	fmt->bits[0] &= (u32)fbits;	fmt->bits[1] &= (u32)(fbits >> 32);	if (! fmt->bits[0] && ! fmt->bits[1]) {		hwc_debug("  --> get empty\n");		return -EINVAL;	}	changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);	hwc_debug("  --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);	return changed;}#define MAX_MASK	64/* * check whether the registered audio formats need special hw-constraints */static int check_hw_params_convention(struct snd_usb_substream *subs){	int i;	u32 *channels;	u32 *rates;	u32 cmaster, rmaster;	u32 rate_min = 0, rate_max = 0;	struct list_head *p;	int err = 1;	channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL);	rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL);	list_for_each(p, &subs->fmt_list) {		struct audioformat *f;		f = list_entry(p, struct audioformat, list);		/* unconventional channels? */		if (f->channels > 32)			goto __out;		/* continuous rate min/max matches? */		if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) {			if (rate_min && f->rate_min != rate_min)				goto __out;			if (rate_max && f->rate_max != rate_max)				goto __out;			rate_min = f->rate_min;			rate_max = f->rate_max;		}		/* combination of continuous rates and fixed rates? */		if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) {			if (f->rates != rates[f->format])				goto __out;		}		if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) {			if (rates[f->format] && rates[f->format] != f->rates)				goto __out;		}		channels[f->format] |= (1 << f->channels);		rates[f->format] |= f->rates;		/* needs knot? */		if (f->rates & SNDRV_PCM_RATE_KNOT)			goto __out;	}	/* check whether channels and rates match for all formats */	cmaster = rmaster = 0;	for (i = 0; i < MAX_MASK; i++) {		if (cmaster != channels[i] && cmaster && channels[i])			goto __out;		if (rmaster != rates[i] && rmaster && rates[i])			goto __out;		if (channels[i])			cmaster = channels[i];		if (rates[i])			rmaster = rates[i];	}	/* check whether channels match for all distinct rates */	memset(channels, 0, MAX_MASK * sizeof(u32));	list_for_each(p, &subs->fmt_list) {		struct audioformat *f;		f = list_entry(p, struct audioformat, list);		if (f->rates & SNDRV_PCM_RATE_CONTINUOUS)			continue;		for (i = 0; i < 32; i++) {			if (f->rates & (1 << i))				channels[i] |= (1 << f->channels);		}	}	cmaster = 0;	for (i = 0; i < 32; i++) {		if (cmaster != channels[i] && cmaster && channels[i])			goto __out;		if (channels[i])			cmaster = channels[i];	}	err = 0; __out:	kfree(channels);	kfree(rates);	return err;}/* *  If the device supports unusual bit rates, does the request meet these? */static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,				  struct snd_usb_substream *subs){	struct audioformat *fp;	int count = 0, needs_knot = 0;	int err;	list_for_each_entry(fp, &subs->fmt_list, list) {		if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)			return 0;		count += fp->nr_rates;		if (fp->rates & SNDRV_PCM_RATE_KNOT)			needs_knot = 1;	}	if (!needs_knot)		return 0;	subs->rate_list.count = count;	subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);	subs->rate_list.mask = 0;	count = 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -