📄 esssolo1.c
字号:
return -ENOMEM; db->buforder = order; /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); for (page = virt_to_page(db->rawbuf); page <= pend; page++) mem_map_reserve(page); } if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) sample_shift++; if (s->channels > 1) sample_shift++; bytespersec = s->rate << sample_shift; bufs = PAGE_SIZE << db->buforder; if (db->ossfragshift) { if ((1000 << db->ossfragshift) < bytespersec) db->fragshift = ld2(bytespersec/1000); else db->fragshift = db->ossfragshift; } else { db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1)); if (db->fragshift < 3) db->fragshift = 3; } db->numfrag = bufs >> db->fragshift; while (db->numfrag < 4 && db->fragshift > 3) { db->fragshift--; db->numfrag = bufs >> db->fragshift; } db->fragsize = 1 << db->fragshift; if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; db->fragsamples = db->fragsize >> sample_shift; db->dmasize = db->numfrag << db->fragshift; db->enabled = 1; return 0;}static inline int prog_dmabuf_adc(struct solo1_state *s){ unsigned long va; int c; stop_adc(s); /* check if PCI implementation supports 24bit busmaster DMA */ if (s->dev->dma_mask > 0xffffff) return -EIO; if ((c = prog_dmabuf(s, &s->dma_adc))) return c; va = s->dma_adc.dmaaddr; if ((va & ~((1<<24)-1))) panic("solo1: buffer above 16M boundary"); outb(0, s->ddmabase+0xd); /* clear */ outb(1, s->ddmabase+0xf); /* mask */ /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ outl(va, s->ddmabase); outw(s->dma_adc.dmasize-1, s->ddmabase+4); c = - s->dma_adc.fragsamples; write_ctrl(s, 0xa4, c); write_ctrl(s, 0xa5, c >> 8); outb(0, s->ddmabase+0xf); s->dma_adc.ready = 1; return 0;}static inline int prog_dmabuf_dac(struct solo1_state *s){ unsigned long va; int c; stop_dac(s); if ((c = prog_dmabuf(s, &s->dma_dac))) return c; memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ va = s->dma_dac.dmaaddr; if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) panic("solo1: buffer crosses 1M boundary"); outl(va, s->iobase); /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ outw(s->dma_dac.dmasize, s->iobase+4); c = - s->dma_dac.fragsamples; write_mixer(s, 0x74, c); write_mixer(s, 0x76, c >> 8); outb(0xa, s->iobase+6); s->dma_dac.ready = 1; return 0;}static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c){ if (bptr + len > bsize) { unsigned x = bsize - bptr; memset(((char *)buf) + bptr, c, x); bptr = 0; len -= x; } memset(((char *)buf) + bptr, c, len);}/* call with spinlock held! */static void solo1_update_ptr(struct solo1_state *s){ int diff; unsigned hwptr; /* update ADC pointer */ if (s->ena & FMODE_READ) { hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; s->dma_adc.hwptr = hwptr; s->dma_adc.total_bytes += diff; s->dma_adc.count += diff;#if 0 printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count);#endif if (s->dma_adc.mapped) { if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) wake_up(&s->dma_adc.wait); } else { if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { s->ena &= ~FMODE_READ; write_ctrl(s, 0xb8, 0xe); s->dma_adc.error++; } if (s->dma_adc.count > 0) wake_up(&s->dma_adc.wait); } } /* update DAC pointer */ if (s->ena & FMODE_WRITE) { hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; s->dma_dac.hwptr = hwptr; s->dma_dac.total_bytes += diff;#if 0 printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count);#endif if (s->dma_dac.mapped) { s->dma_dac.count += diff; if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) wake_up(&s->dma_dac.wait); } else { s->dma_dac.count -= diff; if (s->dma_dac.count <= 0) { s->ena &= ~FMODE_WRITE; write_mixer(s, 0x78, 0x12); s->dma_dac.error++; } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); s->dma_dac.endcleared = 1; } if (s->dma_dac.count < (signed)s->dma_dac.dmasize) wake_up(&s->dma_dac.wait); } }}/* --------------------------------------------------------------------- */static void prog_codec(struct solo1_state *s){ unsigned long flags; int fdiv, filter; unsigned char c; reset_ctrl(s); write_seq(s, 0xd3); /* program sampling rates */ filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ fdiv = 256 - 7160000 / (filter * 82); spin_lock_irqsave(&s->lock, flags); write_ctrl(s, 0xa1, s->clkdiv); write_ctrl(s, 0xa2, fdiv); write_mixer(s, 0x70, s->clkdiv); write_mixer(s, 0x72, fdiv); /* program ADC parameters */ write_ctrl(s, 0xb8, 0xe); write_ctrl(s, 0xb9, /*0x1*/0); write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); c = 0xd0; if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) c |= 0x04; if (s->fmt & (AFMT_S16_LE | AFMT_S8)) c |= 0x20; if (s->channels > 1) c ^= 0x48; write_ctrl(s, 0xb7, (c & 0x70) | 1); write_ctrl(s, 0xb7, c); write_ctrl(s, 0xb1, 0x50); write_ctrl(s, 0xb2, 0x50); /* program DAC parameters */ c = 0x40; if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) c |= 1; if (s->fmt & (AFMT_S16_LE | AFMT_S8)) c |= 4; if (s->channels > 1) c |= 2; write_mixer(s, 0x7a, c); write_mixer(s, 0x78, 0x10); s->ena = 0; spin_unlock_irqrestore(&s->lock, flags);}/* --------------------------------------------------------------------- */static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n";#define VALIDATE_STATE(s) \({ \ if (!(s) || (s)->magic != SOLO1_MAGIC) { \ printk(invalid_magic); \ return -ENXIO; \ } \})/* --------------------------------------------------------------------- */static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg){ static const unsigned int mixer_src[8] = { SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 }; static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_PCM] = 1, /* voice */ [SOUND_MIXER_SYNTH] = 2, /* FM */ [SOUND_MIXER_CD] = 3, /* CD */ [SOUND_MIXER_LINE] = 4, /* Line */ [SOUND_MIXER_LINE1] = 5, /* AUX */ [SOUND_MIXER_MIC] = 6, /* Mic */ [SOUND_MIXER_LINE2] = 7, /* Mono in */ [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ [SOUND_MIXER_RECLEV] = 9, /* Recording level */ [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ }; static const unsigned char mixreg[] = { 0x7c, /* voice */ 0x36, /* FM */ 0x38, /* CD */ 0x3e, /* Line */ 0x3a, /* AUX */ 0x1a, /* Mic */ 0x6d /* Mono in */ }; unsigned char l, r, rl, rr, vidx; int i, val; VALIDATE_STATE(s); if (cmd == SOUND_MIXER_PRIVATE1) { /* enable/disable/query mixer preamp */ if (get_user(val, (int *)arg)) return -EFAULT; if (val != -1) { val = val ? 0xff : 0xf7; write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); } val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; return put_user(val, (int *)arg); } if (cmd == SOUND_MIXER_PRIVATE2) { /* enable/disable/query spatializer */ if (get_user(val, (int *)arg)) return -EFAULT; if (val != -1) { val &= 0x3f; write_mixer(s, 0x52, val); write_mixer(s, 0x50, val ? 0x08 : 0); } return put_user(read_mixer(s, 0x52), (int *)arg); } if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "Solo1", sizeof(info.id)); strncpy(info.name, "ESS Solo1", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.id, "Solo1", sizeof(info.id)); strncpy(info.name, "ESS Solo1", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *)arg); if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) return -EINVAL; if (_SIOC_DIR(cmd) == _SIOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ return put_user(mixer_src[read_mixer(s, 0x1c) & 7], (int *)arg); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER, (int *)arg); case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, (int *)arg); case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV, (int *)arg); case SOUND_MIXER_CAPS: return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); default: i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) return -EINVAL; return put_user(s->mix.vol[vidx-1], (int *)arg); } } if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) return -EINVAL; s->mix.modcnt++; switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */#if 0 { static const unsigned char regs[] = { 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c }; int i; for (i = 0; i < sizeof(regs); i++) printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", regs[i], read_mixer(s, regs[i])); printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", 0xb4, read_ctrl(s, 0xb4)); }#endif if (get_user(val, (int *)arg)) return -EFAULT; i = hweight32(val); if (i == 0) return 0; else if (i > 1) val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; for (i = 0; i < 8; i++) { if (mixer_src[i] & val) break; } if (i > 7) return 0; write_mixer(s, 0x1c, i); return 0; case SOUND_MIXER_VOLUME: if (get_user(val, (int *)arg)) return -EFAULT; l = val & 0xff; if (l > 100) l = 100; r = (val >> 8) & 0xff; if (r > 100) r = 100; if (l < 6) { rl = 0x40; l = 0; } else { rl = (l * 2 - 11) / 3; l = (rl * 3 + 11) / 2; } if (r < 6) { rr = 0x40; r = 0; } else { rr = (r * 2 - 11) / 3; r = (rr * 3 + 11) / 2; } write_mixer(s, 0x60, rl); write_mixer(s, 0x62, rr);#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS s->mix.vol[9] = ((unsigned int)r << 8) | l;#else s->mix.vol[9] = val;#endif return put_user(s->mix.vol[9], (int *)arg); case SOUND_MIXER_SPEAKER: if (get_user(val, (int *)arg)) return -EFAULT; l = val & 0xff; if (l > 100) l = 100; else if (l < 2) l = 2; rl = (l - 2) / 14; l = rl * 14 + 2; write_mixer(s, 0x3c, rl);#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS s->mix.vol[7] = l * 0x101;#else s->mix.vol[7] = val;#endif return put_user(s->mix.vol[7], (int *)arg); case SOUND_MIXER_RECLEV: if (get_user(val, (int *)arg)) return -EFAULT; l = (val << 1) & 0x1fe; if (l > 200) l = 200; else if (l < 5) l = 5; r = (val >> 7) & 0x1fe; if (r > 200) r = 200; else if (r < 5) r = 5; rl = (l - 5) / 13; rr = (r - 5) / 13; r = (rl * 13 + 5) / 2; l = (rr * 13 + 5) / 2; write_ctrl(s, 0xb4, (rl << 4) | rr);#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS s->mix.vol[8] = ((unsigned int)r << 8) | l;#else s->mix.vol[8] = val;#endif return put_user(s->mix.vol[8], (int *)arg); default: i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) return -EINVAL; if (get_user(val, (int *)arg)) return -EFAULT; l = (val << 1) & 0x1fe; if (l > 200) l = 200;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -