📄 via82cxxx_audio.c
字号:
printk (KERN_ERR PFX "insane IRQ %d, aborting\n", card->pdev->irq); DPRINTK ("EXIT, returning -EIO\n"); return -EIO; } if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, 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; } /* we don't want interrupts until we're opened */ via_interrupt_disable (card); DPRINTK ("EXIT, returning 0\n"); return 0;}/** * via_interrupt_cleanup - Shutdown driver interrupt handling * @card: Private info for specified board * * Disable any potential interrupt sources in the Via audio * hardware, and then release (un-reserve) the IRQ line * in the kernel core. */static void via_interrupt_cleanup (struct via_info *card){ DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->pdev != NULL); via_interrupt_disable (card); free_irq (card->pdev->irq, card); DPRINTK ("EXIT\n");}/**************************************************************** * * OSS DSP device * */static 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: via_llseek, ioctl: via_dsp_ioctl,#ifdef VIA_SUPPORT_MMAP mmap: via_dsp_mmap,#endif};static int __init via_dsp_init (struct via_info *card){ u8 tmp8; DPRINTK ("ENTER\n"); assert (card != NULL); /* 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_MIDI_ENABLE | VIA_CR42_FM_ENABLE)) { tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_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");}#ifdef VIA_SUPPORT_MMAPstatic struct page * via_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access){ struct via_info *card = vma->vm_private_data; struct via_channel *chan = &card->ch_out; struct page *dmapage; unsigned long pgoff; int rd, wr; DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n", vma->vm_start, address - vma->vm_start, (address - vma->vm_start) >> PAGE_SHIFT, address, write_access); assert (VIA_DMA_BUF_SIZE == PAGE_SIZE); if (address > vma->vm_end) { DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n"); return NOPAGE_SIGBUS; /* Disallow mremap */ } if (!card) { DPRINTK ("EXIT, returning NOPAGE_OOM\n"); return NOPAGE_OOM; /* Nothing allocated */ } pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); rd = card->ch_in.is_mapped; wr = card->ch_out.is_mapped;#ifndef VIA_NDEBUG { unsigned long max_bufs = VIA_DMA_BUFFERS; if (rd && wr) max_bufs *= 2; /* via_dsp_mmap() should ensure this */ assert (pgoff < max_bufs); }#endif /* if full-duplex (read+write) and we have two sets of bufs, * then the playback buffers come first, sez soundcard.c */ if (pgoff >= VIA_DMA_BUFFERS) { pgoff -= VIA_DMA_BUFFERS; chan = &card->ch_in; } else if (!wr) chan = &card->ch_in; assert ((((unsigned long)chan->sgbuf[pgoff].cpuaddr) % PAGE_SIZE) == 0); dmapage = virt_to_page (chan->sgbuf[pgoff].cpuaddr); DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n", dmapage, (unsigned long) chan->sgbuf[pgoff].cpuaddr); get_page (dmapage); return dmapage;}#ifndef VM_RESERVEDstatic int via_mm_swapout (struct page *page, struct file *filp){ return 0;}#endif /* VM_RESERVED */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); assert (VIA_DMA_BUF_SIZE == PAGE_SIZE); max_size = 0; if (file->f_mode & FMODE_READ) { rd = 1; max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE); } if (file->f_mode & FMODE_WRITE) { wr = 1; max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE); } 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; up (&card->syscall_sem); rc = 0;out: DPRINTK("EXIT, returning %d\n", rc); return rc;}#endif /* VIA_SUPPORT_MMAP */static ssize_t via_dsp_do_read (struct via_info *card, char *userbuf, size_t count, int nonblock){ const char *orig_userbuf = userbuf; struct via_channel *chan = &card->ch_in; size_t size; int n, tmp; /* if SGD has not yet been started, start it */ via_chan_maybe_start (chan);handle_one_block: /* just to be a nice neighbor */ if (current->need_resched) schedule (); /* 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_bufs represents the number of buffers waiting * to be copied to userland. sleep until at least * one buffer has been read from the audio hardware. */ tmp = atomic_read (&chan->n_bufs); assert (tmp >= 0); assert (tmp <= VIA_DMA_BUFFERS); while (tmp == 0) { if (nonblock || !chan->is_active) return -EAGAIN; DPRINTK ("Sleeping on block %d\n", n); interruptible_sleep_on (&chan->wait); if (signal_pending (current)) return -ERESTARTSYS; tmp = atomic_read (&chan->n_bufs); } /* 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 < VIA_DMA_BUF_SIZE)) { size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len; size = (count < slop_left) ? count : slop_left; if (copy_to_user (userbuf, chan->sgbuf[n].cpuaddr + chan->slop_len, size)) return -EFAULT; count -= size; chan->slop_len += size; userbuf += size; } /* If we didn't copy the buffer completely to userspace, * stop now. */ if (chan->slop_len < VIA_DMA_BUF_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 == (VIA_DMA_BUFFERS - 1)) chan->sw_ptr = 0; else chan->sw_ptr++; /* mark one less buffer waiting to be processed */ assert (atomic_read (&chan->n_bufs) > 0); atomic_dec (&chan->n_bufs); /* we are at a block boundary, there is no fragment data */ chan->slop_len = 0; DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n", n, chan->sw_ptr, atomic_read (&chan->n_bufs)); 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;}static ssize_t via_dsp_read(struct file *file, char *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); assert (buffer != NULL); card = file->private_data; assert (card != NULL); if (ppos != &file->f_pos) { DPRINTK ("EXIT, returning -ESPIPE\n"); return -ESPIPE; } rc = via_syscall_down (card, nonblock); if (rc) goto out; if (card->ch_in.is_mapped) { rc = -ENXIO; goto out_up; } rc = via_dsp_do_read (card, buffer, count, nonblock);out_up: up (&card->syscall_sem);out: DPRINTK("EXIT, returning %ld\n",(long) rc); return rc;}static ssize_t via_dsp_do_write (struct via_info *card, const char *userbuf, size_t count, int nonblock){ const char *orig_userbuf = userbuf; struct via_channel *chan = &card->ch_out; volatile struct via_sgd_table *sgtable = chan->sgtable; size_t size; int n, tmp;handle_one_block: /* just to be a nice neighbor */ if (current->need_resched) schedule (); /* grab current channel software pointer. In the case of * playback, this is pointing to the next buffer that * should receive data from userland. */ n = chan->sw_ptr; /* n_bufs represents the number of buffers remaining * to be filled by userspace. Sleep until * at least one buffer is available for our use. */ tmp = atomic_read (&chan->n_bufs); assert (tmp >= 0); assert (tmp <= VIA_DMA_BUFFERS); while (tmp == 0) { if (nonblock || !chan->is_enabled) return -EAGAIN; DPRINTK ("Sleeping on block %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record); interruptible_sleep_on (&chan->wait); if (signal_pending (current)) return -ERESTARTSYS; tmp = atomic_read (&chan->n_bufs); } /* Now that we have a buffer we can write to, fill it up * as much as possible with data from userspace. */ while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) { size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len; size = (count < slop_left) ? count : slop_left; if (copy_from_user (chan->sgbuf[n].cpuaddr + chan->slop_len, userbuf, size)) return -EFAULT; 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 < VIA_DMA_BUF_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 == (VIA_DMA_BUFFERS - 1)) sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL); else sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG); /* advance channel software pointer to point to * the next buffer we will fill with data */ if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1)) chan->sw_ptr = 0; else chan->sw_ptr++; /* mark one less buffer as being available for userspace consumption */ assert (atomic_read (&chan->n_bufs) > 0); atomic_dec (&chan->n_bufs); /* we are at a block boundary, there is no fragment data */ chan->slop_len = 0; /* if SGD has not yet been started, start it */ via_chan_maybe_start (chan); DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n", n, chan->sw_ptr, atomic_read (&chan->n_bufs)); 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;}static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ struct via_info *card; ssize_t rc; int nonblock = (file->f_flags & O_NONBLOCK); DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); assert (file != NULL); assert (buffer != NULL); card = file->private_data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -