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 + -
显示快捷键?