📄 sbc.c
字号:
/*****************************************************************************//* * sbc.c -- Linux soundcard HF FSK driver, * Soundblaster specific functions. * * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) * Swiss Federal Institute of Technology (ETH), Electronics Lab * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * *//*****************************************************************************/ #include <linux/types.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/hfmodem.h>#include <asm/io.h>#include <asm/dma.h>/* --------------------------------------------------------------------- *//* * the sbc converter's registers */#define DSP_RESET(iobase) (iobase+0x6)#define DSP_READ_DATA(iobase) (iobase+0xa)#define DSP_WRITE_DATA(iobase) (iobase+0xc)#define DSP_WRITE_STATUS(iobase) (iobase+0xc)#define DSP_DATA_AVAIL(iobase) (iobase+0xe)#define DSP_MIXER_ADDR(iobase) (iobase+0x4)#define DSP_MIXER_DATA(iobase) (iobase+0x5)#define DSP_INTACK_16BIT(iobase) (iobase+0xf)#define SBC_EXTENT 16/* --------------------------------------------------------------------- *//* * SBC commands */#define SBC_OUTPUT 0x14#define SBC_INPUT 0x24#define SBC_BLOCKSIZE 0x48#define SBC_HI_OUTPUT 0x91 #define SBC_HI_INPUT 0x99 #define SBC_LO_OUTPUT_AUTOINIT 0x1c#define SBC_LO_INPUT_AUTOINIT 0x2c#define SBC_HI_OUTPUT_AUTOINIT 0x90 #define SBC_HI_INPUT_AUTOINIT 0x98#define SBC_IMMED_INT 0xf2#define SBC_GET_REVISION 0xe1#define ESS_GET_REVISION 0xe7#define ESS_EXTENDED_MODE 0xc6#define SBC_SPEAKER_ON 0xd1#define SBC_SPEAKER_OFF 0xd3#define SBC_DMA_ON 0xd0#define SBC_DMA_OFF 0xd4#define SBC_SAMPLE_RATE 0x40#define SBC_SAMPLE_RATE_OUT 0x41#define SBC_SAMPLE_RATE_IN 0x42#define SBC_MONO_8BIT 0xa0#define SBC_MONO_16BIT 0xa4#define SBC_STEREO_8BIT 0xa8#define SBC_STEREO_16BIT 0xac#define SBC4_OUT8_AI 0xc6#define SBC4_IN8_AI 0xce#define SBC4_MODE_UNS_MONO 0x00#define SBC4_MODE_SIGN_MONO 0x10#define SBC4_OUT16_AI 0xb6#define SBC4_IN16_AI 0xbe#define SBC4_OUT16_AI_NO_FIFO 0xb4#define SBC4_IN16_AI_NO_FIFO 0xbc/* --------------------------------------------------------------------- */extern const struct hfmodem_scops sbc4_scops;extern const struct hfmodem_scops ess_scops;/* --------------------------------------------------------------------- */static int reset_dsp(struct hfmodem_state *dev){ int i; outb(1, DSP_RESET(dev->io.base_addr)); udelay(3); outb(0, DSP_RESET(dev->io.base_addr)); for (i = 0; i < 0xffff; i++) if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) return 1; return 0;}/* --------------------------------------------------------------------- */static void write_dsp(struct hfmodem_state *dev, unsigned char data){ int i; for (i = 0; i < 0xffff; i++) if (!(inb(DSP_WRITE_STATUS(dev->io.base_addr)) & 0x80)) { outb(data, DSP_WRITE_DATA(dev->io.base_addr)); return; }}/* --------------------------------------------------------------------- */static int read_dsp(struct hfmodem_state *dev, unsigned char *data){ int i; if (!data) return 0; for (i = 0; i < 0xffff; i++) if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) { *data = inb(DSP_READ_DATA(dev->io.base_addr)); return 1; } return 0;}/* --------------------------------------------------------------------- */static void write_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char data){ write_dsp(dev, reg); write_dsp(dev, data);}/* --------------------------------------------------------------------- */static int read_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char *data){ write_dsp(dev, 0xc0); write_dsp(dev, reg); return read_dsp(dev, data);}/* --------------------------------------------------------------------- */static int reset_ess(struct hfmodem_state *dev){ int i; outb(3, DSP_RESET(dev->io.base_addr)); /* reset FIFOs too */ udelay(3); outb(0, DSP_RESET(dev->io.base_addr)); for (i = 0; i < 0xffff; i++) if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) { write_dsp(dev, ESS_EXTENDED_MODE); return 1; } return 0;}/* --------------------------------------------------------------------- */static int config_resources(struct hfmodem_state *dev){ unsigned char irqreg = 0, dmareg = 0, realirq, realdma; unsigned long flags; switch (dev->io.irq) { case 2: case 9: irqreg |= 0x01; break; case 5: irqreg |= 0x02; break; case 7: irqreg |= 0x04; break; case 10: irqreg |= 0x08; break; default: return -ENODEV; } switch (dev->io.dma) { case 0: dmareg |= 0x01; break; case 1: dmareg |= 0x02; break; case 3: dmareg |= 0x08; break; case 5: dmareg |= 0x20; break; case 6: dmareg |= 0x40; break; case 7: dmareg |= 0x80; break; default: return -ENODEV; } save_flags(flags); cli(); outb(0x80, DSP_MIXER_ADDR(dev->io.base_addr)); outb(irqreg, DSP_MIXER_DATA(dev->io.base_addr)); realirq = inb(DSP_MIXER_DATA(dev->io.base_addr)); outb(0x81, DSP_MIXER_ADDR(dev->io.base_addr)); outb(dmareg, DSP_MIXER_DATA(dev->io.base_addr)); realdma = inb(DSP_MIXER_DATA(dev->io.base_addr)); restore_flags(flags); if ((~realirq) & irqreg || (~realdma) & dmareg) { printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " "and IRQ/DMA specified wrongly?\n", hfmodem_drvname); return -EINVAL; } return 0;}/* --------------------------------------------------------------------- */extern __inline__ void sbc_int_ack_8bit(struct hfmodem_state *dev){ inb(DSP_DATA_AVAIL(dev->io.base_addr));}/* --------------------------------------------------------------------- */extern __inline__ void sbc_int_ack_16bit(struct hfmodem_state *dev){ inb(DSP_INTACK_16BIT(dev->io.base_addr));}/* --------------------------------------------------------------------- */static void set_mixer(struct hfmodem_state *dev, unsigned char reg, unsigned char data){ outb(reg, DSP_MIXER_ADDR(dev->io.base_addr)); outb(data, DSP_MIXER_DATA(dev->io.base_addr));} /* --------------------------------------------------------------------- */int hfmodem_sbcprobe(struct hfmodem_state *dev){ unsigned char revhi, revlo, essrevhi, essrevlo, tmp; int ret; if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-SBC_EXTENT || dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 7 || dev->io.dma == 2) return -ENXIO; if (check_region(dev->io.base_addr, SBC_EXTENT)) return -EACCES; /* * check if a card is available */ if (!reset_dsp(dev)) { printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", hfmodem_drvname, dev->io.base_addr); return -ENODEV; } set_mixer(dev, 0, 0); /* reset mixer */ write_dsp(dev, SBC_GET_REVISION); if (!read_dsp(dev, &revhi) || !read_dsp(dev, &revlo)) return -ENODEV; printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%02d\n", hfmodem_drvname, revhi, revlo); if (revhi == 3 && revlo == 1) { write_dsp(dev, ESS_GET_REVISION); if (!read_dsp(dev, &essrevhi) || !read_dsp(dev, &essrevlo)) return -ENODEV; if (essrevhi == 0x48 && (essrevlo & 0xf0) == 0x80) { printk(KERN_INFO "%s: ESS ES488 AudioDrive (rev %d): unsupported.\n", hfmodem_drvname, essrevlo & 0x0f); return -ENODEV; } if (essrevhi == 0x68 && (essrevlo & 0xf0) == 0x80) { printk(KERN_INFO "%s: ESS ES%s688 AudioDrive (rev %d)\n", hfmodem_drvname, ((essrevlo & 0x0f) >= 8) ? "1" : "", essrevlo & 0x0f); if (dev->io.dma > 3) { printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname); return -ENXIO; } printk(KERN_INFO "%s: ess: irq: ", hfmodem_drvname); read_ess(dev, 0xb1, &tmp); switch (tmp & 0xf) { case 0: printk("2, 9, \"all others\""); break; case 5: printk("5"); break; case 10: printk("7"); break; case 15: printk("10"); break; default: printk("unknown (%d)", tmp & 0xf); break; } printk(" dma: "); read_ess(dev, 0xb2, &tmp); switch (tmp & 0xf) { case 0: printk("\"all others\""); break; case 5: printk("0"); break; case 10: printk("1"); break; case 15: printk("3"); break; default: printk("unknown (%d)", tmp & 0xf); break; } printk("\n"); dev->scops = &ess_scops; return 0; } } if (revhi < 4) { printk(KERN_INFO "%s: at least SB16 required\n", hfmodem_drvname); return -ENODEV; } if (dev->io.dma < 4) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -