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

📄 i810_audio.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
{//	u16 w;//	struct i810_card *card = state->card;//	struct dmabuf *dmabuf = &state->dmabuf;//	struct i810_channel *channel = dmabuf->channel;	/* Enable AC-97 ADC (capture) *///	if (dmabuf->fmt & I810_FMT_16BIT) {//	if (dmabuf->fmt & I810_FMT_STEREO)}/* get current playback/recording dma buffer pointer (byte offset from LBA),   called with spinlock held! */   extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned int civ, offset;	struct i810_channel *c = dmabuf->channel;		if (!dmabuf->enable)		return 0;	do {		civ = inb(state->card->iobase+c->port+OFF_CIV);		offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -			      2 * inw(state->card->iobase+c->port+OFF_PICB);		/* CIV changed before we read PICB (very seldom) ?		 * then PICB was rubbish, so try again */	} while (civ != inb(state->card->iobase+c->port+OFF_CIV));		 	return offset;}static void resync_dma_ptrs(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_channel *c = dmabuf->channel;	int offset;		offset = inb(state->card->iobase+c->port+OFF_CIV);	offset *= (dmabuf->dmasize/SG_LEN);		dmabuf->hwptr=dmabuf->swptr = offset;}	/* Stop recording (lock held) */extern __inline__ void __stop_adc(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_card *card = state->card;	dmabuf->enable &= ~ADC_RUNNING;	outb(0, card->iobase + PI_CR);}static void stop_adc(struct i810_state *state){	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	__stop_adc(state);	spin_unlock_irqrestore(&card->lock, flags);}static void start_adc(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) {		dmabuf->enable |= ADC_RUNNING;		outb((1<<4) | 1<<2 | 1, card->iobase + PI_CR);	}	spin_unlock_irqrestore(&card->lock, flags);}/* stop playback (lock held) */extern __inline__ void __stop_dac(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_card *card = state->card;	dmabuf->enable &= ~DAC_RUNNING;	outb(0, card->iobase + PO_CR);}static void stop_dac(struct i810_state *state){	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	__stop_dac(state);	spin_unlock_irqrestore(&card->lock, flags);}	static void start_dac(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {		if(!(dmabuf->enable&DAC_RUNNING))		{			dmabuf->enable |= DAC_RUNNING;			outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR);		}	}	spin_unlock_irqrestore(&card->lock, flags);}#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)#define DMABUF_MINORDER 1/* allocate DMA buffer, playback and recording buffer should be allocated seperately */static int alloc_dmabuf(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	void *rawbuf;	int order;	struct page *page, *pend;	/* alloc as big a chunk as we can, FIXME: is this necessary ?? */	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)		if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,						   PAGE_SIZE << order,						   &dmabuf->dma_handle)))			break;	if (!rawbuf)		return -ENOMEM;#ifdef DEBUG	printk("i810_audio: allocated %ld (order = %d) bytes at %p\n",	       PAGE_SIZE << order, order, rawbuf);#endif	dmabuf->ready  = dmabuf->mapped = 0;	dmabuf->rawbuf = rawbuf;	dmabuf->buforder = order;		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */	pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);	for (page = virt_to_page(rawbuf); page <= pend; page++)		mem_map_reserve(page);	return 0;}/* free DMA buffer */static void dealloc_dmabuf(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct page *page, *pend;	if (dmabuf->rawbuf) {		/* undo marking the pages as reserved */		pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);		for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)			mem_map_unreserve(page);		pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,				    dmabuf->rawbuf, dmabuf->dma_handle);	}	dmabuf->rawbuf = NULL;	dmabuf->mapped = dmabuf->ready = 0;}static int prog_dmabuf(struct i810_state *state, unsigned rec){	struct dmabuf *dmabuf = &state->dmabuf;	struct sg_item *sg;	unsigned bytepersec;	unsigned bufsize;	unsigned long flags;	int ret;	unsigned fragsize;	int i;	spin_lock_irqsave(&state->card->lock, flags);	resync_dma_ptrs(state);	dmabuf->total_bytes = 0;	dmabuf->count = dmabuf->error = 0;	spin_unlock_irqrestore(&state->card->lock, flags);	/* allocate DMA buffer if not allocated yet */	if (!dmabuf->rawbuf)		if ((ret = alloc_dmabuf(state)))			return ret;	/* FIXME: figure out all this OSS fragment stuff */	bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];	bufsize = PAGE_SIZE << dmabuf->buforder;	if (dmabuf->ossfragshift) {		if ((1000 << dmabuf->ossfragshift) < bytepersec)			dmabuf->fragshift = ld2(bytepersec/1000);		else			dmabuf->fragshift = dmabuf->ossfragshift;	} else {		/* lets hand out reasonable big ass buffers by default */		dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);	}	dmabuf->numfrag = bufsize >> dmabuf->fragshift;	while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {		dmabuf->fragshift--;		dmabuf->numfrag = bufsize >> dmabuf->fragshift;	}	dmabuf->fragsize = 1 << dmabuf->fragshift;	if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)		dmabuf->numfrag = dmabuf->ossmaxfrags;	dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];	dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;	memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80,	       dmabuf->dmasize);	/*	 *	Now set up the ring 	 */	sg=&dmabuf->channel->sg[0];	fragsize = bufsize / SG_LEN;		/*	 *	Load up 32 sg entries and take an interrupt at half	 *	way (we might want more interrupts later..) 	 */	  	for(i=0;i<32;i++)	{		sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i);		sg->control=(fragsize>>1);		sg->control|=CON_IOC;		sg++;	}	spin_lock_irqsave(&state->card->lock, flags);	outb(2, state->card->iobase+dmabuf->channel->port+OFF_CR);   /* reset DMA machine */	outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR);	outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI);	outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV);	if (rec) {		i810_rec_setup(state);	} else {		i810_play_setup(state);	}	spin_unlock_irqrestore(&state->card->lock, flags);	/* set the ready flag for the dma buffer */	dmabuf->ready = 1;#ifdef DEBUG	printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, "	       "fragsize = %d dmasize = %d\n",	       dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,	       dmabuf->fragsize, dmabuf->dmasize);#endif	return 0;}/* * Clear the rest of the last i810 dma buffer, normally there is no rest * because the OSS fragment size is the same as the size of this buffer. */static void i810_clear_tail(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned swptr;	unsigned char silence = (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80;	unsigned int len;	unsigned long flags;	spin_lock_irqsave(&state->card->lock, flags);	swptr = dmabuf->swptr;	spin_unlock_irqrestore(&state->card->lock, flags);	if(dmabuf->dmasize)		len = swptr % (dmabuf->dmasize/SG_LEN);	else		len = 0;		memset(dmabuf->rawbuf + swptr, silence, len);	spin_lock_irqsave(&state->card->lock, flags);	dmabuf->swptr += len;	dmabuf->count += len;	spin_unlock_irqrestore(&state->card->lock, flags);	/* restart the dma machine in case it is halted */	start_dac(state);}static int drain_dac(struct i810_state *state, int nonblock){	DECLARE_WAITQUEUE(wait, current);	struct dmabuf *dmabuf = &state->dmabuf;	unsigned long flags;	unsigned long tmo;	int count;	if (dmabuf->mapped || !dmabuf->ready)		return 0;	add_wait_queue(&dmabuf->wait, &wait);	for (;;) {		/* It seems that we have to set the current state to TASK_INTERRUPTIBLE		   every time to make the process really go to sleep */		current->state = TASK_INTERRUPTIBLE;		spin_lock_irqsave(&state->card->lock, flags);		count = dmabuf->count;		spin_unlock_irqrestore(&state->card->lock, flags);		if (count <= 0)			break;		if (signal_pending(current))			break;		if (nonblock) {			remove_wait_queue(&dmabuf->wait, &wait);			current->state = TASK_RUNNING;			return -EBUSY;		}		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;		tmo >>= sample_shift[dmabuf->fmt];		if (!schedule_timeout(tmo ? tmo : 1) && tmo){			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");			break;		}	}	remove_wait_queue(&dmabuf->wait, &wait);	current->state = TASK_RUNNING;	if (signal_pending(current))		return -ERESTARTSYS;	return 0;}/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */static void i810_update_ptr(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned hwptr, swptr;	int clear_cnt = 0;	int diff;	unsigned char silence;//	unsigned half_dmasize;	/* update hardware pointer */	hwptr = i810_get_dma_addr(state);	diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;//	printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);	dmabuf->hwptr = hwptr;	dmabuf->total_bytes += diff;	/* error handling and process wake up for DAC */	if (dmabuf->enable == ADC_RUNNING) {		if (dmabuf->mapped) {			dmabuf->count -= diff;			if (dmabuf->count >= (signed)dmabuf->fragsize)				wake_up(&dmabuf->wait);		} else {			dmabuf->count += diff;			if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {				/* buffer underrun or buffer overrun, we have no way to recover				   it here, just stop the machine and let the process force hwptr				   and swptr to sync */				__stop_adc(state);				dmabuf->error++;			}			else if (!dmabuf->endcleared) {				swptr = dmabuf->swptr;				silence = (dmabuf->fmt & I810_FMT_16BIT ? 0 : 0x80);				if (dmabuf->count < (signed) dmabuf->fragsize) 				{					clear_cnt = dmabuf->fragsize;					if ((swptr + clear_cnt) > dmabuf->dmasize)						clear_cnt = dmabuf->dmasize - swptr;					memset (dmabuf->rawbuf + swptr, silence, clear_cnt);					dmabuf->endcleared = 1;				}			}						wake_up(&dmabuf->wait);		}	}	/* error handling and process wake up for DAC */	if (dmabuf->enable == DAC_RUNNING) {		if (dmabuf->mapped) {			dmabuf->count += diff;			if (dmabuf->count >= (signed)dmabuf->fragsize)				wake_up(&dmabuf->wait);		} else {			dmabuf->count -= diff;			if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {				/* buffer underrun or buffer overrun, we have no way to recover				   it here, just stop the machine and let the process force hwptr				   and swptr to sync */				__stop_dac(state);				printk("DMA overrun on send\n");				dmabuf->error++;			}			wake_up(&dmabuf->wait);		}	}}static void i810_channel_interrupt(struct i810_card *card){	int i;//	printk("CHANNEL IRQ .. ");		for(i=0;i<NR_HW_CH;i++)	{		struct i810_state *state = card->states[i];		struct i810_channel *c;		unsigned long port = card->iobase;		u16 status;				if(!state)			continue;		if(!state->dmabuf.ready)			continue;		c=state->dmabuf.channel;				port+=c->port;		//		printk("PORT %lX (", port);				status = inw(port + OFF_SR);		//		printk("ST%d ", status);				if(status & DMA_INT_LVI)		{			/* Back to the start *///			printk("LVI - STOP");			outb((inb(port+OFF_CIV)-1)&31, port+OFF_LVI);			i810_update_ptr(state);			outb(0, port + OFF_CR);		}		if(status & DMA_INT_COMPLETE)		{			int x;			/* Keep the card chasing its tail */			outb(x=((inb(port+OFF_CIV)-1)&31), port+OFF_LVI);			i810_update_ptr(state);//			printk("COMP%d ",x);		}//		printk(")");		outw(status & DMA_INT_MASK, port + OFF_SR);	}//	printk("\n");}static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct i810_card *card = (struct i810_card *)dev_id;	u32 status;	spin_lock(&card->lock);	status = inl(card->iobase + GLOB_STA);	if(!(status & INT_MASK)) 	{		spin_unlock(&card->lock);		return;  /* not for us */	}//	printk("Interrupt %X: ", status);	if(status & (INT_PO|INT_PI|INT_MC))		i810_channel_interrupt(card); 	/* clear 'em */	outl(status & INT_MASK, card->iobase + GLOB_STA);	spin_unlock(&card->lock);}static loff_t i810_llseek(struct file *file, loff_t offset, int origin){	return -ESPIPE;}

⌨️ 快捷键说明

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