📄 sb_dsp.c
字号:
/* * sound/sb_dsp.c * * driver for the SoundBlaster and clones. * * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * *//* * use this as a template file for board-specific drivers. * The next two lines (and the final #endif) are in all drivers: */#include <i386/isa/snd/sound.h>#if NPCM > 0/* * Begin with the board-specific include files... */#define __SB_MIXER_C__ /* XXX warning... */#include <i386/isa/snd/sbcard.h>/* * then prototypes of functions which go in the snddev_info * (usually static, unless they are shared by other modules)... */static int sb_probe(struct isa_device *dev);static int sb_attach(struct isa_device *dev);static d_open_t sb_dsp_open;static d_close_t sb_dsp_close;static d_ioctl_t sb_dsp_ioctl;static irq_proc_t sbintr;static snd_callback_t sb_callback;/* * and prototypes for other private functions defined in this module. */static void sb_dsp_init(snddev_info *d, struct isa_device *dev);static void sb_mix_init(snddev_info *d);static int sb_mixer_set(snddev_info *d, int dev, int value);static int dsp_speed(snddev_info *d);static void sb_mixer_reset(snddev_info *d);u_int sb_get_byte(int io_base);int ess_write(int io_base, u_char reg, int val);int ess_read(int io_base, u_char reg);/* * Then put here the descriptors for the various boards supported * by this module, properly initialized. */snddev_info sb_op_desc = { "basic soundblaster", SNDCARD_SB, sb_probe, sb_attach, sb_dsp_open, sb_dsp_close /* sb_close */, NULL /* use generic sndread */, NULL /* use generic sndwrite */, sb_dsp_ioctl, sndselect, sbintr, sb_callback, DSP_BUFFSIZE, /* bufsize */ AFMT_STEREO | AFMT_U8, /* audio format */} ;/* * Then the file continues with the body of all functions * directly referenced in the descriptor. *//* * the probe routine for the SoundBlaster only consists in * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * * Remember, ISA probe routines are supposed to return the * size of io space used. */static intsb_probe(struct isa_device *dev){ bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); if (dev->id_iobase == -1) { dev->id_iobase = 0x220; BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");) if (snd_conflict(dev->id_iobase)) dev->id_iobase = 0x240; } if (snd_conflict(dev->id_iobase)) return 0 ; if (sb_reset_dsp(dev->id_iobase)) return 16 ; /* the SB uses 16 registers... */ else return 0;}static intsb_attach(struct isa_device *dev){ snddev_info *d = &pcm_info[dev->id_unit] ; dev->id_alive = 16 ; /* number of io ports */ /* should be already set but just in case... */ sb_dsp_init(d, dev); return 0 ;}/* * here are the main routines from the switches. *//* * Unlike MSS, the sb only supports a single open (does not mean * that only a single process is using it, since it can fork * afterwards, or pass the descriptor to another process). * */static intsb_dsp_open(dev_t dev, int flags, int mode, struct proc * p){ snddev_info *d; int unit ; dev = minor(dev); unit = dev >> 4 ; d = &pcm_info[unit] ; DEB(printf("<%s>%d : open\n", d->name, unit)); if (d->flags & SND_F_BUSY) { printf("<%s>%d open: device busy\n", d->name, unit); return EBUSY ; } d->wsel.si_pid = 0; d->wsel.si_flags = 0; d->rsel.si_pid = 0; d->rsel.si_flags = 0; d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; d->flags = 0 ; d->bd_flags &= ~BD_F_HISPEED ; switch ( dev & 0xf ) { case SND_DEV_DSP16 : if ((d->audio_fmt & AFMT_S16_LE) == 0) { printf("sorry, 16-bit not supported on SB %d.%02d\n", (d->bd_id >>8) & 0xff, d->bd_id & 0xff); return ENXIO; } d->play_fmt = d->rec_fmt = AFMT_S16_LE ; break; case SND_DEV_AUDIO : d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; break ; case SND_DEV_DSP : d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; } /* * since the SB is not simmetric, I use the open mode to select * which channel should be privileged, and disable I/O in the * other direction. * In case the board is opened RW, we don't have enough * information on what to do. Temporarily, privilege the * playback channel, which is used more often, and set the other * one to U8. */ if ( (flags & FREAD) == 0) /* opened write only */ d->rec_fmt = 0 ; else if ( (flags & FWRITE) == 0) /* opened read only */ d->play_fmt = 0 ; else /* opened read/write */ d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; d->flags |= SND_F_BUSY ; d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; sb_reset_dsp(d->io_base); if (d->bd_flags & BD_F_ESS) sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ ask_init(d); return 0;}static intsb_dsp_close(dev_t dev, int flags, int mode, struct proc * p){ int unit; snddev_info *d; u_long s; dev = minor(dev); unit = dev >> 4 ; d = &pcm_info[unit] ; s = spltty(); d->flags |= SND_F_CLOSING ; splx(s); snd_flush(d); sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */ d->flags = 0 ; return 0 ;}static intsb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p){ int unit; snddev_info *d; dev = minor(dev); unit = dev >> 4 ; d = &pcm_info[unit] ; /* * handle mixer calls first. Reads are in the default handler, * so do not bother about them. */ if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ; /* * for the remaining functions, use the default handler. * ENOSYS means that the default handler should take care * of implementing the ioctl. */ return ENOSYS ;}static voidsbintr(int unit){ snddev_info *d = &pcm_info[unit]; int reason = 3, c=1, io_base = d->io_base; DEB(printf("got sbintr for unit %d, flags 0x%08lx\n", unit, d->flags)); /* * SB < 4.0 is half duplex and has only 1 bit for int source, * so we fake it. SB 4.x (SB16) has the int source in a separate * register. * The Vibra16X has separate flags for 8 and 16 bit transfers, but * I have no idea how to tell capture from playback interrupts... */#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16)again: if (d->bd_flags & BD_F_SB16) { c = sb_getmixer(io_base, IRQ_STAT); /* this tells us if the source is 8-bit or 16-bit dma. We * have to check the io channel to map it to read or write... */ reason = 0 ; if ( c & 1 ) { /* 8-bit dma */ if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) reason |= 1; if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) reason |= 2; } if ( c & 2 ) { /* 16-bit dma */ if (d->play_fmt == AFMT_S16_LE) reason |= 1; if (d->rec_fmt == AFMT_S16_LE) reason |= 2; } } /* XXX previous location of ack... */ DEB(printf("sbintr, flags 0x%08lx reason %d c 0x%x\n", d->flags, reason, c)); if ( reason & 1 ) { /* possibly a write interrupt */ if ( d->dbuf_out.dl ) dsp_wrintr(d); } if ( reason & 2 ) { if ( d->dbuf_in.dl ) dsp_rdintr(d); } if ( c & 2 ) inb(DSP_DATA_AVL16); /* 16-bit int ack */ if (c & 1) inb(DSP_DATA_AVAIL); /* 8-bit int ack */ /* * the sb16 might have multiple sources etc. */ if ((d->bd_flags & BD_F_SB16) && (c & 3)) goto again;}/* * device-specific function called back from the dma module. * The reason of the callback is the second argument. * NOTE: during operations, some ioctl can be called to change * settings (e.g. speed, channels, format), and the default * ioctl handler will just record the change and set the * flag SND_F_INIT. The callback routine is in charge of applying * the changes at the next convenient time (typically, at the * start of operations). For full duplex devices, in some cases the * init requires both channels to be idle. */static intsb_callback(snddev_info *d, int reason){ int rd = reason & SND_CB_RD ; snd_dbuf *b = (rd) ? & (d->dbuf_in) : & (d->dbuf_out) ; int l = b->dl ; switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ /* * set the speed */ dsp_speed(d); /* * set the desired DMA blocksize (influences select behaviour) */ snd_set_blocksize(d); /* * since native mulaw is not present, emulate it. */ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; /* * there are too many flavours of SB for my taste... here i try to do * the proper initialization for each one. */ if (PLAIN_SB16(d->bd_flags)) { u_char c, c1 ; /* the original SB16 (non-PnP, or PnP, or Vibra16C) * can do full duplex using one 16-bit channel * and one 8-bit channel. It needs to be programmed to * use split format though. * I DON'T do this for the Vibra16X because I have no idea * of what needs to be done there... * * I use the following algorithm: * 1. check which direction(s) are active; * 2. check if we should swap dma channels * 3. check if we can do the swap. */ int swap = 1 ; /* default... */ if (d->play_fmt == 0) { /* do whatever the read channel wants */ if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 ) swap = 0; if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 ) swap = 0; } else { /* privilege the write channel */ if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 ) swap = 0; if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) swap = 0; if ( d->rec_fmt ) { /* check for possible config errors. * This cannot happen at open time since even in * case of opening rw we privilege the play * channel. */ if (d->rec_fmt == d->play_fmt) { DDB(printf("sorry, read DMA channel unavailable\n")); } } } DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n", d->play_fmt, d->rec_fmt, swap);) if (swap) { int c = d->dbuf_in.chan ; d->dbuf_in.chan = d->dbuf_out.chan; d->dbuf_out.chan = c ; } } else if (d->bd_flags & BD_F_ESS) { u_char c; DEB(printf("SND_CB_INIT, play_fmt == 0x%x, rec_fmt == 0x%x\n", (int) d->play_fmt, (int) d->rec_fmt)); /* autoinit DMA mode */ if (d->play_fmt) ess_write(d->io_base, 0xb8, 0x04); else ess_write(d->io_base, 0xb8, 0x0e); c = (ess_read(d->io_base, 0xa8) & ~0x03) | 0x01; if ((d->flags & SND_F_STEREO) == 0) c++; ess_write(d->io_base, 0xa8, c); /* select mono/stereo */ ess_write(d->io_base, 0xb9, 2); /* demand 4 bytes/transfer */ switch (d->play_fmt ? d->play_fmt : d->rec_fmt) { case AFMT_S16_LE: if (d->flags & SND_F_STEREO) { /* 16 bit stereo */ if (d->play_fmt) ess_write(d->io_base, 0xb6, 0x00);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -