📄 esssolo1.c
字号:
return 0; case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) return -EFAULT; if (val != 0) { stop_adc(s); stop_dac(s); s->dma_adc.ready = s->dma_dac.ready = 0; /* program channels */ s->channels = (val >= 2) ? 2 : 1; prog_codec(s); } return put_user(s->channels, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ if (get_user(val, (int *)arg)) return -EFAULT; if (val != AFMT_QUERY) { stop_adc(s); stop_dac(s); s->dma_adc.ready = s->dma_dac.ready = 0; /* program format */ if (val != AFMT_S16_LE && val != AFMT_U16_LE && val != AFMT_S8 && val != AFMT_U8) val = AFMT_U8; s->fmt = val; prog_codec(s); } return put_user(s->fmt, (int *)arg); case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & s->ena & FMODE_READ) val |= PCM_ENABLE_INPUT; if (file->f_mode & s->ena & FMODE_WRITE) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) return ret; s->dma_dac.enabled = 1; start_adc(s); if (inb(s->ddmabase+15) & 1) printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); } else { s->dma_dac.enabled = 0; stop_adc(s); } } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) return ret; s->dma_dac.enabled = 1; start_dac(s); } else { s->dma_dac.enabled = 0; stop_dac(s); } } return 0; case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) return val; spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); abinfo.fragsize = s->dma_dac.fragsize; count = s->dma_dac.count; if (count < 0) count = 0; abinfo.bytes = s->dma_dac.dmasize - count; abinfo.fragstotal = s->dma_dac.numfrag; abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; spin_unlock_irqrestore(&s->lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) return val; spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); abinfo.fragsize = s->dma_adc.fragsize; abinfo.bytes = s->dma_adc.count; abinfo.fragstotal = s->dma_adc.numfrag; abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; spin_unlock_irqrestore(&s->lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) return val; spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); if (count < 0) count = 0; return put_user(count, (int *)arg); case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) return val; spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); cinfo.bytes = s->dma_adc.total_bytes; cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; cinfo.ptr = s->dma_adc.hwptr; if (s->dma_adc.mapped) s->dma_adc.count &= s->dma_adc.fragsize-1; spin_unlock_irqrestore(&s->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) return val; spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); cinfo.bytes = s->dma_dac.total_bytes; count = s->dma_dac.count; if (count < 0) count = 0; cinfo.blocks = count >> s->dma_dac.fragshift; cinfo.ptr = s->dma_dac.hwptr; if (s->dma_dac.mapped) s->dma_dac.count &= s->dma_dac.fragsize-1; spin_unlock_irqrestore(&s->lock, flags);#if 0 printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples);#endif return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { if ((val = prog_dmabuf_dac(s))) return val; return put_user(s->dma_dac.fragsize, (int *)arg); } if ((val = prog_dmabuf_adc(s))) return val; return put_user(s->dma_adc.fragsize, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { s->dma_adc.ossfragshift = val & 0xffff; s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; if (s->dma_adc.ossfragshift < 4) s->dma_adc.ossfragshift = 4; if (s->dma_adc.ossfragshift > 15) s->dma_adc.ossfragshift = 15; if (s->dma_adc.ossmaxfrags < 4) s->dma_adc.ossmaxfrags = 4; } if (file->f_mode & FMODE_WRITE) { s->dma_dac.ossfragshift = val & 0xffff; s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; if (s->dma_dac.ossfragshift < 4) s->dma_dac.ossfragshift = 4; if (s->dma_dac.ossfragshift > 15) s->dma_dac.ossfragshift = 15; if (s->dma_dac.ossmaxfrags < 4) s->dma_dac.ossmaxfrags = 4; } return 0; case SNDCTL_DSP_SUBDIVIDE: if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) return -EINVAL; if (get_user(val, (int *)arg)) return -EFAULT; if (val != 1 && val != 2 && val != 4) return -EINVAL; if (file->f_mode & FMODE_READ) s->dma_adc.subdivision = val; if (file->f_mode & FMODE_WRITE) s->dma_dac.subdivision = val; return 0; case SOUND_PCM_READ_RATE: return put_user(s->rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: return put_user(s->channels, (int *)arg); case SOUND_PCM_READ_BITS: return put_user((s->fmt & (AFMT_S8|AFMT_U8)) ? 8 : 16, (int *)arg); case SOUND_PCM_WRITE_FILTER: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_READ_FILTER: return -EINVAL; } return mixer_ioctl(s, cmd, arg);}static int solo1_release(struct inode *inode, struct file *file){ struct solo1_state *s = (struct solo1_state *)file->private_data; VALIDATE_STATE(s); lock_kernel(); if (file->f_mode & FMODE_WRITE) drain_dac(s, file->f_flags & O_NONBLOCK); down(&s->open_sem); if (file->f_mode & FMODE_WRITE) { stop_dac(s); outb(0, s->iobase+6); /* disable DMA */ dealloc_dmabuf(s, &s->dma_dac); } if (file->f_mode & FMODE_READ) { stop_adc(s); outb(1, s->ddmabase+0xf); /* mask DMA channel */ outb(0, s->ddmabase+0xd); /* DMA master clear */ dealloc_dmabuf(s, &s->dma_adc); } s->open_mode &= ~(FMODE_READ | FMODE_WRITE); wake_up(&s->open_wait); up(&s->open_sem); unlock_kernel(); return 0;}static int solo1_open(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); struct solo1_state *s = NULL; struct pci_dev *pci_dev; pci_for_each_dev(pci_dev) { struct pci_driver *drvr; drvr = pci_dev_driver(pci_dev); if (drvr != &solo1_driver) continue; s = (struct solo1_state*)pci_get_drvdata(pci_dev); if (!s) continue; if (!((s->dev_audio ^ minor) & ~0xf)) break; } if (!s) return -ENODEV; VALIDATE_STATE(s); file->private_data = s; /* wait for device to become free */ down(&s->open_sem); while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { if (file->f_flags & O_NONBLOCK) { up(&s->open_sem); return -EBUSY; } add_wait_queue(&s->open_wait, &wait); __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); schedule(); remove_wait_queue(&s->open_wait, &wait); set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); } s->fmt = AFMT_U8; s->channels = 1; s->rate = 8000; s->clkdiv = 96 | 0x80; s->ena = 0; s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; s->dma_adc.enabled = 1; s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; s->dma_dac.enabled = 1; s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&s->open_sem); prog_codec(s); return 0;}static /*const*/ struct file_operations solo1_audio_fops = { owner: THIS_MODULE, llseek: no_llseek, read: solo1_read, write: solo1_write, poll: solo1_poll, ioctl: solo1_ioctl, mmap: solo1_mmap, open: solo1_open, release: solo1_release,};/* --------------------------------------------------------------------- *//* hold spinlock for the following! */static void solo1_handle_midi(struct solo1_state *s){ unsigned char ch; int wake; if (!(s->mpubase)) return; wake = 0; while (!(inb(s->mpubase+1) & 0x80)) { ch = inb(s->mpubase); if (s->midi.icnt < MIDIINBUF) { s->midi.ibuf[s->midi.iwr] = ch; s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; s->midi.icnt++; } wake = 1; } if (wake) wake_up(&s->midi.iwait); wake = 0; while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { outb(s->midi.obuf[s->midi.ord], s->mpubase); s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; s->midi.ocnt--; if (s->midi.ocnt < MIDIOUTBUF-16) wake = 1; } if (wake) wake_up(&s->midi.owait);}static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct solo1_state *s = (struct solo1_state *)dev_id; unsigned int intsrc; /* fastpath out, to ease interrupt sharing */ intsrc = inb(s->iobase+7); /* get interrupt source(s) */ if (!intsrc) return; (void)inb(s->sbbase+0xe); /* clear interrupt */ spin_lock(&s->lock); /* clear audio interrupts first */ if (intsrc & 0x20) write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); solo1_update_ptr(s); solo1_handle_midi(s); spin_unlock(&s->lock);}static void solo1_midi_timer(unsigned long data){ struct solo1_state *s = (struct solo1_state *)data; unsigned long flags; spin_lock_irqsave(&s->lock, flags); solo1_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); s->midi.timer.expires = jiffies+1; add_timer(&s->midi.timer);}/* --------------------------------------------------------------------- */static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ struct solo1_state *s = (struct solo1_state *)file->private_data; DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; int cnt; VALIDATE_STATE(s); if (ppos != &file->f_pos) return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; if (count == 0) return 0; ret = 0; add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; cnt = MIDIINBUF - ptr; if (s->midi.icnt < cnt) cnt = s->midi.icnt; if (cnt <= 0) __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; break; } continue; } if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { if (!ret) ret = -EFAULT; break; } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; s->midi.icnt -= cnt; spin_unlock_irqrestore(&s->lock, flags); count -= cnt; buffer += cnt; ret += cnt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -