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

📄 usbaudio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * start/stop playback substream */static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream,					int cmd){	struct snd_usb_substream *subs = substream->runtime->private_data;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		subs->ops.prepare = prepare_playback_urb;		return 0;	case SNDRV_PCM_TRIGGER_STOP:		return deactivate_urbs(subs, 0, 0);	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		subs->ops.prepare = prepare_nodata_playback_urb;		return 0;	default:		return -EINVAL;	}}/* * start/stop capture substream */static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream,				       int cmd){	struct snd_usb_substream *subs = substream->runtime->private_data;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		subs->ops.retire = retire_capture_urb;		return start_urbs(subs, substream->runtime);	case SNDRV_PCM_TRIGGER_STOP:		return deactivate_urbs(subs, 0, 0);	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		subs->ops.retire = retire_paused_capture_urb;		return 0;	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		subs->ops.retire = retire_capture_urb;		return 0;	default:		return -EINVAL;	}}/* * release a urb data */static void release_urb_ctx(struct snd_urb_ctx *u){	if (u->urb) {		if (u->buffer_size)			usb_buffer_free(u->subs->dev, u->buffer_size,					u->urb->transfer_buffer,					u->urb->transfer_dma);		usb_free_urb(u->urb);		u->urb = NULL;	}}/* * release a substream */static void release_substream_urbs(struct snd_usb_substream *subs, int force){	int i;	/* stop urbs (to be sure) */	deactivate_urbs(subs, force, 1);	wait_clear_urbs(subs);	for (i = 0; i < MAX_URBS; i++)		release_urb_ctx(&subs->dataurb[i]);	for (i = 0; i < SYNC_URBS; i++)		release_urb_ctx(&subs->syncurb[i]);	usb_buffer_free(subs->dev, SYNC_URBS * 4,			subs->syncbuf, subs->sync_dma);	subs->syncbuf = NULL;	subs->nurbs = 0;}/* * initialize a substream for plaback/capture */static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int period_bytes,			       unsigned int rate, unsigned int frame_bits){	unsigned int maxsize, n, i;	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;	unsigned int npacks[MAX_URBS], urb_packs, total_packs, packs_per_ms;	/* calculate the frequency in 16.16 format */	if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)		subs->freqn = get_usb_full_speed_rate(rate);	else		subs->freqn = get_usb_high_speed_rate(rate);	subs->freqm = subs->freqn;	/* calculate max. frequency */	if (subs->maxpacksize) {		/* whatever fits into a max. size packet */		maxsize = subs->maxpacksize;		subs->freqmax = (maxsize / (frame_bits >> 3))				<< (16 - subs->datainterval);	} else {		/* no max. packet size: just take 25% higher than nominal */		subs->freqmax = subs->freqn + (subs->freqn >> 2);		maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))				>> (16 - subs->datainterval);	}	subs->phase = 0;	if (subs->fill_max)		subs->curpacksize = subs->maxpacksize;	else		subs->curpacksize = maxsize;	if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)		packs_per_ms = 8 >> subs->datainterval;	else		packs_per_ms = 1;	subs->packs_per_ms = packs_per_ms;	if (is_playback) {		urb_packs = nrpacks;		urb_packs = max(urb_packs, (unsigned int)MIN_PACKS_URB);		urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);	} else		urb_packs = 1;	urb_packs *= packs_per_ms;	/* decide how many packets to be used */	if (is_playback) {		unsigned int minsize;		/* determine how small a packet can be */		minsize = (subs->freqn >> (16 - subs->datainterval))			  * (frame_bits >> 3);		/* with sync from device, assume it can be 12% lower */		if (subs->syncpipe)			minsize -= minsize >> 3;		minsize = max(minsize, 1u);		total_packs = (period_bytes + minsize - 1) / minsize;		/* round up to multiple of packs_per_ms */		total_packs = (total_packs + packs_per_ms - 1)				& ~(packs_per_ms - 1);		/* we need at least two URBs for queueing */		if (total_packs < 2 * MIN_PACKS_URB * packs_per_ms)			total_packs = 2 * MIN_PACKS_URB * packs_per_ms;	} else {		total_packs = MAX_URBS * urb_packs;	}	subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;	if (subs->nurbs > MAX_URBS) {		/* too much... */		subs->nurbs = MAX_URBS;		total_packs = MAX_URBS * urb_packs;	}	n = total_packs;	for (i = 0; i < subs->nurbs; i++) {		npacks[i] = n > urb_packs ? urb_packs : n;		n -= urb_packs;	}	if (subs->nurbs <= 1) {		/* too little - we need at least two packets		 * to ensure contiguous playback/capture		 */		subs->nurbs = 2;		npacks[0] = (total_packs + 1) / 2;		npacks[1] = total_packs - npacks[0];	} else if (npacks[subs->nurbs-1] < MIN_PACKS_URB * packs_per_ms) {		/* the last packet is too small.. */		if (subs->nurbs > 2) {			/* merge to the first one */			npacks[0] += npacks[subs->nurbs - 1];			subs->nurbs--;		} else {			/* divide to two */			subs->nurbs = 2;			npacks[0] = (total_packs + 1) / 2;			npacks[1] = total_packs - npacks[0];		}	}	/* allocate and initialize data urbs */	for (i = 0; i < subs->nurbs; i++) {		struct snd_urb_ctx *u = &subs->dataurb[i];		u->index = i;		u->subs = subs;		u->packets = npacks[i];		u->buffer_size = maxsize * u->packets;		if (subs->fmt_type == USB_FORMAT_TYPE_II)			u->packets++; /* for transfer delimiter */		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);		if (! u->urb)			goto out_of_memory;		u->urb->transfer_buffer =			usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,					 &u->urb->transfer_dma);		if (! u->urb->transfer_buffer)			goto out_of_memory;		u->urb->pipe = subs->datapipe;		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;		u->urb->interval = 1 << subs->datainterval;		u->urb->context = u;		u->urb->complete = snd_complete_urb;	}	if (subs->syncpipe) {		/* allocate and initialize sync urbs */		subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,						 GFP_KERNEL, &subs->sync_dma);		if (! subs->syncbuf)			goto out_of_memory;		for (i = 0; i < SYNC_URBS; i++) {			struct snd_urb_ctx *u = &subs->syncurb[i];			u->index = i;			u->subs = subs;			u->packets = 1;			u->urb = usb_alloc_urb(1, GFP_KERNEL);			if (! u->urb)				goto out_of_memory;			u->urb->transfer_buffer = subs->syncbuf + i * 4;			u->urb->transfer_dma = subs->sync_dma + i * 4;			u->urb->transfer_buffer_length = 4;			u->urb->pipe = subs->syncpipe;			u->urb->transfer_flags = URB_ISO_ASAP |						 URB_NO_TRANSFER_DMA_MAP;			u->urb->number_of_packets = 1;			u->urb->interval = 1 << subs->syncinterval;			u->urb->context = u;			u->urb->complete = snd_complete_sync_urb;		}	}	return 0;out_of_memory:	release_substream_urbs(subs, 0);	return -ENOMEM;}/* * find a matching audio format */static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,				       unsigned int rate, unsigned int channels){	struct list_head *p;	struct audioformat *found = NULL;	int cur_attr = 0, attr;	list_for_each(p, &subs->fmt_list) {		struct audioformat *fp;		fp = list_entry(p, struct audioformat, list);		if (fp->format != format || fp->channels != channels)			continue;		if (rate < fp->rate_min || rate > fp->rate_max)			continue;		if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {			unsigned int i;			for (i = 0; i < fp->nr_rates; i++)				if (fp->rate_table[i] == rate)					break;			if (i >= fp->nr_rates)				continue;		}		attr = fp->ep_attr & EP_ATTR_MASK;		if (! found) {			found = fp;			cur_attr = attr;			continue;		}		/* avoid async out and adaptive in if the other method		 * supports the same format.		 * this is a workaround for the case like		 * M-audio audiophile USB.		 */		if (attr != cur_attr) {			if ((attr == EP_ATTR_ASYNC &&			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||			    (attr == EP_ATTR_ADAPTIVE &&			     subs->direction == SNDRV_PCM_STREAM_CAPTURE))				continue;			if ((cur_attr == EP_ATTR_ASYNC &&			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||			    (cur_attr == EP_ATTR_ADAPTIVE &&			     subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {				found = fp;				cur_attr = attr;				continue;			}		}		/* find the format with the largest max. packet size */		if (fp->maxpacksize > found->maxpacksize) {			found = fp;			cur_attr = attr;		}	}	return found;}/* * initialize the picth control and sample rate */static int init_usb_pitch(struct usb_device *dev, int iface,			  struct usb_host_interface *alts,			  struct audioformat *fmt){	unsigned int ep;	unsigned char data[1];	int err;	ep = get_endpoint(alts, 0)->bEndpointAddress;	/* if endpoint has pitch control, enable it */	if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) {		data[0] = 1;		if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,					   PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {			snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",				   dev->devnum, iface, ep);			return err;		}	}	return 0;}static int init_usb_sample_rate(struct usb_device *dev, int iface,				struct usb_host_interface *alts,				struct audioformat *fmt, int rate){	unsigned int ep;	unsigned char data[3];	int err;	ep = get_endpoint(alts, 0)->bEndpointAddress;	/* if endpoint has sampling rate control, set it */	if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) {		int crate;		data[0] = rate;		data[1] = rate >> 8;		data[2] = rate >> 16;		if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {			snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n",				   dev->devnum, iface, fmt->altsetting, rate, ep);			return err;		}		if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR,					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {			snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep 0x%x\n",				   dev->devnum, iface, fmt->altsetting, ep);			return 0; /* some devices don't support reading */		}		crate = data[0] | (data[1] << 8) | (data[2] << 16);		if (crate != rate) {			snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);			// runtime->rate = crate;		}	}	return 0;}/* * find a matching format and set up the interface */static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt){	struct usb_device *dev = subs->dev;	struct usb_host_interface *alts;	struct usb_interface_descriptor *altsd;	struct usb_interface *iface;	unsigned int ep, attr;	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;	int err;	iface = usb_ifnum_to_if(dev, fmt->iface);	snd_assert(iface, return -EINVAL);	alts = &iface->altsetting[fmt->altset_idx];	altsd = get_iface_desc(alts);	snd_assert(altsd->bAlternateSetting == fmt->altsetting, return -EINVAL);	if (fmt == subs->cur_audiofmt)		return 0;	/* close the old interface */	if (subs->interface >= 0 && subs->interface != fmt->iface) {		if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {			snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",				dev->devnum, fmt->iface, fmt->altsetting);			return -EIO;		}		subs->interface = -1;		subs->format = 0;	}	/* set interface */	if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) {		if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {			snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",				   dev->devnum, fmt->iface, fmt->altsetting);			return -EIO;		}		snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);		subs->interface = fmt->iface;		subs->format = fmt->altset_idx;	}	/* create a data pipe */	ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;	if (is_playback)		subs->datapipe = usb_sndisocpipe(dev, ep);	else		subs->datapipe = usb_rcvisocpipe(dev, ep);	if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH &&	    get_endpoint(alts, 0)->bInterval >= 1 &&	    get_endpoint(alts, 0)->bInterval <= 4)		subs->datainterval = get_endpoint(alts, 0)->bInterval - 1;	else		subs->datainterval = 0;	subs->syncpipe = subs->syncinterval = 0;	subs->maxpacksize = fmt->maxpacksize;	subs->fill_max = 0;	/* we need a sync pipe in async OUT or adaptive IN mode */	/* check the number of EP, since some devices have broken	 * descriptors which fool us.  if it has only one EP,	 * assume it as adaptive-out or sync-in.	 */	attr = fmt->ep_attr & EP_ATTR_MASK;	if (((is_playback && attr == EP_ATTR_ASYNC) ||	     (! is_playback && attr == EP_ATTR_ADAPTIVE)) &&	    altsd->bNumEndpoints >= 2) {		/* check sync-pipe endpoint */		/* ... and check descriptor size before accessing bSynchAddress		   because there is a version of the SB Audigy 2 NX firmware lacking		   the audio fields in the endpoint descriptors */		if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||		    (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&		     get_endpoint(alts, 1)->bSynchAddress != 0)) {			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",				   dev->devnum, fmt->iface, fmt->altsetting);			return -EINVAL;		}		ep = get_endpoint(alts, 1)->bEndpointAddress;		if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&		    (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||		     (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",				   dev->devnum, fmt->iface, fmt->altsetting);			return -EINVAL;

⌨️ 快捷键说明

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