📄 esssolo1.c
字号:
break; } __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.iwait, &wait); return ret;}static ssize_t solo1_midi_write(struct file *file, const 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_READ, buffer, count)) return -EFAULT; if (count == 0) return 0; ret = 0; add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; cnt = MIDIOUTBUF - ptr; if (s->midi.ocnt + cnt > MIDIOUTBUF) cnt = MIDIOUTBUF - s->midi.ocnt; if (cnt <= 0) { __set_current_state(TASK_INTERRUPTIBLE); solo1_handle_midi(s); } 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_from_user(s->midi.obuf + ptr, buffer, cnt)) { if (!ret) ret = -EFAULT; break; } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; s->midi.ocnt += cnt; spin_unlock_irqrestore(&s->lock, flags); count -= cnt; buffer += cnt; ret += cnt; spin_lock_irqsave(&s->lock, flags); solo1_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } __set_current_state(TASK_RUNNING); remove_wait_queue(&s->midi.owait, &wait); return ret;}/* No kernel lock - we have our own spinlock */static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait){ struct solo1_state *s = (struct solo1_state *)file->private_data; unsigned long flags; unsigned int mask = 0; VALIDATE_STATE(s); if (file->f_flags & FMODE_WRITE) poll_wait(file, &s->midi.owait, wait); if (file->f_flags & FMODE_READ) poll_wait(file, &s->midi.iwait, wait); spin_lock_irqsave(&s->lock, flags); if (file->f_flags & FMODE_READ) { if (s->midi.icnt > 0) mask |= POLLIN | POLLRDNORM; } if (file->f_flags & FMODE_WRITE) { if (s->midi.ocnt < MIDIOUTBUF) mask |= POLLOUT | POLLWRNORM; } spin_unlock_irqrestore(&s->lock, flags); return mask;}static int solo1_midi_open(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); unsigned long flags; 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_midi == minor) 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 & (file->f_mode << FMODE_MIDI_SHIFT)) { 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); } spin_lock_irqsave(&s->lock, flags); if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { s->midi.ird = s->midi.iwr = s->midi.icnt = 0; s->midi.ord = s->midi.owr = s->midi.ocnt = 0; outb(0xff, s->mpubase+1); /* reset command */ outb(0x3f, s->mpubase+1); /* uart command */ if (!(inb(s->mpubase+1) & 0x80)) inb(s->mpubase); s->midi.ird = s->midi.iwr = s->midi.icnt = 0; outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ init_timer(&s->midi.timer); s->midi.timer.expires = jiffies+1; s->midi.timer.data = (unsigned long)s; s->midi.timer.function = solo1_midi_timer; add_timer(&s->midi.timer); } if (file->f_mode & FMODE_READ) { s->midi.ird = s->midi.iwr = s->midi.icnt = 0; } if (file->f_mode & FMODE_WRITE) { s->midi.ord = s->midi.owr = s->midi.ocnt = 0; } spin_unlock_irqrestore(&s->lock, flags); s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); up(&s->open_sem); return 0;}static int solo1_midi_release(struct inode *inode, struct file *file){ struct solo1_state *s = (struct solo1_state *)file->private_data; DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; VALIDATE_STATE(s); lock_kernel(); if (file->f_mode & FMODE_WRITE) { add_wait_queue(&s->midi.owait, &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->midi.ocnt; spin_unlock_irqrestore(&s->lock, flags); if (count <= 0) break; if (signal_pending(current)) break; if (file->f_flags & O_NONBLOCK) break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG "solo1: midi timed out??\n"); } remove_wait_queue(&s->midi.owait, &wait); set_current_state(TASK_RUNNING); } down(&s->open_sem); s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); spin_lock_irqsave(&s->lock, flags); if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ del_timer(&s->midi.timer); } spin_unlock_irqrestore(&s->lock, flags); wake_up(&s->open_wait); up(&s->open_sem); unlock_kernel(); return 0;}static /*const*/ struct file_operations solo1_midi_fops = { owner: THIS_MODULE, llseek: no_llseek, read: solo1_midi_read, write: solo1_midi_write, poll: solo1_midi_poll, open: solo1_midi_open, release: solo1_midi_release,};/* --------------------------------------------------------------------- */static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ static const unsigned char op_offset[18] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 }; struct solo1_state *s = (struct solo1_state *)file->private_data; struct dm_fm_voice v; struct dm_fm_note n; struct dm_fm_params p; unsigned int io; unsigned int regb; switch (cmd) { case FM_IOCTL_RESET: for (regb = 0xb0; regb < 0xb9; regb++) { outb(regb, s->sbbase); outb(0, s->sbbase+1); outb(regb, s->sbbase+2); outb(0, s->sbbase+3); } return 0; case FM_IOCTL_PLAY_NOTE: if (copy_from_user(&n, (void *)arg, sizeof(n))) return -EFAULT; if (n.voice >= 18) return -EINVAL; if (n.voice >= 9) { regb = n.voice - 9; io = s->sbbase+2; } else { regb = n.voice; io = s->sbbase; } outb(0xa0 + regb, io); outb(n.fnum & 0xff, io+1); outb(0xb0 + regb, io); outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); return 0; case FM_IOCTL_SET_VOICE: if (copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; if (v.voice >= 18) return -EINVAL; regb = op_offset[v.voice]; io = s->sbbase + ((v.op & 1) << 1); outb(0x20 + regb, io); outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); outb(0x40 + regb, io); outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); outb(0x60 + regb, io); outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); outb(0x80 + regb, io); outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); outb(0xe0 + regb, io); outb(v.waveform & 0x7, io+1); if (n.voice >= 9) { regb = n.voice - 9; io = s->sbbase+2; } else { regb = n.voice; io = s->sbbase; } outb(0xc0 + regb, io); outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | (v.connection & 1), io+1); return 0; case FM_IOCTL_SET_PARAMS: if (copy_from_user(&p, (void *)arg, sizeof(p))) return -EFAULT; outb(0x08, s->sbbase); outb((p.kbd_split & 1) << 6, s->sbbase+1); outb(0xbd, s->sbbase); outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); return 0; case FM_IOCTL_SET_OPL: outb(4, s->sbbase+2); outb(arg, s->sbbase+3); return 0; case FM_IOCTL_SET_MODE: outb(5, s->sbbase+2); outb(arg & 1, s->sbbase+3); return 0; default: return -EINVAL; }}static int solo1_dmfm_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_dmfm == minor) 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_DMFM) { 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); } if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) { up(&s->open_sem); printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); return -EBUSY; } /* init the stuff */ outb(1, s->sbbase); outb(0x20, s->sbbase+1); /* enable waveforms */ outb(4, s->sbbase+2); outb(0, s->sbbase+3); /* no 4op enabled */ outb(5, s->sbbase+2); outb(1, s->sbbase+3); /* enable OPL3 */ s->open_mode |= FMODE_DMFM; up(&s->open_sem); return 0;}static int solo1_dmfm_release(struct inode *inode, struct file *file){ struct solo1_state *s = (struct solo1_state *)file->private_data; unsigned int regb; VALIDATE_STATE(s); lock_kernel(); down(&s->open_sem); s->open_mode &= ~FMODE_DMFM; for (regb = 0xb0; regb < 0xb9; regb++) { outb(regb, s->sbbase); outb(0, s->sbbase+1); outb(regb, s->sbbase+2); outb(0, s->sbbase+3); } release_region(s->sbbase, FMSYNTH_EXTENT); wake_up(&s->open_wait); up(&s->open_sem); unlock_kernel(); return 0;}static /*const*/ struct file_operations solo1_dmfm_fops = { owner: THIS_MODULE, llseek: no_llseek, ioctl: solo1_dmfm_ioctl, open: solo1_dmfm_open, release: solo1_dmfm_release,};/* --------------------------------------------------------------------- */static struct initvol { int mixch; int vol;} initvol[] __initdata = { { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, { SOUND_MIXER_WRITE_PCM, 0x4040 }, { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, { SOUND_MIXER_WRITE_CD, 0x4040 }, { SOUND_MIXER_WRITE_LINE, 0x4040 }, { SOUND_MIXER_WRITE_LINE1, 0x4040 }, { SOUND_MIXER_WRITE_LINE2, 0x4040 }, { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, { SOUND_MIXER_WRITE_MIC, 0x4040 }};static int setup_solo1(struct solo1_state *s){ struct pci_dev *pcidev = s->dev; mm_segment_t fs; int i, val; /* initialize DDMA base address */ printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ pci_write_config_dword(pcidev, 0x50, 0); /* disable legacy audio address decode */ pci_write_config_word(pcidev, 0x40, 0x907f); /* initialize the chips */ if (!reset_ctrl(s)) { printk(KERN_ERR "esssolo1: cannot reset controller\n"); return -
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -