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

📄 usbaudio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * * retrieve the current 12.13 frequency from pipe, and set it. * the value is referred in prepare_playback_urb(). */static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,				       struct snd_pcm_runtime *runtime,				       struct urb *urb){	unsigned int f;	unsigned long flags;	if (urb->iso_frame_desc[0].status == 0 &&	    urb->iso_frame_desc[0].actual_length == 4) {		f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;		if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {			spin_lock_irqsave(&subs->lock, flags);			subs->freqm = f;			spin_unlock_irqrestore(&subs->lock, flags);		}	}	return 0;}/* determine the number of frames in the next packet */static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs){	if (subs->fill_max)		return subs->maxframesize;	else {		subs->phase = (subs->phase & 0xffff)			+ (subs->freqm << subs->datainterval);		return min(subs->phase >> 16, subs->maxframesize);	}}/* * Prepare urb for streaming before playback starts or when paused. * * We don't have any data, so we send a frame of silence. */static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,				       struct snd_pcm_runtime *runtime,				       struct urb *urb){	unsigned int i, offs, counts;	struct snd_urb_ctx *ctx = urb->context;	int stride = runtime->frame_bits >> 3;	offs = 0;	urb->dev = ctx->subs->dev;	urb->number_of_packets = subs->packs_per_ms;	for (i = 0; i < subs->packs_per_ms; ++i) {		counts = snd_usb_audio_next_packet_size(subs);		urb->iso_frame_desc[i].offset = offs * stride;		urb->iso_frame_desc[i].length = counts * stride;		offs += counts;	}	urb->transfer_buffer_length = offs * stride;	memset(urb->transfer_buffer,	       subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,	       offs * stride);	return 0;}/* * prepare urb for playback data pipe * * Since a URB can handle only a single linear buffer, we must use double * buffering when the data to be transferred overflows the buffer boundary. * To avoid inconsistencies when updating hwptr_done, we use double buffering * for all URBs. */static int prepare_playback_urb(struct snd_usb_substream *subs,				struct snd_pcm_runtime *runtime,				struct urb *urb){	int i, stride, offs;	unsigned int counts;	unsigned long flags;	int period_elapsed = 0;	struct snd_urb_ctx *ctx = urb->context;	stride = runtime->frame_bits >> 3;	offs = 0;	urb->dev = ctx->subs->dev; /* we need to set this at each time */	urb->number_of_packets = 0;	spin_lock_irqsave(&subs->lock, flags);	for (i = 0; i < ctx->packets; i++) {		counts = snd_usb_audio_next_packet_size(subs);		/* set up descriptor */		urb->iso_frame_desc[i].offset = offs * stride;		urb->iso_frame_desc[i].length = counts * stride;		offs += counts;		urb->number_of_packets++;		subs->transfer_done += counts;		if (subs->transfer_done >= runtime->period_size) {			subs->transfer_done -= runtime->period_size;			period_elapsed = 1;			if (subs->fmt_type == USB_FORMAT_TYPE_II) {				if (subs->transfer_done > 0) {					/* FIXME: fill-max mode is not					 * supported yet */					offs -= subs->transfer_done;					counts -= subs->transfer_done;					urb->iso_frame_desc[i].length =						counts * stride;					subs->transfer_done = 0;				}				i++;				if (i < ctx->packets) {					/* add a transfer delimiter */					urb->iso_frame_desc[i].offset =						offs * stride;					urb->iso_frame_desc[i].length = 0;					urb->number_of_packets++;				}				break;			} 		}		/* finish at the frame boundary at/after the period boundary */		if (period_elapsed &&		    (i & (subs->packs_per_ms - 1)) == subs->packs_per_ms - 1)			break;	}	if (subs->hwptr_done + offs > runtime->buffer_size) {		/* err, the transferred area goes over buffer boundary. */		unsigned int len = runtime->buffer_size - subs->hwptr_done;		memcpy(urb->transfer_buffer,		       runtime->dma_area + subs->hwptr_done * stride,		       len * stride);		memcpy(urb->transfer_buffer + len * stride,		       runtime->dma_area,		       (offs - len) * stride);	} else {		memcpy(urb->transfer_buffer,		       runtime->dma_area + subs->hwptr_done * stride,		       offs * stride);	}	subs->hwptr_done += offs;	if (subs->hwptr_done >= runtime->buffer_size)		subs->hwptr_done -= runtime->buffer_size;	spin_unlock_irqrestore(&subs->lock, flags);	urb->transfer_buffer_length = offs * stride;	if (period_elapsed)		snd_pcm_period_elapsed(subs->pcm_substream);	return 0;}/* * process after playback data complete * - nothing to do */static int retire_playback_urb(struct snd_usb_substream *subs,			       struct snd_pcm_runtime *runtime,			       struct urb *urb){	return 0;}/* */static struct snd_urb_ops audio_urb_ops[2] = {	{		.prepare =	prepare_nodata_playback_urb,		.retire =	retire_playback_urb,		.prepare_sync =	prepare_playback_sync_urb,		.retire_sync =	retire_playback_sync_urb,	},	{		.prepare =	prepare_capture_urb,		.retire =	retire_capture_urb,		.prepare_sync =	prepare_capture_sync_urb,		.retire_sync =	retire_capture_sync_urb,	},};static struct snd_urb_ops audio_urb_ops_high_speed[2] = {	{		.prepare =	prepare_nodata_playback_urb,		.retire =	retire_playback_urb,		.prepare_sync =	prepare_playback_sync_urb_hs,		.retire_sync =	retire_playback_sync_urb_hs,	},	{		.prepare =	prepare_capture_urb,		.retire =	retire_capture_urb,		.prepare_sync =	prepare_capture_sync_urb_hs,		.retire_sync =	retire_capture_sync_urb,	},};/* * complete callback from data urb */static void snd_complete_urb(struct urb *urb){	struct snd_urb_ctx *ctx = urb->context;	struct snd_usb_substream *subs = ctx->subs;	struct snd_pcm_substream *substream = ctx->subs->pcm_substream;	int err = 0;	if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||	    ! subs->running || /* can be stopped during retire callback */	    (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {		clear_bit(ctx->index, &subs->active_mask);		if (err < 0) {			snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);		}	}}/* * complete callback from sync urb */static void snd_complete_sync_urb(struct urb *urb){	struct snd_urb_ctx *ctx = urb->context;	struct snd_usb_substream *subs = ctx->subs;	struct snd_pcm_substream *substream = ctx->subs->pcm_substream;	int err = 0;	if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||	    ! subs->running || /* can be stopped during retire callback */	    (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {		clear_bit(ctx->index + 16, &subs->active_mask);		if (err < 0) {			snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);		}	}}/* get the physical page pointer at the given offset */static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,					     unsigned long offset){	void *pageptr = subs->runtime->dma_area + offset;	return vmalloc_to_page(pageptr);}/* allocate virtual buffer; may be called more than once */static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size){	struct snd_pcm_runtime *runtime = subs->runtime;	if (runtime->dma_area) {		if (runtime->dma_bytes >= size)			return 0; /* already large enough */		vfree(runtime->dma_area);	}	runtime->dma_area = vmalloc(size);	if (! runtime->dma_area)		return -ENOMEM;	runtime->dma_bytes = size;	return 0;}/* free virtual buffer; may be called more than once */static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream *subs){	struct snd_pcm_runtime *runtime = subs->runtime;	vfree(runtime->dma_area);	runtime->dma_area = NULL;	return 0;}/* * unlink active urbs. */static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep){	unsigned int i;	int async;	subs->running = 0;	if (!force && subs->stream->chip->shutdown) /* to be sure... */		return -EBADFD;	async = !can_sleep && async_unlink;	if (! async && in_interrupt())		return 0;	for (i = 0; i < subs->nurbs; i++) {		if (test_bit(i, &subs->active_mask)) {			if (! test_and_set_bit(i, &subs->unlink_mask)) {				struct urb *u = subs->dataurb[i].urb;				if (async)					usb_unlink_urb(u);				else					usb_kill_urb(u);			}		}	}	if (subs->syncpipe) {		for (i = 0; i < SYNC_URBS; i++) {			if (test_bit(i+16, &subs->active_mask)) { 				if (! test_and_set_bit(i+16, &subs->unlink_mask)) {					struct urb *u = subs->syncurb[i].urb;					if (async)						usb_unlink_urb(u);					else						usb_kill_urb(u);				}			}		}	}	return 0;}static const char *usb_error_string(int err){	switch (err) {	case -ENODEV:		return "no device";	case -ENOENT:		return "endpoint not enabled";	case -EPIPE:		return "endpoint stalled";	case -ENOSPC:		return "not enough bandwidth";	case -ESHUTDOWN:		return "device disabled";	case -EHOSTUNREACH:		return "device suspended";#ifndef CONFIG_USB_EHCI_SPLIT_ISO	case -ENOSYS:		return "enable CONFIG_USB_EHCI_SPLIT_ISO to play through a hub";#endif	case -EINVAL:	case -EAGAIN:	case -EFBIG:	case -EMSGSIZE:		return "internal error";	default:		return "unknown error";	}}/* * set up and start data/sync urbs */static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime){	unsigned int i;	int err;	if (subs->stream->chip->shutdown)		return -EBADFD;	for (i = 0; i < subs->nurbs; i++) {		snd_assert(subs->dataurb[i].urb, return -EINVAL);		if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {			snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);			goto __error;		}	}	if (subs->syncpipe) {		for (i = 0; i < SYNC_URBS; i++) {			snd_assert(subs->syncurb[i].urb, return -EINVAL);			if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {				snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);				goto __error;			}		}	}	subs->active_mask = 0;	subs->unlink_mask = 0;	subs->running = 1;	for (i = 0; i < subs->nurbs; i++) {		err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);		if (err < 0) {			snd_printk(KERN_ERR "cannot submit datapipe "				   "for urb %d, error %d: %s\n",				   i, err, usb_error_string(err));			goto __error;		}		set_bit(i, &subs->active_mask);	}	if (subs->syncpipe) {		for (i = 0; i < SYNC_URBS; i++) {			err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);			if (err < 0) {				snd_printk(KERN_ERR "cannot submit syncpipe "					   "for urb %d, error %d: %s\n",					   i, err, usb_error_string(err));				goto __error;			}			set_bit(i + 16, &subs->active_mask);		}	}	return 0; __error:	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);	deactivate_urbs(subs, 0, 0);	return -EPIPE;}/* *  wait until all urbs are processed. */static int wait_clear_urbs(struct snd_usb_substream *subs){	unsigned long end_time = jiffies + msecs_to_jiffies(1000);	unsigned int i;	int alive;	do {		alive = 0;		for (i = 0; i < subs->nurbs; i++) {			if (test_bit(i, &subs->active_mask))				alive++;		}		if (subs->syncpipe) {			for (i = 0; i < SYNC_URBS; i++) {				if (test_bit(i + 16, &subs->active_mask))					alive++;			}		}		if (! alive)			break;		schedule_timeout_uninterruptible(1);	} while (time_before(jiffies, end_time));	if (alive)		snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);	return 0;}/* * return the current pcm pointer.  just return the hwptr_done value. */static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream){	struct snd_usb_substream *subs;	snd_pcm_uframes_t hwptr_done;		subs = (struct snd_usb_substream *)substream->runtime->private_data;	spin_lock(&subs->lock);	hwptr_done = subs->hwptr_done;	spin_unlock(&subs->lock);	return hwptr_done;}

⌨️ 快捷键说明

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