via82cxxx_audio.c

来自「linux 内核源代码」· C语言 代码 · 共 2,515 行 · 第 1/5 页

C
2,515
字号
			tmp8 |= VIA_CR48_FM_TRAP_TO_NMI;			pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8);		}		if (request_irq (card->pdev->irq, via_interrupt, IRQF_SHARED, VIA_MODULE_NAME, card)) {			printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",				card->pdev->irq);			DPRINTK ("EXIT, returning -EBUSY\n");			return -EBUSY;		}	}	else 	{		if (request_irq (card->pdev->irq, via_new_interrupt, IRQF_SHARED, VIA_MODULE_NAME, card)) {			printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",				card->pdev->irq);			DPRINTK ("EXIT, returning -EBUSY\n");			return -EBUSY;		}	}	DPRINTK ("EXIT, returning 0\n");	return 0;}/**************************************************************** * * OSS DSP device * */static const struct file_operations via_dsp_fops = {	.owner		= THIS_MODULE,	.open		= via_dsp_open,	.release	= via_dsp_release,	.read		= via_dsp_read,	.write		= via_dsp_write,	.poll		= via_dsp_poll,	.llseek		= no_llseek,	.ioctl		= via_dsp_ioctl,	.mmap		= via_dsp_mmap,};static int __devinit via_dsp_init (struct via_info *card){	u8 tmp8;	DPRINTK ("ENTER\n");	assert (card != NULL);	if(card->legacy)	{		/* turn off legacy features, if not already */		pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8);		if (tmp8 & (VIA_CR42_SB_ENABLE |  VIA_CR42_FM_ENABLE)) {			tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE);			pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);		}	}	via_stop_everything (card);	card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1);	if (card->dev_dsp < 0) {		DPRINTK ("EXIT, returning -ENODEV\n");		return -ENODEV;	}	DPRINTK ("EXIT, returning 0\n");	return 0;}static void via_dsp_cleanup (struct via_info *card){	DPRINTK ("ENTER\n");	assert (card != NULL);	assert (card->dev_dsp >= 0);	via_stop_everything (card);	unregister_sound_dsp (card->dev_dsp);	DPRINTK ("EXIT\n");}static struct page * via_mm_nopage (struct vm_area_struct * vma,				    unsigned long address, int *type){	struct via_info *card = vma->vm_private_data;	struct via_channel *chan = &card->ch_out;	unsigned long max_bufs;	struct page *dmapage;	unsigned long pgoff;	int rd, wr;	DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n",		 vma->vm_start,		 address - vma->vm_start,		 (address - vma->vm_start) >> PAGE_SHIFT,		 address);        if (address > vma->vm_end) {		DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");		return NOPAGE_SIGBUS; /* Disallow mremap */	}        if (!card) {		DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");		return NOPAGE_SIGBUS;	/* Nothing allocated */	}	pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT);	rd = card->ch_in.is_mapped;	wr = card->ch_out.is_mapped;	max_bufs = chan->frag_number;	if (rd && wr)		max_bufs *= 2;	if (pgoff >= max_bufs)		return NOPAGE_SIGBUS;	/* if full-duplex (read+write) and we have two sets of bufs,	 * then the playback buffers come first, sez soundcard.c */	if (pgoff >= chan->page_number) {		pgoff -= chan->page_number;		chan = &card->ch_in;	} else if (!wr)		chan = &card->ch_in;	assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0);	dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr);	DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n",		 dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr);	get_page (dmapage);	if (type)		*type = VM_FAULT_MINOR;	return dmapage;}#ifndef VM_RESERVEDstatic int via_mm_swapout (struct page *page, struct file *filp){	return 0;}#endif /* VM_RESERVED */static struct vm_operations_struct via_mm_ops = {	.nopage		= via_mm_nopage,#ifndef VM_RESERVED	.swapout	= via_mm_swapout,#endif};static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma){	struct via_info *card;	int nonblock = (file->f_flags & O_NONBLOCK);	int rc = -EINVAL, rd=0, wr=0;	unsigned long max_size, size, start, offset;	assert (file != NULL);	assert (vma != NULL);	card = file->private_data;	assert (card != NULL);	DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n",		 vma->vm_start,		 vma->vm_end - vma->vm_start,		 vma->vm_pgoff);	max_size = 0;	if (vma->vm_flags & VM_READ) {		rd = 1;		via_chan_set_buffering(card, &card->ch_in, -1);		via_chan_buffer_init (card, &card->ch_in);		max_size += card->ch_in.page_number << PAGE_SHIFT;	}	if (vma->vm_flags & VM_WRITE) {		wr = 1;		via_chan_set_buffering(card, &card->ch_out, -1);		via_chan_buffer_init (card, &card->ch_out);		max_size += card->ch_out.page_number << PAGE_SHIFT;	}	start = vma->vm_start;	offset = (vma->vm_pgoff << PAGE_SHIFT);	size = vma->vm_end - vma->vm_start;	/* some basic size/offset sanity checks */	if (size > max_size)		goto out;	if (offset > max_size - size)		goto out;	rc = via_syscall_down (card, nonblock);	if (rc) goto out;	vma->vm_ops = &via_mm_ops;	vma->vm_private_data = card;#ifdef VM_RESERVED	vma->vm_flags |= VM_RESERVED;#endif	if (rd)		card->ch_in.is_mapped = 1;	if (wr)		card->ch_out.is_mapped = 1;	mutex_unlock(&card->syscall_mutex);	rc = 0;out:	DPRINTK ("EXIT, returning %d\n", rc);	return rc;}static ssize_t via_dsp_do_read (struct via_info *card,				char __user *userbuf, size_t count,				int nonblock){        DECLARE_WAITQUEUE(wait, current);	const char __user *orig_userbuf = userbuf;	struct via_channel *chan = &card->ch_in;	size_t size;	int n, tmp;	ssize_t ret = 0;	/* if SGD has not yet been started, start it */	via_chan_maybe_start (chan);handle_one_block:	/* just to be a nice neighbor */	/* Thomas Sailer:	 * But also to ourselves, release semaphore if we do so */	if (need_resched()) {		mutex_unlock(&card->syscall_mutex);		schedule ();		ret = via_syscall_down (card, nonblock);		if (ret)			goto out;	}	/* grab current channel software pointer.  In the case of	 * recording, this is pointing to the next buffer that	 * will receive data from the audio hardware.	 */	n = chan->sw_ptr;	/* n_frags represents the number of fragments waiting	 * to be copied to userland.  sleep until at least	 * one buffer has been read from the audio hardware.	 */	add_wait_queue(&chan->wait, &wait);	for (;;) {		__set_current_state(TASK_INTERRUPTIBLE);		tmp = atomic_read (&chan->n_frags);		assert (tmp >= 0);		assert (tmp <= chan->frag_number);		if (tmp)			break;		if (nonblock || !chan->is_active) {			ret = -EAGAIN;			break;		}		mutex_unlock(&card->syscall_mutex);		DPRINTK ("Sleeping on block %d\n", n);		schedule();		ret = via_syscall_down (card, nonblock);		if (ret)			break;		if (signal_pending (current)) {			ret = -ERESTARTSYS;			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&chan->wait, &wait);	if (ret)		goto out;	/* Now that we have a buffer we can read from, send	 * as much as sample data possible to userspace.	 */	while ((count > 0) && (chan->slop_len < chan->frag_size)) {		size_t slop_left = chan->frag_size - chan->slop_len;		void *base = chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr;		unsigned ofs = (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size;		size = (count < slop_left) ? count : slop_left;		if (copy_to_user (userbuf,				  base + ofs + chan->slop_len,				  size)) {			ret = -EFAULT;			goto out;		}		count -= size;		chan->slop_len += size;		userbuf += size;	}	/* If we didn't copy the buffer completely to userspace,	 * stop now.	 */	if (chan->slop_len < chan->frag_size)		goto out;	/*	 * If we get to this point, we copied one buffer completely	 * to userspace, give the buffer back to the hardware.	 */	/* advance channel software pointer to point to	 * the next buffer from which we will copy	 */	if (chan->sw_ptr == (chan->frag_number - 1))		chan->sw_ptr = 0;	else		chan->sw_ptr++;	/* mark one less buffer waiting to be processed */	assert (atomic_read (&chan->n_frags) > 0);	atomic_dec (&chan->n_frags);	/* we are at a block boundary, there is no fragment data */	chan->slop_len = 0;	DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",		n, chan->sw_ptr, atomic_read (&chan->n_frags));	DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",		 inb (card->baseaddr + 0x00),		 inb (card->baseaddr + 0x01),		 inb (card->baseaddr + 0x02),		 inl (card->baseaddr + 0x04),		 inl (card->baseaddr + 0x0C),		 inl (card->baseaddr + 0x80),		 inl (card->baseaddr + 0x84));	if (count > 0)		goto handle_one_block;out:	return (userbuf != orig_userbuf) ? (userbuf - orig_userbuf) : ret;}static ssize_t via_dsp_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos){	struct via_info *card;	int nonblock = (file->f_flags & O_NONBLOCK);	int rc;	DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n",		 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);	assert (file != NULL);	card = file->private_data;	assert (card != NULL);	rc = via_syscall_down (card, nonblock);	if (rc) goto out;	if (card->ch_in.is_mapped) {		rc = -ENXIO;		goto out_up;	}	via_chan_set_buffering(card, &card->ch_in, -1);        rc = via_chan_buffer_init (card, &card->ch_in);	if (rc)		goto out_up;	rc = via_dsp_do_read (card, buffer, count, nonblock);out_up:	mutex_unlock(&card->syscall_mutex);out:	DPRINTK ("EXIT, returning %ld\n",(long) rc);	return rc;}static ssize_t via_dsp_do_write (struct via_info *card,				 const char __user *userbuf, size_t count,				 int nonblock){        DECLARE_WAITQUEUE(wait, current);	const char __user *orig_userbuf = userbuf;	struct via_channel *chan = &card->ch_out;	volatile struct via_sgd_table *sgtable = chan->sgtable;	size_t size;	int n, tmp;	ssize_t ret = 0;handle_one_block:	/* just to be a nice neighbor */	/* Thomas Sailer:	 * But also to ourselves, release semaphore if we do so */	if (need_resched()) {		mutex_unlock(&card->syscall_mutex);		schedule ();		ret = via_syscall_down (card, nonblock);		if (ret)			goto out;	}	/* grab current channel fragment pointer.  In the case of	 * playback, this is pointing to the next fragment that	 * should receive data from userland.	 */	n = chan->sw_ptr;	/* n_frags represents the number of fragments remaining	 * to be filled by userspace.  Sleep until	 * at least one fragment is available for our use.	 */	add_wait_queue(&chan->wait, &wait);	for (;;) {		__set_current_state(TASK_INTERRUPTIBLE);		tmp = atomic_read (&chan->n_frags);		assert (tmp >= 0);		assert (tmp <= chan->frag_number);		if (tmp)			break;		if (nonblock || !chan->is_active) {			ret = -EAGAIN;			break;		}		mutex_unlock(&card->syscall_mutex);		DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);		schedule();		ret = via_syscall_down (card, nonblock);		if (ret)			break;		if (signal_pending (current)) {			ret = -ERESTARTSYS;			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&chan->wait, &wait);	if (ret)		goto out;	/* Now that we have at least one fragment we can write to, fill the buffer	 * as much as possible with data from userspace.	 */	while ((count > 0) && (chan->slop_len < chan->frag_size)) {		size_t slop_left = chan->frag_size - chan->slop_len;		size = (count < slop_left) ? count : slop_left;		if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len,				    userbuf, size)) {			ret = -EFAULT;			goto out;		}		count -= size;		chan->slop_len += size;		userbuf += size;	}	/* If we didn't fill up the buffer with data, stop now.         * Put a 'stop' marker in the DMA table too, to tell the         * audio hardware to stop if it gets here.         */	if (chan->slop_len < chan->frag_size) {		sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP);		goto out;	}	/*         * If we get to this point, we have filled a buffer with         * audio data, flush the buffer to audio hardware.         */	/* Record the true size for the audio hardware to notice */        if (n == (chan->frag_number - 1))                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL);        else                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);	/* advance channel software po

⌨️ 快捷键说明

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