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

📄 vwsnd.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 5 页
字号:
			     wport->sample_size);		ad1843_setup_dac(&devc->lith,				 wport->sw_framerate,				 wport->sw_samplefmt,				 wport->sw_channels);		li_enable_interrupts(&devc->lith, WRITE_INTR_MASK);	}	DBGRV();	return 0;}/* * pcm_shutdown_port - shut down one port (direction) for PCM I/O. * Only called from pcm_shutdown. */static void pcm_shutdown_port(vwsnd_dev_t *devc,			      vwsnd_port_t *aport,			      unsigned int mask){	unsigned long flags;	vwsnd_port_hwstate_t hwstate;	DECLARE_WAITQUEUE(wait, current);	aport->swstate = SW_INITIAL;	add_wait_queue(&aport->queue, &wait);	while (1) {		set_current_state(TASK_UNINTERRUPTIBLE);		spin_lock_irqsave(&aport->lock, flags);		{			hwstate = aport->hwstate;		}				spin_unlock_irqrestore(&aport->lock, flags);		if (hwstate == HW_STOPPED)			break;		schedule();	}	current->state = TASK_RUNNING;	remove_wait_queue(&aport->queue, &wait);	li_disable_interrupts(&devc->lith, mask);	if (aport == &devc->rport)		ad1843_shutdown_adc(&devc->lith);	else /* aport == &devc->wport) */		ad1843_shutdown_dac(&devc->lith);	li_shutdown_dma(&aport->chan);	vfree(aport->swbuf);	aport->swbuf = NULL;	aport->byte_count = 0;}/* * pcm_shutdown undoes what pcm_setup did. * Also sets the ports' swstate to newstate. */static void pcm_shutdown(vwsnd_dev_t *devc,			 vwsnd_port_t *rport,			 vwsnd_port_t *wport){	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);	if (rport && rport->swbuf) {		DBGPV("shutting down rport\n");		pcm_shutdown_port(devc, rport, READ_INTR_MASK);	}	if (wport && wport->swbuf) {		DBGPV("shutting down wport\n");		pcm_shutdown_port(devc, wport, WRITE_INTR_MASK);	}	DBGRV();}static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb){	char *src = rport->hwbuf + hwidx;	char *dst = rport->swbuf + swidx;	int fmt = rport->sw_samplefmt;	DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx);	ASSERT(rport->hwbuf != NULL);	ASSERT(rport->swbuf != NULL);	ASSERT(nb > 0 && (nb % 32) == 0);	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);	ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size);	ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size);	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {		/* See Sample Format Notes above. */		char *end = src + nb;		while (src < end)			*dst++ = *src++ ^ 0x80;	} else		memcpy(dst, src, nb);}static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb){	char *src = wport->swbuf + swidx;	char *dst = wport->hwbuf + hwidx;	int fmt = wport->sw_samplefmt;	ASSERT(nb > 0 && (nb % 32) == 0);	ASSERT(wport->hwbuf != NULL);	ASSERT(wport->swbuf != NULL);	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);	ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size);	ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size);	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {		/* See Sample Format Notes above. */		char *end = src + nb;		while (src < end)			*dst++ = *src++ ^ 0x80;	} else		memcpy(dst, src, nb);}/* * pcm_output() is called both from baselevel and from interrupt level. * This is where audio frames are copied into the hardware-accessible * ring buffer. * * Locking note: The part of this routine that figures out what to do * holds wport->lock.  The longer part releases wport->lock, but sets * wport->flags & HW_BUSY.  Afterward, it reacquires wport->lock, and * checks for more work to do. * * If another thread calls pcm_output() while HW_BUSY is set, it * returns immediately, knowing that the thread that set HW_BUSY will * look for more work to do before returning. * * This has the advantage that port->lock is held for several short * periods instead of one long period.  Also, when pcm_output is * called from base level, it reenables interrupts. */static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb){	vwsnd_port_t *wport = &devc->wport;	const int hwmax  = wport->hwbuf_max;	const int hwsize = wport->hwbuf_size;	const int swsize = wport->swbuf_size;	const int fragsize = wport->hw_fragsize;	unsigned long iflags;	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);	spin_lock_irqsave(&wport->lock, iflags);	if (erflown)		wport->flags |= ERFLOWN;	(void) __swb_inc_u(wport, nb);	if (wport->flags & HW_BUSY) {		spin_unlock_irqrestore(&wport->lock, iflags);		DBGPV("returning: HW BUSY\n");		return;	}	if (wport->flags & DISABLED) {		spin_unlock_irqrestore(&wport->lock, iflags);		DBGPV("returning: DISABLED\n");		return;	}	wport->flags |= HW_BUSY;	while (1) {		int swptr, hwptr, hw_avail, sw_avail, swidx;		vwsnd_port_hwstate_t hwstate = wport->hwstate;		vwsnd_port_swstate_t swstate = wport->swstate;		int hw_unavail;		ustmsc_t ustmsc;		hwptr = li_read_hwptr(&wport->chan);		swptr = li_read_swptr(&wport->chan);		hw_unavail = (swptr - hwptr + hwsize) % hwsize;		hw_avail = (hwmax - hw_unavail) & -fragsize;		sw_avail = wport->swb_i_avail & -fragsize;		if (sw_avail && swstate == SW_RUN) {			if (wport->flags & ERFLOWN) {				wport->flags &= ~ERFLOWN;			}		} else if (swstate == SW_INITIAL ||			 swstate == SW_OFF ||			 (swstate == SW_DRAIN &&			  !sw_avail &&			  (wport->flags & ERFLOWN))) {			DBGP("stopping.  hwstate = %d\n", hwstate);			if (hwstate != HW_STOPPED) {				li_deactivate_dma(&wport->chan);				wport->hwstate = HW_STOPPED;			}			wake_up(&wport->queue);			break;		}		if (!sw_avail || !hw_avail)			break;		spin_unlock_irqrestore(&wport->lock, iflags);		/*		 * We gave up the port lock, but we have the HW_BUSY flag.		 * Proceed without accessing any nonlocal state.		 * Do not exit the loop -- must check for more work.		 */		swidx = wport->swb_i_idx;		nb = hw_avail;		if (nb > sw_avail)			nb = sw_avail;		if (nb > hwsize - swptr)			nb = hwsize - swptr; /* don't overflow hwbuf */		if (nb > swsize - swidx)			nb = swsize - swidx; /* don't overflow swbuf */		ASSERT(nb > 0);		if (nb % fragsize) {			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);			DBGP("hw_avail = %d\n", hw_avail);			DBGP("sw_avail = %d\n", sw_avail);			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);		}		ASSERT(!(nb % fragsize));		DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n",		      swidx, swidx + nb, swptr, swptr + nb);		pcm_copy_out(wport, swidx, swptr, nb);		li_write_swptr(&wport->chan, (swptr + nb) % hwsize);		spin_lock_irqsave(&wport->lock, iflags);		if (hwstate == HW_STOPPED) {			DBGPV("starting\n");			li_activate_dma(&wport->chan);			wport->hwstate = HW_RUNNING;			li_read_USTMSC(&wport->chan, &ustmsc);			ASSERT(wport->byte_count % wport->frame_size == 0);			wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size;		}		__swb_inc_i(wport, nb);		wport->byte_count += nb;		wport->frag_count += nb / fragsize;		ASSERT(nb % fragsize == 0);		wake_up(&wport->queue);	}	wport->flags &= ~HW_BUSY;	spin_unlock_irqrestore(&wport->lock, iflags);	DBGRV();}/* * pcm_input() is called both from baselevel and from interrupt level. * This is where audio frames are copied out of the hardware-accessible * ring buffer. * * Locking note: The part of this routine that figures out what to do * holds rport->lock.  The longer part releases rport->lock, but sets * rport->flags & HW_BUSY.  Afterward, it reacquires rport->lock, and * checks for more work to do. * * If another thread calls pcm_input() while HW_BUSY is set, it * returns immediately, knowing that the thread that set HW_BUSY will * look for more work to do before returning. * * This has the advantage that port->lock is held for several short * periods instead of one long period.  Also, when pcm_input is * called from base level, it reenables interrupts. */static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb){	vwsnd_port_t *rport = &devc->rport;	const int hwmax  = rport->hwbuf_max;	const int hwsize = rport->hwbuf_size;	const int swsize = rport->swbuf_size;	const int fragsize = rport->hw_fragsize;	unsigned long iflags;	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);	spin_lock_irqsave(&rport->lock, iflags);	if (erflown)		rport->flags |= ERFLOWN;	(void) __swb_inc_u(rport, nb);	if (rport->flags & HW_BUSY || !rport->swbuf) {		spin_unlock_irqrestore(&rport->lock, iflags);		DBGPV("returning: HW BUSY or !swbuf\n");		return;	}	if (rport->flags & DISABLED) {		spin_unlock_irqrestore(&rport->lock, iflags);		DBGPV("returning: DISABLED\n");		return;	}	rport->flags |= HW_BUSY;	while (1) {		int swptr, hwptr, hw_avail, sw_avail, swidx;		vwsnd_port_hwstate_t hwstate = rport->hwstate;		vwsnd_port_swstate_t swstate = rport->swstate;		hwptr = li_read_hwptr(&rport->chan);		swptr = li_read_swptr(&rport->chan);		hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize;		if (hw_avail > hwmax)			hw_avail = hwmax;		sw_avail = rport->swb_i_avail & -fragsize;		if (swstate != SW_RUN) {			DBGP("stopping.  hwstate = %d\n", hwstate);			if (hwstate != HW_STOPPED) {				li_deactivate_dma(&rport->chan);				rport->hwstate = HW_STOPPED;			}			wake_up(&rport->queue);			break;		}		if (!sw_avail || !hw_avail)			break;		spin_unlock_irqrestore(&rport->lock, iflags);		/*		 * We gave up the port lock, but we have the HW_BUSY flag.		 * Proceed without accessing any nonlocal state.		 * Do not exit the loop -- must check for more work.		 */		swidx = rport->swb_i_idx;		nb = hw_avail;		if (nb > sw_avail)			nb = sw_avail;		if (nb > hwsize - swptr)			nb = hwsize - swptr; /* don't overflow hwbuf */		if (nb > swsize - swidx)			nb = swsize - swidx; /* don't overflow swbuf */		ASSERT(nb > 0);		if (nb % fragsize) {			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);			DBGP("hw_avail = %d\n", hw_avail);			DBGP("sw_avail = %d\n", sw_avail);			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);		}		ASSERT(!(nb % fragsize));		DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n",		      swptr, swptr + nb, swidx, swidx + nb);		pcm_copy_in(rport, swidx, swptr, nb);		li_write_swptr(&rport->chan, (swptr + nb) % hwsize);		spin_lock_irqsave(&rport->lock, iflags);		__swb_inc_i(rport, nb);		rport->byte_count += nb;		rport->frag_count += nb / fragsize;		ASSERT(nb % fragsize == 0);		wake_up(&rport->queue);	}	rport->flags &= ~HW_BUSY;	spin_unlock_irqrestore(&rport->lock, iflags);	DBGRV();}/* * pcm_flush_frag() writes zero samples to fill the current fragment, * then flushes it to the hardware. * * It is only meaningful to flush output, not input. */static void pcm_flush_frag(vwsnd_dev_t *devc){	vwsnd_port_t *wport = &devc->wport;	DBGPV("swstate = %d\n", wport->swstate);	if (wport->swstate == SW_RUN) {		int idx = wport->swb_u_idx;		int end = (idx + wport->hw_fragsize - 1)			>> wport->hw_fragshift			<< wport->hw_fragshift;		int nb = end - idx;		DBGPV("clearing %d bytes\n", nb);		if (nb)			memset(wport->swbuf + idx,			       (char) wport->zero_word,			       nb);		wport->swstate = SW_DRAIN;		pcm_output(devc, 0, nb);	}	DBGRV();}/* * Wait for output to drain.  This sleeps uninterruptibly because * there is nothing intelligent we can do if interrupted.  This * means the process will be delayed in responding to the signal. */static void pcm_write_sync(vwsnd_dev_t *devc){	vwsnd_port_t *wport = &devc->wport;	DECLARE_WAITQUEUE(wait, current);	unsigned long flags;	vwsnd_port_hwstate_t hwstate;	DBGEV("(devc=0x%p)\n", devc);	add_wait_queue(&wport->queue, &wait);	while (1) {		set_current_state(TASK_UNINTERRUPTIBLE);		spin_lock_irqsave(&wport->lock, flags);		{			hwstate = wport->hwstate;		}		spin_unlock_irqrestore(&wport->lock, flags);		if (hwstate == HW_STOPPED)			break;		schedule();	}	current->state = TASK_RUNNING;	remove_wait_queue(&wport->queue, &wait);	DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate);	DBGRV();}/*****************************************************************************//* audio driver *//* * seek on an audio device always fails. */static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status){	int overflown = status & LI_INTR_COMM1_OVERFLOW;	if (status & READ_INTR_MASK)		pcm_input(devc, overflown, 0);}static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status){	int underflown = status & LI_INTR_COMM2_UNDERFLOW;	if (status & WRITE_INTR_MASK)		pcm_output(devc, underflown, 0);}static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs){	vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id;	unsigned int status;	DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs);	status = li_get_clear_intr_status(&devc->lith);	vwsnd_audio_read_intr(devc, status);	vwsnd_audio_write_intr(devc, status);}static ssize_t vwsnd_audio_do_read(struct file *file,				   char *buffe

⌨️ 快捷键说明

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