📄 via82cxxx_audio.c
字号:
} } 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); 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 *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; } 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: 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){ DECLARE_WAITQUEUE(wait, current); 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; 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 (current->need_resched) { up(&card->syscall_sem); 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; } up(&card->syscall_sem); 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 pointer to point to * the next buffer we will fill with data */ if (chan->sw_ptr == (chan->frag_number - 1)) chan->sw_ptr = 0; else chan->sw_ptr++; /* mark one less buffer as being available for userspace consumption */ 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; /* if SGD has not yet been started, start it */ via_chan_maybe_start (chan); 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;}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; 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_out.is_mapped) { rc = -ENXIO; goto out_up; } via_chan_set_buffering(card, &card->ch_out, -1); rc = via_chan_buffer_init (card, &card->ch_out); if (rc) goto out_up; rc = via_dsp_do_write (card, buffer, count, nonblock);out_up: up (&card->syscall_sem);out: DPRINTK ("EXIT, returning %ld\n",(long) rc); return rc;}static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait){ struct via_info *card; struct via_channel *chan; unsigned int mask = 0; DPRINTK ("ENTER\n"); assert (file != NULL); card = file->private_data; assert (card != NULL); if (file->f_mode & FMODE_READ) { chan = &card->ch_in; if (sg_active (chan->iobase)) poll_wait(file, &chan->wait, wait); if (atomic_read (&chan->n_frags) > 0) mask |= POLLIN | POLLRDNORM; } if (file->f_mode & FMODE_WRITE) { chan = &card->ch_out; if (sg_active (chan->iobase)) poll_wait(file, &chan->wait, wait); if (atomic_read (&chan->n_frags) > 0) mask |= POLLOUT | POLLWRNORM; } DPRINTK ("EXIT, returning %u\n", mask); return mask;}/** * via_dsp_drain_playback - sleep until all playback samples are flushed * @card: Private info for specified board * @chan: Channel to drain * @nonblock: boolean, non-zero if O_NONBLOCK is set * * Sleeps until all playback has been flushed to the audio * hardware. * * Locking: inside card->syscall_sem */static int via_dsp_drain_playback (struct via_info *card, struct via_channel *chan, int nonblock){ DECLARE_WAITQUEUE(wait, current); int ret = 0; DPRINTK ("ENTER, nonblock = %d\n", nonblock); if (chan->slop_len > 0) via_chan_flush_frag (chan); if (atomic_read (&chan->n_frags) == chan->frag_number) goto out; via_chan_maybe_start (chan); add_wait_queue(&chan->wait, &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); if (atomic_read (&chan->n_frags) >= chan->frag_number) break; if (nonblock) { DPRINTK ("EXIT, returning -EAGAIN\n"); ret = -EAGAIN; break; }#ifdef VIA_DEBUG { u8 r40,r41,r42,r43,r44,r48; pci_read_config_byte (card->pdev, 0x40, &r40); pci_read_config_byte (card->pdev, 0x41, &r41); pci_read_config_byte (card->pdev, 0x42, &r42); pci_read_config_byte (card->pdev, 0x43, &r43); pci_read_config_byte (card->pdev, 0x44, &r44); pci_read_config_byte (card->pdev, 0x48, &r48); DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", r40,r41,r42,r43,r44,r48); 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 (!chan->is_active) printk (KERN_ERR "sleeping but not active\n");#endif up(&card->syscall_sem); DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags)); schedule(); if ((ret = via_syscall_down (card, nonblock))) break; if (signal_pending (current)) { DPRINTK ("EXIT, returning -ERESTARTSYS\n"); ret = -ERESTARTSYS; break; } } set_current_state(TASK_RUNNING); remove_wait_queue(&chan->wait, &wait);#ifdef VIA_DEBUG { u8 r40,r41,r42,r43,r44,r48; pci_read_config_byte (card->pdev, 0x40, &r40); pci_read_config_byte (card->pdev, 0x41, &r41); pci_read_config_byte (card->pdev, 0x42, &r42); pci_read_config_byte (card->pdev, 0x43, &r43); pci_read_config_byte (card->pdev, 0x44, &r44); pci_read_config_byte (card->pdev, 0x48, &r48); DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", r40,r41,r42,r43,r44,r48); 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)); DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags)); }#endifout: DPRINTK ("EXIT, returning %d\n", ret); return ret;}/** * via_dsp_ioctl_space - get information about channel buffering * @card: Private info for specified board * @chan: pointer to channel-specific info * @arg: user buffer for returned information * * Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE. * * Locking: inside card->syscall_sem */static int via_dsp_ioctl_space (struct via_info *card, struct via_channel *chan, void *arg){ audio_buf_info info; via_chan_set_buffering(card, chan, -1); info.fragstotal = chan->frag_number; info.fragsize = chan->frag_size; /* number of full fragments we can read/write without blocking */ info.fragments = atomic_read (&chan->n_frags); if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0)) info.fragments--; /* number of bytes that can be read or written immediately * without blocking. */ info.bytes = (info.fragments * chan->frag_size); if (chan->slop_len % chan->frag_size > 0) info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size); DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n", info.fragstotal, info.fragsize, info.fragments, info.bytes); return copy_to_user (arg, &info, sizeof (info));}/** * via_dsp_ioctl_ptr - get information about hardware buffer ptr * @card: Private info for specified board * @chan: pointer to channel-specific info * @arg: user buffer for returned information * * Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR. * * Locking: inside card->syscall_sem */static int via_dsp_ioctl_ptr (struct via_info *card
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -