usbaudio.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,293 行 · 第 1/5 页

C
2,293
字号
 * the value is referred in prepare_playback_urb(). */static int retire_playback_sync_urb(snd_usb_substream_t *subs,				    snd_pcm_runtime_t *runtime,				    struct urb *urb){	int i;	unsigned int f, found;	unsigned char *cp = urb->transfer_buffer;	unsigned long flags;	found = 0;	for (i = 0; i < urb->number_of_packets; i++, cp += 4) {		if (urb->iso_frame_desc[i].status ||		    urb->iso_frame_desc[i].actual_length < 3)			continue;		f = combine_triple(cp) << 2;#if 0		if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) {			snd_printd(KERN_WARNING "requested frequency %d (%u,%03uHz) out of range (current nominal %d (%u,%03uHz))\n",				   f, f >> 14, (f & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1),				   subs->freqn, subs->freqn >> 14, (subs->freqn & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1));			continue;		}#endif		found = f;	}	if (found) {		spin_lock_irqsave(&subs->lock, flags);		subs->freqm = found;		spin_unlock_irqrestore(&subs->lock, flags);	}	return 0;}/* * process after high speed playback sync complete * * 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(snd_usb_substream_t *subs,				       snd_pcm_runtime_t *runtime,				       struct urb *urb){	int i;	unsigned int found;	unsigned char *cp = urb->transfer_buffer;	unsigned long flags;	found = 0;	for (i = 0; i < urb->number_of_packets; i++, cp += 4) {		if (urb->iso_frame_desc[i].status ||		    urb->iso_frame_desc[i].actual_length < 4)			continue;		found = combine_quad(cp) & 0x0fffffff;	}	if (found) {		spin_lock_irqsave(&subs->lock, flags);		subs->freqm = found;		spin_unlock_irqrestore(&subs->lock, flags);	}	return 0;}/* * prepare urb for playback data pipe * * we copy the data directly from the pcm buffer. * the current position to be copied is held in hwptr field. * since a urb can handle only a single linear buffer, if the total * transferred area overflows the buffer boundary, we cannot send * it directly from the buffer.  thus the data is once copied to * a temporary buffer and urb points to that. */static int prepare_playback_urb(snd_usb_substream_t *subs,				snd_pcm_runtime_t *runtime,				struct urb *urb){	int i, stride, offs;	unsigned int counts;	unsigned long flags;	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)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++) {		/* calculate the size of a packet */		if (subs->fill_max)			counts = subs->maxframesize; /* fixed */		else {			subs->phase = (subs->phase & 0xffff) + subs->freqm;			counts = subs->phase >> 16;			if (counts > subs->maxframesize)				counts = subs->maxframesize;		}		/* 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_sched += counts;		if (subs->transfer_sched >= runtime->period_size) {			subs->transfer_sched -= runtime->period_size;			if (subs->fmt_type == USB_FORMAT_TYPE_II) {				if (subs->transfer_sched > 0) {					/* FIXME: fill-max mode is not supported yet */					offs -= subs->transfer_sched;					counts -= subs->transfer_sched;					urb->iso_frame_desc[i].length = counts * stride;					subs->transfer_sched = 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; 		}	}	if (subs->hwptr + offs > runtime->buffer_size) {		/* err, the transferred area goes over buffer boundary.		 * copy the data to the temp buffer.		 */		int len;		len = runtime->buffer_size - subs->hwptr;		urb->transfer_buffer = subs->tmpbuf;		memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride);		memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride);		subs->hwptr += offs;		subs->hwptr -= runtime->buffer_size;	} else {		/* set the buffer pointer */		urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;		subs->hwptr += offs;	}	spin_unlock_irqrestore(&subs->lock, flags);	urb->transfer_buffer_length = offs * stride;	ctx->transfer = offs;	return 0;}/* * process after playback data complete * * update the current position and call callback if a period is processed. */static int retire_playback_urb(snd_usb_substream_t *subs,			       snd_pcm_runtime_t *runtime,			       struct urb *urb){	unsigned long flags;	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;	spin_lock_irqsave(&subs->lock, flags);	subs->transfer_done += ctx->transfer;	subs->hwptr_done += ctx->transfer;	ctx->transfer = 0;	if (subs->hwptr_done >= runtime->buffer_size)		subs->hwptr_done -= runtime->buffer_size;	if (subs->transfer_done >= runtime->period_size) {		subs->transfer_done -= runtime->period_size;		spin_unlock_irqrestore(&subs->lock, flags);		snd_pcm_period_elapsed(subs->pcm_substream);	} else		spin_unlock_irqrestore(&subs->lock, flags);	return 0;}/* */static struct snd_urb_ops audio_urb_ops[2] = {	{		.prepare =	prepare_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_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 pt_regs *regs){	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;	snd_usb_substream_t *subs = ctx->subs;	snd_pcm_substream_t *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 pt_regs *regs){	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;	snd_usb_substream_t *subs = ctx->subs;	snd_pcm_substream_t *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);		}	}}/* * unlink active urbs. */static int deactivate_urbs(snd_usb_substream_t *subs, int force, int can_sleep){	unsigned int i;	int async;	subs->running = 0;	if (!force && subs->stream->chip->shutdown) /* to be sure... */		return 0;	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)					u->transfer_flags |= URB_ASYNC_UNLINK;				else					u->transfer_flags &= ~URB_ASYNC_UNLINK;				usb_unlink_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)						u->transfer_flags |= URB_ASYNC_UNLINK;					else						u->transfer_flags &= ~URB_ASYNC_UNLINK;					usb_unlink_urb(u);				}			}		}	}	return 0;}/* * set up and start data/sync urbs */static int start_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime){	unsigned int i;	int err;	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++) {		if ((err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC)) < 0) {			snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);			goto __error;		}		set_bit(i, &subs->active_mask);	}	if (subs->syncpipe) {		for (i = 0; i < SYNC_URBS; i++) {			if ((err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC)) < 0) {				snd_printk(KERN_ERR "cannot submit syncpipe for urb %d, err = %d\n", i, 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(snd_usb_substream_t *subs){	int timeout = HZ;	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;		set_current_state(TASK_UNINTERRUPTIBLE);		schedule_timeout(1);	} while (--timeout > 0);	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(snd_pcm_substream_t *substream){	snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;	return subs->hwptr_done;}/* * start/stop substream */static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd){	snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;	int err;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		err = start_urbs(subs, substream->runtime);		break;	case SNDRV_PCM_TRIGGER_STOP:		err = deactivate_urbs(subs, 0, 0);		break;	default:		err = -EINVAL;		break;	}	return err < 0 ? err : 0;}/* * release a urb data */static void release_urb_ctx(snd_urb_ctx_t *u){	if (u->urb) {		usb_free_urb(u->urb);		u->urb = NULL;	}	if (u->buf) {		kfree(u->buf);		u->buf = NULL;	}}/* * release a substream */static void release_substream_urbs(snd_usb_substream_t *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]);	if (subs->tmpbuf) {		kfree(subs->tmpbuf);		subs->tmpbuf = NULL;	}	subs->nurbs = 0;

⌨️ 快捷键说明

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