summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/snd
diff options
context:
space:
mode:
authorjmg <jmg@FreeBSD.org>1997-09-14 21:42:12 +0000
committerjmg <jmg@FreeBSD.org>1997-09-14 21:42:12 +0000
commitd77c6d8be9c8cc0c98327f4e3d573575fb1e8550 (patch)
treeef367537ba76248741b9455557d3062857d71850 /sys/i386/isa/snd
downloadFreeBSD-src-d77c6d8be9c8cc0c98327f4e3d573575fb1e8550.zip
FreeBSD-src-d77c6d8be9c8cc0c98327f4e3d573575fb1e8550.tar.gz
Import of Luigi Rizzo's sound code. For more information about the driver
check out the README that is included. Submitted by: Luigi Rizzo <luigi@labinfo.iet.unipi.it>
Diffstat (limited to 'sys/i386/isa/snd')
-rw-r--r--sys/i386/isa/snd/README78
-rw-r--r--sys/i386/isa/snd/ad1848.c1509
-rw-r--r--sys/i386/isa/snd/clones.c262
-rw-r--r--sys/i386/isa/snd/dmabuf.c864
-rw-r--r--sys/i386/isa/snd/dmabuf_auto.c802
-rw-r--r--sys/i386/isa/snd/mss.h250
-rw-r--r--sys/i386/isa/snd/sb_dsp.c1080
-rw-r--r--sys/i386/isa/snd/sbcard.h387
-rw-r--r--sys/i386/isa/snd/sound.c1254
-rw-r--r--sys/i386/isa/snd/sound.h492
-rw-r--r--sys/i386/isa/snd/soundcard.h1297
-rw-r--r--sys/i386/isa/snd/ulaw.h93
12 files changed, 8368 insertions, 0 deletions
diff --git a/sys/i386/isa/snd/README b/sys/i386/isa/snd/README
new file mode 100644
index 0000000..70e3c11
--- /dev/null
+++ b/sys/i386/isa/snd/README
@@ -0,0 +1,78 @@
+ --- A new FreeBSD sound driver ---
+ by Luigi Rizzo (luigi@iet.unipi.it)
+
+
+This is an experimental version of the sound driver for FreeBSD.
+I have almost completely rewritten the main parts of the code,
+starting from the Voxware 3.5-alpha release with patches from
+Amancio Hasty. The only file which is largely similar to the original
+ones is "soundcard.h" (for compatibility reasons, since it contains
+the definition of all the ioctls).
+
+Visit http://www.iet.unipi.it/~luigi/FreeBSD.html for the latest
+information on the drivers. There you can obtain the latest source
+package that includes documentation on the design of the driver.
+
+
+CARD SUPPORT INFORMATION:
+
+For PnP cards, I also include the vendor_id and serial numbers of
+cards I have encountered.
+
+CS4236: PnP id 0x3642630e
+
+ works like a charm. All modes, including full duplex, supported in
+ MSS mode.
+
+CS4237: PnP id 0x3742630e
+
+ I had early reports of success with this board, which is almost
+ the same as the CS4236.
+
+CS4232: PnP id 0x3242630e
+
+ this chip is reported as broken in the OSS documentation. As a
+ matter of fact, on my Intel Zappa motherboard, I have problems in
+ make it use the secondary DMA channel. I have it working in
+ half duplex (both capture and playback) in SB3.2 emulation,
+ and working in playback mode in MSS emulation.
+
+OPTi931: PnP id 0x3109143e
+
+ The data sheets of this chip are very cryptic. I have it working
+ in full duplex in all modes _except_ capture of uLAW/ALAW data.
+ I am strongly convinced of a bug in the chip. I have sent email
+ to OPTI but got no reply so far. In SB emulation mode the
+ driver does not work yet (maybe I do not initialize it the
+ right way).
+
+ Another bug seems to affect full duplex operation -- it appears
+ that at times DMA transfer are requested but not counted by
+ the device. In normal DMA mode this causes deadlocks. The only
+ solution I have found is to fetch the count from the ISA DMA
+ registers, but this does not seem to work very well either.
+
+SB16 PnP: PnP id 0xXX008c0e
+
+ There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16,
+ etc. all differing in the PnP id (and with different synthesis
+ devices, which we do not support anyways).
+
+ Since 970903 the driver supports them all, both capture and
+ playback, 8 and 16 bits.
+
+GusPnP: PnP id 0x0100561e
+
+ I have code to recognize the board as MSS, but have not tested it
+ since I don't own the board. Hopefully someone will test it soon.
+
+OPTI925: PnP id 0x2509143e
+
+ there is code to recognize it as a SB clone. I have reports that
+ it probes ok, but not sure if it works.
+
+OPTI930:
+
+ should work as an MSS clone, but support for it is not implemented
+ yet.
+
diff --git a/sys/i386/isa/snd/ad1848.c b/sys/i386/isa/snd/ad1848.c
new file mode 100644
index 0000000..97b328d0
--- /dev/null
+++ b/sys/i386/isa/snd/ad1848.c
@@ -0,0 +1,1509 @@
+/*
+ * sound/ad1848.c
+ *
+ * Modified by Luigi Rizzo (luigi@iet.unipi.it)
+ *
+ * Driver for Microsoft Sound System/Windows Sound System (mss)
+ * -compatible boards. This includes:
+ *
+ * AD1848, CS4248
+ *
+ * CS4231, used in the GUS MAX and some other cards;
+ * AD1845, CS4231A (CS4231-like)
+ * CS4232 (CS4231+SB and MPU, PnP)
+ * CS4236 (upgrade of the CS4232, has a better mixer)
+ * OPTi931 (WSS compatible, full duplex, some differences from CS42xx)
+ *
+ * Copyright Luigi Rizzo, 1997
+ * Copyright by Hannu Savolainen 1994, 1995
+ *
+ * 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.
+ *
+ *
+ * Full data sheets in PDF format for the MSS-compatible chips
+ * are available at
+ *
+ * http://www.crystal.com/ for the CS42XX series, or
+ * http://www.opti.com/ for the OPTi931
+ *
+ * The OPTi931 appears to be quite buggy.
+ */
+
+#include <i386/isa/snd/sound.h>
+#if NPCM > 0
+
+/*
+ * board-specific include files
+ */
+
+#include <i386/isa/snd/mss.h>
+
+/*
+ * prototypes for procedures exported in the device descriptor
+ */
+
+static int mss_probe(struct isa_device *dev);
+static int mss_attach(struct isa_device *dev);
+
+static d_open_t mss_open;
+static d_close_t mss_close;
+static d_ioctl_t mss_ioctl;
+static irq_proc_t mss_intr;
+static irq_proc_t opti931_intr;
+static snd_callback_t mss_callback;
+
+/*
+ * prototypes for local functions
+ */
+
+static void mss_reinit(snddev_info *d);
+static int AD_WAIT_INIT(snddev_info *d, int x);
+static int mss_mixer_set(snddev_info *d, int dev, int value);
+static int mss_set_recsrc(snddev_info *d, int mask);
+static void ad1848_mixer_reset(snddev_info *d);
+
+static void opti_write(int io_base, u_char reg, u_char data);
+static u_char opti_read(int io_base, u_char reg);
+static void ad_write(snddev_info *d, int reg, u_char data);
+static void ad_write_cnt(snddev_info *d, int reg, u_short data);
+static int ad_read(snddev_info *d, int reg);
+
+/*
+ * device descriptors for the boards supported by this module.
+ */
+snddev_info mss_op_desc = {
+ "mss",
+
+ SNDCARD_MSS,
+ mss_probe,
+ mss_attach,
+
+ mss_open,
+ mss_close,
+ NULL /* mss_read */,
+ NULL /* mss_write */,
+ mss_ioctl,
+ sndselect /* mss_select */,
+
+ mss_intr,
+ mss_callback ,
+
+ DSP_BUFFSIZE, /* bufsize */
+
+ AFMT_STEREO |
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */
+ /*
+ * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE
+ */
+} ;
+
+/*
+ * this is the probe routine. Note, it is not necessary to
+ * go through this for PnP devices, since they are already
+ * indentified precisely using their PnP id.
+ *
+ * The base address supplied in the device refers to the old MSS
+ * specs where the four 4 registers in io space contain configuration
+ * information. Some boards (as an example, early MSS boards)
+ * has such a block of registers, whereas others (generally CS42xx)
+ * do not. In order to distinguish between the two and do not have
+ * to supply two separate probe routines, the flags entry in isa_device
+ * has a bit to mark this.
+ *
+ */
+
+static int
+mss_probe(struct isa_device *dev)
+{
+ u_char tmp;
+ int irq = ffs(dev->id_irq) - 1;
+
+ if (dev->id_iobase == -1) {
+ dev->id_iobase = 0x530;
+ printf("mss_probe: no address supplied, try default 0x%x\n",
+ dev->id_iobase);
+ }
+ if (snd_conflict(dev->id_iobase))
+ return 0 ;
+
+ if ( !(dev->id_flags & DV_F_TRUE_MSS) ) /* Has no IRQ/DMA registers */
+ goto mss_probe_end;
+
+ /*
+ * Check if the IO port returns valid signature. The original MS
+ * Sound system returns 0x04 while some cards
+ * (AudioTriX Pro for example) return 0x00 or 0x0f.
+ */
+
+ tmp = inb(dev->id_iobase + 3);
+ if (tmp == 0xff) { /* Bus float */
+ DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp));
+ dev->id_flags &= ~DV_F_TRUE_MSS ;
+ goto mss_probe_end;
+ }
+ tmp &= 0x3f ;
+ if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) {
+ DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n",
+ dev->id_iobase, inb(dev->id_iobase + 3)));
+ return 0;
+ }
+ if (irq > 11) {
+ printf("MSS: Bad IRQ %d\n", irq);
+ return 0;
+ }
+ if (dev->id_drq != 0 && dev->id_drq != 1 && dev->id_drq != 3) {
+ printf("MSS: Bad DMA %d\n", dev->id_drq);
+ return 0;
+ }
+ if (inb(dev->id_iobase + 3) & 0x80) {
+ /* 8-bit board: only drq1/3 and irq7/9 */
+ if (dev->id_drq == 0) {
+ printf("MSS: Can't use DMA0 with a 8 bit card/slot\n");
+ return 0;
+ }
+ if (irq != 7 && irq != 9) {
+ printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq);
+ return 0;
+ }
+ }
+mss_probe_end:
+ return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */
+}
+
+/*
+ * the address passed as io_base for mss_attach is also the old
+ * MSS base address (e.g. 0x530). The codec is four locations ahead.
+ * Note that the attach routine for PnP devices might support
+ * device-specific initializations.
+ */
+
+static int
+mss_attach(struct isa_device *dev)
+{
+ snddev_info *d = &(pcm_info[dev->id_unit]);
+
+ printf("mss_attach <%s>%d at 0x%x irq %d dma %d:%d flags 0x%x\n",
+ d->name, dev->id_unit,
+ d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags);
+
+ if ( dev->id_flags & DV_F_TRUE_MSS ) {
+ /* has IRQ/DMA registers, set IRQ and DMA addr */
+ static char interrupt_bits[12] = {
+ -1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20
+ };
+ static char dma_bits[4] = { 1, 2, 0, 3 };
+ char bits ;
+
+ if (d->irq == -1 || (bits = interrupt_bits[d->irq]) == -1) {
+ dev->id_irq = 0 ; /* makk invalid irq */
+ return 0 ;
+ }
+
+ outb(dev->id_iobase, bits | 0x40); /* config port */
+ if ((inb(dev->id_iobase + 3) & 0x40) == 0) /* version port */
+ printf("[IRQ Conflict?]");
+
+ /* Write IRQ+DMA setup */
+ if (d->dma1 == d->dma2) /* single chan dma */
+ outb(dev->id_iobase, bits | dma_bits[d->dma1]);
+ else {
+ if (d->dma1 == 0 && d->dma2 == 1)
+ bits |= 5 ;
+ else if (d->dma1 == 1 && d->dma2 == 0)
+ bits |= 6 ;
+ else if (d->dma1 == 3 && d->dma2 == 0)
+ bits |= 7 ;
+ else {
+ printf("invalid dual dma config %d:%d\n",
+ d->dma1, d->dma2);
+ dev->id_irq = 0 ;
+ return 0 ;
+ }
+ outb(dev->id_iobase, bits );
+ }
+ }
+ mss_reinit(d);
+ ad1848_mixer_reset(d);
+ return 0;
+}
+
+static int
+mss_open(dev_t dev, int flags, int mode, struct proc * p)
+{
+ int unit;
+ snddev_info *d;
+ u_long s;
+
+ dev = minor(dev);
+ unit = dev >> 4 ;
+ dev &= 0xf ;
+ d = &pcm_info[unit] ;
+
+ s = spltty();
+ /*
+ * This was meant to support up to 2 open descriptors for the
+ * some device, and check proper device usage on open.
+ * Unfortunately, the kernel will trap all close() calls but
+ * the last one, with the consequence that we cannot really
+ * keep track of which channels are busy.
+ * So, the correct tests cannot be done :( and we must rely
+ * on the locks on concurrent operations of the same type and
+ * on some approximate tests...
+ */
+
+ if (dev == SND_DEV_AUDIO)
+ d->flags |= SND_F_BUSY_AUDIO ;
+ else if (dev == SND_DEV_DSP)
+ d->flags |= SND_F_BUSY_DSP ;
+ else if (dev == SND_DEV_DSP16)
+ d->flags |= SND_F_BUSY_DSP16 ;
+ if ( ! (d->flags & SND_F_BUSY) ) {
+ /*
+ * device was idle. Do the necessary initialization,
+ * but no need keep interrupts blocked since this device
+ * will not get them
+ */
+
+ splx(s);
+ d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
+ d->flags |= SND_F_BUSY ;
+
+ d->wsel.si_pid = 0;
+ d->wsel.si_flags = 0;
+
+ d->rsel.si_pid = 0;
+ d->rsel.si_flags = 0;
+
+ d->esel.si_pid = 0;
+ d->esel.si_flags = 0;
+
+ if (flags & O_NONBLOCK)
+ d->flags |= SND_F_NBIO ;
+
+ switch (dev) {
+ default :
+ 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 ;
+ case SND_DEV_DSP16 :
+ d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
+ break;
+ }
+ reset_dbuf(& (d->dbuf_in) );
+ reset_dbuf(& (d->dbuf_out) );
+ ask_init(d);
+ }
+ splx(s);
+ return 0 ;
+}
+
+static int
+mss_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 ;
+ dev &= 0xf;
+ d = &pcm_info[unit] ;
+
+ /*
+ * We will only get a single close call when the last reference
+ * to the device is gone. But we must handle ourselves references
+ * through different devices.
+ */
+
+ s = spltty();
+
+ if (dev == SND_DEV_AUDIO)
+ d->flags &= ~SND_F_BUSY_AUDIO ;
+ else if (dev == SND_DEV_DSP)
+ d->flags &= ~SND_F_BUSY_DSP ;
+ else if (dev == SND_DEV_DSP16)
+ d->flags &= ~SND_F_BUSY_DSP16 ;
+ if ( ! (d->flags & SND_F_BUSY_ANY) ) { /* last one ... */
+ d->flags |= SND_F_CLOSING ;
+ splx(s); /* is this ok here ? */
+ snd_flush(d);
+ outb(io_Status(d), 0); /* Clear interrupt status */
+ d->flags = 0 ;
+ }
+ splx(s);
+ return 0 ;
+}
+
+static int
+mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
+{
+ snddev_info *d;
+ int unit;
+
+ 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) ) {
+ cmd &= 0xff ;
+ if (cmd == SOUND_MIXER_RECSRC)
+ return mss_set_recsrc(d, *(int *)arg) ;
+ else
+ return mss_mixer_set(d, cmd, *(int *)arg) ;
+ }
+
+ return ENOSYS ; /* fallback to the default ioctl handler */
+}
+
+
+/*
+ * the callback routine to handle all dma ops etc.
+ */
+
+static int
+mss_callback(snddev_info *d, int reason)
+{
+ u_long s;
+ u_char m;
+ int retry, wr, cnt;
+
+ DEB(printf("-- mss_callback reason 0x%03x\n", reason));
+ wr = reason & SND_CB_WR ;
+ reason &= SND_CB_REASON_MASK ;
+ switch (reason) {
+ case SND_CB_INIT : /* called with int enabled and no pending I/O */
+ /*
+ * perform all necessary initializations for i/o
+ */
+ d->rec_fmt = d->play_fmt ; /* no split format on the WSS */
+ snd_set_blocksize(d);
+ mss_reinit(d);
+ return 1 ;
+ break ;
+
+ case SND_CB_START :
+ /* fallthrough */
+ case SND_CB_RESTART :
+ s = spltty();
+ cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ;
+ if (d->play_fmt == AFMT_S16_LE)
+ cnt /= 2;
+ if (d->flags & SND_F_STEREO)
+ cnt /= 2;
+ cnt-- ;
+
+ DEB(printf("-- (re)start cnt %d\n", cnt));
+ m = ad_read(d,9) ;
+ DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); );
+ m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */
+ /*
+ * on the OPTi931 the enable bit seems hard to set...
+ */
+ for (retry = 10; retry; retry--) {
+ ad_write(d, 9, m );
+ if (ad_read(d,9) ==m) break;
+ }
+ if (retry == 0)
+ printf("start dma, failed to set bit 0x%02x 0x%02x\n",
+ m, ad_read(d, 9) ) ;
+ if (wr || (d->dma1 == d->dma2) )
+ ad_write_cnt(d, 14, cnt);
+ else
+ ad_write_cnt(d, 30, cnt);
+
+ splx(s);
+ break ;
+ case SND_CB_STOP :
+ case SND_CB_ABORT : /* XXX check this... */
+ s = spltty();
+ m = ad_read(d,9) ;
+ m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */
+ /*
+ * on the OPTi931 the enable bit seems hard to set...
+ */
+ for (retry = 10; retry ; retry-- ) {
+ ad_write(d, 9, m );
+ if (ad_read(d,9) ==m) break;
+ }
+ if (retry == 0)
+ printf("start dma, failed to clear bit 0x%02x 0x%02x\n",
+ m, ad_read(d, 9) ) ;
+
+ /* disable DMA by clearing count registers. */
+ if (wr || (d->dma1 == d->dma2) )
+ ad_write_cnt(d, 14, 0);
+ else
+ ad_write_cnt(d, 30, 0);
+ splx(s);
+ break;
+ }
+ return 0 ;
+}
+
+/*
+ * main irq handler for the CS423x. The OPTi931 code is
+ * a separate one.
+ */
+
+static void
+mss_intr(int unit)
+{
+ snddev_info *d = &pcm_info[unit];
+ u_char i11, c;
+ u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
+
+ i11 = ad_read(d, 11);
+ reason = inb(io_Status(d));
+
+ if ( ! (reason & 1) ) /* no int, maybe a shared line ? */
+ return;
+ /* get exact reason */
+ c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24);
+ if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) )
+ dsp_wrintr(d);
+ c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24);
+ if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) )
+ dsp_rdintr(d);
+ /* XXX check this on the 4236... */
+ if (d->dma1 == d->dma2)
+ outb(io_Status(d), 0); /* Clear interrupt status */
+ else
+ ad_write(d, 24, ~c); /* ack selectively */
+}
+
+/*
+ * the opti931 seems to miss interrupts when working in full
+ * duplex. god know why...
+ */
+static void
+opti931_intr(int unit)
+{
+ snddev_info *d = &pcm_info[unit];
+ u_char masked=0, i11, mc11, c=0;
+ u_char reason; /* b0 = playback, b1 = capture, b2 = timer */
+ int loops = 10;
+ int w_miss=0, r_miss=0;
+ static int misses=0; /* XXX kludge */
+
+#if 0
+ reason = inb(io_Status(d));
+ if ( ! (reason & 1) ) {/* no int, maybe a shared line ? */
+ printf("opti931_intr: flag 0, mcir11 0x%02x\n", ad_read(d,11));
+ return;
+ }
+#endif
+ i11 = ad_read(d, 11); /* XXX what's for ? */
+again:
+
+ c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11);
+ mc11 &= 0x0c ;
+ if (c & 0x10) {
+ printf("Warning CD interrupt\n");
+ mc11 |= 0x10 ;
+ }
+ if (c & 0x20) {
+ printf("Warning MPU interrupt\n");
+ mc11 |= 0x20 ;
+ }
+ if (mc11 & masked)
+ printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked);
+ masked |= mc11 ;
+ if ( mc11 == 0 ) { /* perhaps can return ... */
+ reason = inb(io_Status(d));
+ if (reason & 1) {
+ printf("one more try...\n");
+ goto again;
+ }
+ if (w_miss || r_miss ) {
+ misses++;
+ if ( (misses & 0xff) == 1 )
+ printf("opti931: %d missed irq (now %s%s)\n",
+ misses, w_miss?"play ":"",
+ r_miss?"capture ":"");
+ }
+ if (loops==10)
+ printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11);
+ if (w_miss) mc11 |= 4 ;
+ if (r_miss) mc11 |= 8 ;
+ if (mc11==0)
+ return;
+ }
+
+ if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) {
+ dsp_rdintr(d);
+ r_miss = 0 ;
+ }
+ else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) )
+ r_miss = 1 ;
+
+ if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) {
+ if (isa_dmastatus1(d->dma1) != 0)
+ printf("opti931_intr: wr dma %d\n",
+ isa_dmastatus1(d->dma1));
+ dsp_wrintr(d);
+ w_miss = 0 ;
+ }
+ else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) )
+ w_miss = 1 ;
+
+ opti_write(d->conf_base, 11, ~mc11); /* ack */
+ if (--loops) goto again;
+ printf("xxx too many loops\n");
+}
+
+/*
+ * Second part of the file: functions local to this module.
+ * in this section a few routines to access MSS registers
+ *
+ */
+
+static void
+opti_write(int io_base, u_char reg, u_char value)
+{
+ outb(io_base, reg);
+ outb(io_base+1, value);
+}
+
+static u_char
+opti_read(int io_base, u_char reg)
+{
+ outb(io_base, reg);
+ return inb(io_base+1);
+}
+/*
+ * AD_WAIT_INIT waits if we are initializing the board and
+ * we cannot modify its settings
+ */
+static int
+AD_WAIT_INIT(snddev_info *d, int x)
+{
+ int n = 0; /* to shut up the compiler... */
+ for (; x-- ; )
+ if ( (n=inb(io_Index_Addr(d))) & IA_BUSY)
+ DELAY(10);
+ else
+ return n ;
+ printf("AD_WAIT_INIT FAILED 0x%02x\n", n);
+ return n ;
+}
+
+static int
+ad_read(snddev_info *d, int reg)
+{
+ u_long flags;
+ int x;
+
+ flags = spltty();
+ AD_WAIT_INIT(d, 100);
+ x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
+ outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
+ x = inb(io_Indexed_Data(d));
+ splx(flags);
+ return x;
+}
+
+static void
+ad_write(snddev_info *d, int reg, u_char data)
+{
+ u_long flags;
+
+ int x ;
+ flags = spltty();
+ AD_WAIT_INIT(d, 100);
+ x = inb(io_Index_Addr(d)) & ~IA_AMASK ;
+ outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ;
+ outb(io_Indexed_Data(d), data);
+ splx(flags);
+}
+
+static void
+ad_write_cnt(snddev_info *d, int reg, u_short cnt)
+{
+ ad_write(d, reg+1, cnt & 0xff );
+ ad_write(d, reg, cnt >> 8 ); /* upper base must be last */
+}
+
+static void
+wait_for_calibration(snddev_info *d)
+{
+ int n, t;
+
+ /*
+ * Wait until the auto calibration process has finished.
+ *
+ * 1) Wait until the chip becomes ready (reads don't return 0x80).
+ * 2) Wait until the ACI bit of I11 gets on
+ * 3) Wait until the ACI bit of I11 gets off
+ */
+
+ n = AD_WAIT_INIT(d, 1000);
+ if (n & IA_BUSY)
+ printf("mss: Auto calibration timed out(1).\n");
+
+ for (t = 100 ; t>0 && (ad_read(d, 11) & 0x20) == 0 ; t--)
+ DELAY(100);
+ for (t = 100 ; t>0 && ad_read(d, 11) & 0x20 ; t--)
+ DELAY(100);
+}
+
+#if 0 /* unused right now... */
+static void
+ad_mute(snddev_info *d)
+{
+ ad_write(d, 6, ad_read(d,6) | I6_MUTE);
+ ad_write(d, 7, ad_read(d,7) | I6_MUTE);
+}
+
+static void
+ad_unmute(snddev_info *d)
+{
+ ad_write(d, 6, ad_read(d,6) & ~I6_MUTE);
+ ad_write(d, 7, ad_read(d,7) & ~I6_MUTE);
+}
+#endif
+
+static void
+ad_enter_MCE(snddev_info *d)
+{
+ int prev;
+
+ d->bd_flags |= BD_F_MCE_BIT;
+ AD_WAIT_INIT(d, 100);
+ prev = inb(io_Index_Addr(d));
+ outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ;
+}
+
+static void
+ad_leave_MCE(snddev_info *d)
+{
+ u_long flags;
+ u_char prev;
+
+ if ( (d->bd_flags & BD_F_MCE_BIT) == 0 ) {
+ printf("--- hey, leave_MCE: MCE bit was not set!\n");
+ return;
+ }
+
+ AD_WAIT_INIT(d, 1000);
+
+ flags = spltty();
+ d->bd_flags &= ~BD_F_MCE_BIT;
+
+ prev = inb(io_Index_Addr(d));
+
+ outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */
+ wait_for_calibration(d);
+ splx(flags);
+}
+
+/*
+ * only one source can be set...
+ *
+ * fixed -- lr 970725
+ */
+static int
+mss_set_recsrc(snddev_info *d, int mask)
+{
+ u_char recdev;
+
+ mask &= d->mix_rec_devs;
+ switch (mask) {
+ case SOUND_MASK_LINE:
+ case SOUND_MASK_LINE3:
+ recdev = 0;
+ break;
+
+ case SOUND_MASK_CD:
+ case SOUND_MASK_LINE1:
+ recdev = 0x40;
+ break;
+
+ case SOUND_MASK_IMIX:
+ recdev = 0xc0;
+ break;
+
+ case SOUND_MASK_MIC:
+ default:
+ mask = SOUND_MASK_MIC;
+ recdev = 0x80;
+ }
+
+ ad_write(d, 0, (ad_read(d, 0) & 0x3f) | recdev);
+ ad_write(d, 1, (ad_read(d, 1) & 0x3f) | recdev);
+
+ d->mix_recsrc = mask;
+ return 0;
+}
+
+/*
+ * mixer conversion table: from 0..100 scale to codec values
+ *
+ * I don't understand what's this for... maybe achieve a log-scale
+ * volume control ?
+ */
+
+static char mix_cvt[101] = {
+ 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+ 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+ 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+ 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+ 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+ 100
+};
+
+/*
+ * there are differences in the mixer depending on the actual sound
+ * card.
+ */
+static int
+mss_mixer_set(snddev_info *d, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+
+ int regoffs;
+ mixer_tab *mix_d = &mix_devices;
+
+ u_char old, val;
+
+ if (dev > 31)
+ return EINVAL;
+
+ if (!(d->mix_devs & (1 << dev)))
+ return EINVAL;
+
+ if (d->bd_id == MD_OPTI931)
+ mix_d = &(opti931_devices);
+
+ if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
+ DDB(printf("nbits = 0 for dev %d\n", dev) );
+ return EINVAL;
+ }
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+
+ if ( (*mix_d)[dev][RIGHT_CHN].nbits == 0) /* Mono control */
+ right = left;
+
+ d->mix_levels[dev] = left | (right << 8);
+
+ /* Scale volumes */
+ left = mix_cvt[left];
+ right = mix_cvt[right];
+
+ /*
+ * Set the left channel
+ */
+
+ regoffs = (*mix_d)[dev][LEFT_CHN].regno;
+ old = val = ad_read(d, regoffs);
+ if (regoffs != 0)
+ val = old & 0x7f ; /* clear mute bit. */
+ change_bits(mix_d, &val, dev, LEFT_CHN, left);
+ ad_write(d, regoffs, val);
+ DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n",
+ dev, regoffs, old, val));
+
+ if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */
+ /*
+ * Set the right channel
+ */
+ regoffs = (*mix_d)[dev][RIGHT_CHN].regno;
+ val = ad_read(d, regoffs);
+ if (regoffs != 1)
+ val = old & 0x7f ; /* clear mute bit. */
+ change_bits(mix_d, &val, dev, RIGHT_CHN, right);
+ ad_write(d, regoffs, val);
+ }
+ return 0; /* success */
+}
+
+static void
+ad1848_mixer_reset(snddev_info *d)
+{
+ int i;
+
+ if (d->bd_id == MD_OPTI931)
+ d->mix_devs = OPTI931_MIXER_DEVICES;
+ else if (d->bd_id != MD_AD1848)
+ d->mix_devs = MODE2_MIXER_DEVICES;
+ else
+ d->mix_devs = MODE1_MIXER_DEVICES;
+
+ d->mix_rec_devs = MODE1_REC_DEVICES;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (d->mix_devs & (1 << i))
+ mss_mixer_set(d, i, default_mixer_levels[i]);
+ mss_set_recsrc(d, SOUND_MASK_MIC);
+}
+
+/*
+ * mss_speed processes the value in play_speed finding the
+ * matching one. As a side effect, it returns the value to
+ * be written in the speed bits of the codec. It does _NOT_
+ * set the speed of the device (but it should!)
+ *
+ * fixed lr970724
+ */
+
+static int
+mss_speed(snddev_info *d)
+{
+ /*
+ * In the CS4231, the low 4 bits of I8 are used to hold the
+ * sample rate. Only a fixed number of values is allowed. This
+ * table lists them. The speed-setting routines scans the table
+ * looking for the closest match. This is the only supported method.
+ *
+ * In the CS4236, there is an alternate metod (which we do not
+ * support yet) which provides almost arbitrary frequency setting.
+ * In the AD1845, it looks like the sample rate can be
+ * almost arbitrary, and written directly to a register.
+ * In the OPTi931, there is a SB command which provides for
+ * almost arbitrary frequency setting.
+ *
+ */
+ static int speeds[] = {
+ 8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050,
+ -1, 37800, -1, 44100, 48000, 33075, 9600, 6615
+ };
+
+ int arg, i, sel = 0; /* assume entry 0 does not contain -1 */
+
+ arg = d->play_speed ;
+
+ for (i=1; i < 16 ; i++)
+ if (speeds[i] >0 && abs(arg-speeds[i]) < abs(arg-speeds[sel]) )
+ sel = i ;
+
+ d->play_speed = d->rec_speed = speeds[sel] ;
+ return sel ;
+}
+
+/*
+ * mss_format checks that the format is supported (or defaults to AFMT_U8)
+ * and returns the bit setting for the 1848 register corresponding to
+ * the desired format.
+ *
+ * fixed lr970724
+ */
+
+static int
+mss_format(snddev_info *d)
+{
+ int i, arg = d->play_fmt ;
+
+ /*
+ * The data format uses 3 bits (just 2 on the 1848). For each
+ * bit setting, the following array returns the corresponding format.
+ * The code scans the array looking for a suitable format. In
+ * case it is not found, default to AFMT_U8 (not such a good
+ * choice, but let's do it for compatibility...).
+ */
+
+ static int fmts[] = {
+ AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW,
+ -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1
+ };
+
+ if ( (arg & d->audio_fmt) == 0 ) /* unsupported fmt, default to AFMT_U8 */
+ arg = AFMT_U8 ;
+
+ /* ulaw/alaw seems broken on the opti931... */
+ if (d->bd_id == MD_OPTI931) {
+ if (arg == AFMT_MU_LAW) {
+ arg = AFMT_U8 ;
+ d->flags |= SND_F_XLAT8 ;
+ } else
+ d->flags &= ~SND_F_XLAT8 ;
+ }
+ /*
+ * check that arg is one of the supported formats in d->format;
+ * otherwise fallback to AFMT_U8
+ */
+
+ for (i=0 ; i<8 ; i++)
+ if (arg == fmts[i]) break;
+ if (i==8) { /* not found, default to AFMT_U8 */
+ arg = AFMT_U8 ;
+ i = 0 ;
+ }
+ d->play_fmt = d->rec_fmt = arg;
+
+ return i ;
+}
+
+/*
+ * mss_detect can be used in the probe and the attach routine.
+ *
+ * We store probe information in pcm_info[unit]. This descriptor
+ * is reinitialized just before the attach, so all relevant
+ * information is lost, and mss_detect must be run again in
+ * the attach routine if necessary.
+ */
+
+int
+mss_detect(struct isa_device *dev)
+{
+ int i;
+ u_char tmp, tmp1, tmp2 ;
+ snddev_info *d = &(pcm_info[dev->id_unit]);
+ char *name;
+
+ d->io_base = dev->id_iobase;
+ d->bd_flags |= BD_F_MCE_BIT ;
+ if (d->bd_id != 0) {
+ printf("preselected bd_id 0x%04x -- %s\n",
+ d->bd_id, d->name ? d->name : "???");
+ return 1;
+ }
+
+ name = "AD1848" ;
+ d->bd_id = MD_AD1848; /* AD1848 or CS4248 */
+
+ /*
+ * Check that the I/O address is in use.
+ *
+ * bit 7 of the base I/O port is known to be 0 after the chip has
+ * performed it's power on initialization. Just assume this has
+ * happened before the OS is starting.
+ *
+ * If the I/O address is unused, it typically returns 0xff.
+ */
+
+ for (i=0; i<10; i++)
+ if (inb(io_Index_Addr(d)) & IA_BUSY)
+ DELAY(10000); /* maybe busy, wait & retry later */
+ else
+ break ;
+ if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */
+ DDB(printf("mss_detect error, busy still set (0x%02x)\n",
+ inb(io_Index_Addr(d))));
+ return 0;
+ }
+ /*
+ * Test if it's possible to change contents of the indirect
+ * registers. Registers 0 and 1 are ADC volume registers. The bit
+ * 0x10 is read only so try to avoid using it.
+ */
+
+ ad_write(d, 0, 0xaa);
+ ad_write(d, 1, 0x45);/* 0x55 with bit 0x10 clear */
+ tmp1 = ad_read(d, 0) ;
+ tmp2 = ad_read(d, 1) ;
+ if ( tmp1 != 0xaa || tmp2 != 0x45) {
+ DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n",
+ tmp1, tmp2));
+ return 0;
+ }
+
+ ad_write(d, 0, 0x45);
+ ad_write(d, 1, 0xaa);
+ tmp1 = ad_read(d, 0) ;
+ tmp2 = ad_read(d, 1) ;
+
+ if (tmp1 != 0x45 || tmp2 != 0xaa) {
+ DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ /*
+ * The indirect register I12 has some read only bits. Lets try to
+ * change them.
+ */
+
+ tmp = ad_read(d, 12);
+ ad_write(d, 12, (~tmp) & 0x0f);
+ tmp1 = ad_read(d, 12);
+
+ if ((tmp & 0x0f) != (tmp1 & 0x0f)) {
+ DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n",
+ tmp1, tmp));
+ return 0;
+ }
+
+ /*
+ * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+ * 0x01=RevB
+ * 0x0A=RevC. also CS4231/CS4231A and OPTi931
+ */
+
+ printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);
+
+ /*
+ * The original AD1848/CS4248 has just 16 indirect registers. This
+ * means that I0 and I16 should return the same value (etc.). Ensure
+ * that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+ * with new parts.
+ */
+
+ ad_write(d, 12, 0); /* Mode2=disabled */
+
+ for (i = 0; i < 16; i++)
+ if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) {
+ DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n",
+ i, tmp1, tmp2));
+ /*
+ * note - this seems to fail on the 4232 on I11. So we just break
+ * rather than fail.
+ */
+ break ; /* return 0; */
+ }
+ /*
+ * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit
+ * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231.
+ *
+ * On the OPTi931, however, I12 is readonly and only contains the
+ * chip revision ID (as in the CS4231A). The upper bits return 0.
+ */
+
+ ad_write(d, 12, 0x40); /* Set mode2, clear 0x80 */
+
+ tmp1 = ad_read(d, 12);
+ if (tmp1 & 0x80) {
+ name = "CS4248" ; /* Our best knowledge just now */
+ }
+ if ((tmp1 & 0xf0) == 0x00) {
+ printf("this should be an OPTi931\n");
+ } else if ((tmp1 & 0xc0) == 0xC0) {
+ /*
+ * The 4231 has bit7=1 always, and bit6 we just set to 1.
+ * We want to check that this is really a CS4231
+ * Verify that setting I0 doesn't change I16.
+ */
+ ad_write(d, 16, 0); /* Set I16 to known value */
+
+ ad_write(d, 0, 0x45);
+ if ((tmp1 = ad_read(d, 16)) != 0x45) { /* No change -> CS4231? */
+
+ ad_write(d, 0, 0xaa);
+ if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */
+ DDB(printf("mss_detect error - step H(%x)\n", tmp1));
+ return 0;
+ }
+ /*
+ * Verify that some bits of I25 are read only.
+ */
+
+ DDB(printf("mss_detect() - step I\n"));
+ tmp1 = ad_read(d, 25); /* Original bits */
+ ad_write(d, 25, ~tmp1); /* Invert all bits */
+ if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) {
+ int id;
+
+ /*
+ * It's at least CS4231
+ */
+ name = "CS4231" ;
+ d->bd_id = MD_CS4231;
+
+ /*
+ * It could be an AD1845 or CS4231A as well.
+ * CS4231 and AD1845 report the same revision info in I25
+ * while the CS4231A reports different.
+ */
+
+ id = ad_read(d, 25) & 0xe7;
+ /*
+ * b7-b5 = version number;
+ * 100 : all CS4231
+ * 101 : CS4231A
+ *
+ * b2-b0 = chip id;
+ */
+ switch (id) {
+
+ case 0xa0:
+ name = "CS4231A" ;
+ d->bd_id = MD_CS4231A;
+ break;
+
+ case 0xa2:
+ name = "CS4232" ;
+ d->bd_id = MD_CS4232;
+ break;
+
+ case 0xb2:
+ /* strange: the 4231 data sheet says b4-b3 are XX
+ * so this should be the same as 0xa2
+ */
+ name = "CS4232A" ;
+ d->bd_id = MD_CS4232A;
+ break;
+
+ case 0x80:
+ /*
+ * It must be a CS4231 or AD1845. The register I23
+ * of CS4231 is undefined and it appears to be read
+ * only. AD1845 uses I23 for setting sample rate.
+ * Assume the chip is AD1845 if I23 is changeable.
+ */
+
+ tmp = ad_read(d, 23);
+
+ ad_write(d, 23, ~tmp);
+ if (ad_read(d, 23) != tmp) { /* AD1845 ? */
+ name = "AD1845" ;
+ d->bd_id = MD_AD1845;
+ }
+ ad_write(d, 23, tmp); /* Restore */
+ break;
+
+ case 0x83: /* CS4236 */
+ name = "CS4236";
+ d->bd_id = MD_CS4236;
+ break ;
+
+ default: /* Assume CS4231 */
+ printf("unknown id 0x%02x, assuming CS4231\n", id);
+ d->bd_id = MD_CS4231;
+
+ }
+ }
+ ad_write(d, 25, tmp1); /* Restore bits */
+
+ }
+ }
+ DDB(printf("mss_detect() - Detected %s\n", name));
+ strcpy(d->name, name);
+ dev->id_flags &= ~DV_F_DEV_MASK ;
+ dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ;
+ return 1;
+}
+
+
+/*
+ * mss_reinit resets registers of the codec
+ */
+static void
+mss_reinit(snddev_info *d)
+{
+ u_char r;
+
+ r = mss_speed(d) ;
+ r |= (mss_format(d) << 5) ;
+ if (d->flags & SND_F_STEREO)
+ r |= 0x10 ;
+ /* XXX check if MCE is necessary... */
+ ad_enter_MCE(d);
+
+ /*
+ * perhaps this is not the place to set mode2, should be done
+ * only once at attach time...
+ */
+ if ( d->dma1 != d->dma2 && d->bd_id != MD_OPTI931)
+ /*
+ * set mode2 bit for dual dma op. This bit is not implemented
+ * on the OPTi931
+ */
+ ad_write(d, 12, ad_read(d, 12) | 0x40 /* mode 2 on the CS42xx */ );
+
+ /*
+ * XXX this should really go into mss-speed...
+ */
+ if (d->bd_id == MD_AD1845) { /* Use alternate speed select regs */
+ r &= 0xf0; /* Mask off the rate select bits */
+
+ ad_write(d, 22, (d->play_speed >> 8) & 0xff); /* Speed MSB */
+ ad_write(d, 23, d->play_speed & 0xff); /* Speed LSB */
+ /*
+ * XXX must also do something in I27 for the ad1845
+ */
+ }
+
+ ad_write(d, 8, r) ;
+ if (d->dma1 != d->dma2) {
+ ad_write(d, 28, r & 0xf0 ) ; /* capture mode */
+ ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ;
+ } else
+ ad_write(d, 9, 4 /* no capture, no playback, single dma */) ;
+ ad_leave_MCE(d);
+ ad_write_cnt(d, 14, 0 ); /* playback count */
+ if (d->dma1 != d->dma2)
+ ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */
+
+ ad_write(d, 10, 2 /* int enable */) ;
+ outb(io_Status(d), 0); /* Clear interrupt status */
+ /* the following seem required on the CS4232 */
+ ad_write(d, 6, ad_read(d,6) & ~I6_MUTE);
+ ad_write(d, 7, ad_read(d,7) & ~I6_MUTE);
+
+ snd_set_blocksize(d); /* update blocksize if user did not force it */
+}
+
+/*
+ * here we have support for PnP cards
+ *
+ */
+
+#if NPNP > 0
+
+static char * cs4236_probe(u_long csn, u_long vend_id);
+static void cs4236_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device cs4236 = {
+ "cs423x",
+ cs4236_probe,
+ cs4236_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, cs4236);
+
+static char *
+cs4236_probe(u_long csn, u_long vend_id)
+{
+ char *s = NULL ;
+ if (vend_id == 0x3742630e)
+ s = "CS4237" ;
+ else if (vend_id == 0x3642630e)
+ s = "CS4236" ;
+ else if (vend_id == 0x3242630e)
+ s = "CS4232" ;
+ if (s) {
+ struct pnp_cinfo d;
+ read_pnp_parms(&d, 0);
+ if (d.enable == 0) {
+ printf("This is a %s, but LDN 0 is disabled\n", s);
+ return NULL ;
+ }
+ return s;
+ }
+
+ return NULL ;
+}
+
+extern snddev_info sb_op_desc;
+
+static void
+cs4236_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ int ldn = 0 ;
+
+ if (read_pnp_parms ( &d , ldn ) == 0 ) {
+ printf("failed to read pnp parms\n");
+ return ;
+ }
+ snddev_last_probed = &tmp_d;
+#if 0
+ /* sb-compatible codec */
+ dev->id_iobase = d.port[2] ;
+ tmp_d = sb_op_desc ;
+ tmp_d.alt_base = d.port[0] - 4;
+ d.drq[1] = 4 ; /* disable, it is not used ... */
+ d.drq[0] = 0 ; /* remap ... */
+#else
+ /* mss-compatible codec */
+ dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */
+ tmp_d = mss_op_desc ;
+ tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
+ tmp_d.alt_base = d.port[2];
+ strcpy(tmp_d.name, name);
+ tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
+#endif
+ write_pnp_parms( &d, ldn );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
+
+ pcm_info[dev->id_unit] = tmp_d; /* during the probe... */
+ snddev_last_probed->probe(dev);
+
+ pcmattach(dev);
+}
+
+static char *opti931_probe(u_long csn, u_long vend_id);
+static void opti931_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+static struct pnp_device opti931 = {
+ "OPTi931",
+ opti931_probe,
+ opti931_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, opti931);
+
+static char *
+opti931_probe(u_long csn, u_long vend_id)
+{
+ if (vend_id == 0x3109143e) {
+ struct pnp_cinfo d;
+ read_pnp_parms(&d, 1);
+ if (d.enable == 0) {
+ printf("This is an OPTi931, but LDN 1 is disabled\n");
+ return NULL ;
+ }
+ return "OPTi931" ;
+ }
+ return NULL ;
+}
+
+static void
+opti931_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ int p;
+ int sb_mode = 0 ; /* XXX still not work in SB mode */
+
+ read_pnp_parms ( &d , 3 ); /* free resources taken by LDN 3 */
+ d.irq[0]=0; /* free irq... */
+ d.port[0]=0; /* free address... */
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 3 );
+
+ read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 2 );
+
+ read_pnp_parms ( &d , 1 ) ;
+ write_pnp_parms( &d, 1 );
+ enable_pnp_card();
+
+ snddev_last_probed = &tmp_d;
+ tmp_d = sb_mode ? sb_op_desc : mss_op_desc ;
+
+ strcpy(tmp_d.name, name);
+
+ /*
+ * My MED3931 v.1.0 allocates 3 bytes for the config space,
+ * whereas v.2.0 allocates 4 bytes. What I know for sure is that the
+ * upper two ports must be used, and they should end on a boundary
+ * of 4 bytes. So I need the following trick...
+ */
+ p = tmp_d.conf_base = (d.port[3] & ~3) + 2; /* config port */
+
+ /*
+ * now set default values for both modes.
+ */
+ dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */
+ tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */
+ tmp_d.alt_base = d.port[2];
+ opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ );
+ ad_write (&tmp_d, 10, 2); /* enable interrupts */
+
+ if (sb_mode) { /* sb-compatible codec */
+ /*
+ * the 931 is not a real SB, it has important pieces of
+ * hardware controlled by both the WSS and the SB port...
+ */
+ opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */
+ /*
+ * swap the main and alternate iobase address since we want
+ * to work in sb mode.
+ */
+ dev->id_iobase = d.port[2] ;
+ tmp_d.alt_base = d.port[0] - 4;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+ } else { /* mss-compatible codec */
+ tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */
+ opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */
+#if 0 /* not working yet... */
+ opti_write(p, 5, 0x0 /* codec in single mode */ );
+ dev->id_flags = 0 ;
+#else
+ opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+#endif
+ tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */
+ tmp_d.isr = opti931_intr;
+ }
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ pcmattach(dev);
+}
+
+static char *guspnp_probe(u_long csn, u_long vend_id);
+static void guspnp_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+static struct pnp_device guspnp = {
+ "GusPnP",
+ guspnp_probe,
+ guspnp_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, guspnp);
+
+static char *
+guspnp_probe(u_long csn, u_long vend_id)
+{
+ if (vend_id == 0x0100561e) {
+ struct pnp_cinfo d;
+ read_pnp_parms(&d, 0);
+ if (d.enable == 0) {
+ printf("This is a GusPnP, but LDN 0 is disabled\n");
+ return NULL ;
+ }
+ return "GusPnP" ;
+ }
+ return NULL ;
+}
+
+static void
+guspnp_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+
+ read_pnp_parms ( &d , 0 ) ;
+ write_pnp_parms ( &d , 0 );
+ enable_pnp_card();
+
+ tmp_d = mss_op_desc ;
+ snddev_last_probed = &tmp_d;
+
+ dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ;
+
+ tmp_d.alt_base = d.port[0];
+
+ pcmattach(dev);
+}
+#endif /* NPNP > 0 */
+#endif /* NPCM > 0 */
diff --git a/sys/i386/isa/snd/clones.c b/sys/i386/isa/snd/clones.c
new file mode 100644
index 0000000..edb64f6
--- /dev/null
+++ b/sys/i386/isa/snd/clones.c
@@ -0,0 +1,262 @@
+/*
+ * sound/clones.c
+ *
+ * init code for enabling clone cards to work in sb/mss emulation.
+ *
+ * Note -- this code is currently unused!
+ *
+ * Copyright by Luigi Rizzo - 1997
+ *
+ * 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.
+ *
+ * This file has been written using information from various sources
+ * in the Voxware 3.5 distribution.
+ */
+
+#include <i386/isa/snd/sound.h>
+#if NPCM > 0
+
+/*
+ * Known clones card include:
+ *
+ * Trix (emulating MSS)
+ * MAD16 (emulating MSS)
+ * OPTi930 -- same as the OPTi931, but no PnP ?
+ */
+
+
+#ifdef JAZZ16
+
+/*
+ * Initialization of a Media Vision ProSonic 16 Soundcard. The function
+ * initializes a ProSonic 16 like PROS.EXE does for DOS. It sets the base
+ * address, the DMA-channels, interrupts and enables the joystickport.
+ *
+ * Also used by Jazz 16 (same card, different name)
+ *
+ * written 1994 by Rainer Vranken E-Mail:
+ * rvranken@polaris.informatik.uni-essen.de
+ */
+
+#ifdef SM_WAVE
+/*
+ * Logitech Soundman Wave detection and initialization by Hannu Savolainen.
+ *
+ * There is a microcontroller (8031) in the SM Wave card for MIDI emulation.
+ * it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific
+ * control register for MC reset, SCSI, OPL4 and DSP (future expansion)
+ * address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16 based
+ * soundcard.
+ */
+
+static void
+smw_putmem(int base, int addr, u_char val)
+{
+ u_long s;
+
+ s = spltty();
+
+ outb(base + 1, addr & 0xff); /* Low address bits */
+ outb(base + 2, addr >> 8); /* High address bits */
+ outb(base, val); /* Data */
+
+ splx(s);
+}
+
+static u_char
+smw_getmem(int base, int addr)
+{
+ u_long s;
+ u_char val;
+
+ s = spltty();
+
+ outb(base + 1, addr & 0xff); /* Low address bits */
+ outb(base + 2, addr >> 8); /* High address bits */
+ val = inb(base); /* Data */
+
+ splx(s);
+ return val;
+}
+
+#ifdef SMW_MIDI0001_INCLUDED
+#include </sys/i386/isa/snd/smw-midi0001.h>
+#else
+u_char *smw_ucode = NULL;
+int smw_ucodeLen = 0;
+#endif /* SWM_MIDI0001_INCLUDED */
+
+static int
+initialize_smw(int mpu_base)
+{
+
+ int i, mp_base = mpu_base + 4; /* Microcontroller base */
+ u_char control;
+
+ /*
+ * Reset the microcontroller so that the RAM can be accessed
+ */
+
+ control = inb(mpu_base + 7);
+ outb(mpu_base + 7, control | 3); /* Set last two bits to 1 (?) */
+ outb(mpu_base + 7, (control & 0xfe) | 2); /* xxxxxxx0 resets the mc */
+ DELAY(3000); /* Wait at least 1ms */
+
+ outb(mpu_base + 7, control & 0xfc); /* xxxxxx00 enables RAM */
+
+ /*
+ * Detect microcontroller by probing the 8k RAM area
+ */
+ smw_putmem(mp_base, 0, 0x00);
+ smw_putmem(mp_base, 1, 0xff);
+ DELAY(10);
+
+ if (smw_getmem(mp_base, 0) != 0x00 || smw_getmem(mp_base, 1) != 0xff) {
+ printf("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
+ smw_getmem(mp_base, 0), smw_getmem(mp_base, 1));
+ return 0; /* No RAM */
+ }
+ /*
+ * There is RAM so assume it's really a SM Wave
+ */
+
+ if (smw_ucodeLen > 0) {
+ if (smw_ucodeLen != 8192) {
+ printf("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
+ return 1;
+ }
+ /*
+ * Download microcode
+ */
+
+ for (i = 0; i < 8192; i++)
+ smw_putmem(mp_base, i, smw_ucode[i]);
+
+ /*
+ * Verify microcode
+ */
+
+ for (i = 0; i < 8192; i++)
+ if (smw_getmem(mp_base, i) != smw_ucode[i]) {
+ printf("SM Wave: Microcode verification failed\n");
+ return 0;
+ }
+ }
+ control = 0;
+#ifdef SMW_SCSI_IRQ
+ /*
+ * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
+ * is disabled by default.
+ *
+ * Btw the Zilog 5380 SCSI controller is located at MPU base + 0x10.
+ */
+ {
+ static u_char scsi_irq_bits[] =
+ {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
+
+ control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
+ }
+#endif
+
+#ifdef SMW_OPL4_ENABLE
+ /*
+ * Make the OPL4 chip visible on the PC bus at 0x380.
+ *
+ * There is no need to enable this feature since VoxWare doesn't support
+ * OPL4 yet. Also there is no RAM in SM Wave so enabling OPL4 is
+ * pretty useless.
+ */
+ control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
+ /* control |= 0x20; Uncomment this if you want to use IRQ7 */
+#endif
+
+ outb(mpu_base + 7, control | 0x03); /* xxxxxx11 restarts */
+ return 1;
+}
+
+#endif
+
+/*
+ * this is only called during the probe. Variables are found in
+ * sb_probed.
+ */
+static sbdev_info sb_probed ;
+static int
+initialize_ProSonic16(snddev_info *d)
+{
+ int x;
+ static u_char int_translat[16] =
+ {0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6},
+ dma_translat[8] =
+ {0, 1, 0, 2, 0, 3, 0, 4};
+
+ struct address_info *mpu_config;
+
+ int mpu_base, mpu_irq;
+
+ if ((mpu_config = NULL)) {
+ mpu_base = mpu_config->io_base;
+ mpu_irq = mpu_config->irq;
+ } else {
+ mpu_base = mpu_irq = 0;
+ }
+
+ outb(0x201, 0xAF); /* ProSonic/Jazz16 wakeup */
+ DELAY(15000); /* wait at least 10 milliseconds */
+ outb(0x201, 0x50);
+ outb(0x201, (sb_probed.io_base & 0x70) | ((mpu_base & 0x30) >> 4));
+
+ if (sb_reset_dsp(sb_probed.io_base)) { /* OK. We have at least a SB */
+
+ /* Check the version number of ProSonic (I guess) */
+
+ if (!sb_cmd(sb_probed.io_base, 0xFA))
+ return 1;
+ if (sb_get_byte(sb_probed.io_base) != 0x12)
+ return 1;
+
+ if (sb_cmd(sb_probed.io_base, 0xFB) && /* set DMA and irq */
+ sb_cmd(sb_probed.io_base,
+ (dma_translat[JAZZ_DMA16]<<4)|dma_translat[sb_probed.dma1]) &&
+ sb_cmd(sb_probed.io_base,
+ (int_translat[mpu_irq]<<4)|int_translat[sb_probed.irq])) {
+ d->bf_flags |= BD_F_JAZZ16 ;
+ if (mpu_base == 0)
+ printf("Jazz16: No MPU401 devices configured "
+ "- MIDI port not initialized\n");
+
+#ifdef SM_WAVE
+ if (mpu_base != 0)
+ if (initialize_smw(mpu_base))
+ d->bf_flags |= BD_F_JAZZ16_2 ;
+#endif
+ /* sb_dsp_disable_midi(); */
+ }
+ return 1; /* There was at least a SB */
+ }
+ return 0; /* No SB or ProSonic16 detected */
+}
+
+#endif /* ifdef JAZZ16 */
+
+#endif /* NPCM */
diff --git a/sys/i386/isa/snd/dmabuf.c b/sys/i386/isa/snd/dmabuf.c
new file mode 100644
index 0000000..d4ac55e
--- /dev/null
+++ b/sys/i386/isa/snd/dmabuf.c
@@ -0,0 +1,864 @@
+/*
+ * snd/dmabuf.c
+ *
+ * New DMA routines -- Luigi Rizzo, 19 jul 97
+ * This file implements the new DMA routines for the sound driver.
+ *
+ * Copyright by Luigi Rizzo - 1997
+ *
+ * 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.
+ *
+ */
+
+#include <i386/isa/snd/sound.h>
+#include <i386/isa/snd/ulaw.h>
+
+#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
+#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
+#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
+#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
+
+static void dsp_wr_dmadone(snddev_info *d);
+static void dsp_rd_dmadone(snddev_info *d);
+
+/*
+ *
+
+SOUND OUTPUT
+
+We use a circular buffer to store samples directed to the DAC.
+The buffer is split into three variable-size regions, each identified
+by an offset in the buffer (dp,rp,fp) and a lenght (dl,rl,fl).
+
+ 0 dp,dl rp,rl fp,fl bufsize
+ |__________|_____>______|_____________|________|
+ FREE DMA READY FREE
+
+ READY region: contains data written from the process and ready
+ to be sent to the DAC;
+
+ FREE region: is the empty region of the buffer, where a process
+ can write new data.
+
+ DMA region: contains data being sent to the DAC by the DMA engine.
+ the actual boundary between the FREE and READY regions is in
+ fact within the DMA region (indicated by the > symbol above),
+ and advances as the DMA engine makes progress.
+
+Both the "READY" and "FREE" regions can wrap around the end of the
+buffer. The "DMA" region can only wrap if AUTO DMA is used, otherwise
+it cannot cross the end of the buffer.
+
+Since dl can be updated on the fly, dl0 marks the value used when the
+operation was started. When using AUTO DMA, bufsize-(count in the ISA DMA
+register) directly reflects the position of dp.
+
+At initialization, DMA and READY are empty, and FREE takes all the
+available space:
+
+ dp = rp = fp = 0 ; -- beginning of buffer
+ dl0 = dl = 0 ; -- meaning no dma activity
+ rl = 0 ; -- meaning no data ready
+ fl = bufsize ;
+
+The DMA region is also empty whenever there is no DMA activity, for
+whatever reason (e.g. no ready data, or output is paused).
+The code works as follows: the user write routine dsp_write_body()
+fills up the READY region with new data (reclaiming space from the
+FREE region) and starts the write DMA engine if inactive (activity
+is indicated by d->flags & SND_F_WR_DMA ). The size of each
+DMA transfer is chosen according to a series of rules which will be
+discussed later. When a DMA transfer is complete, an interrupt causes
+dsp_wrintr() to be called which empties the DMA region, extends
+the FREE region and possibly starts the next transfer.
+
+In some cases, the code tries to track the current status of DMA
+operations by calling isa_dmastatus() and advancing the boundary
+between FREE and DMA regions accordingly.
+
+The size of a DMA transfer is selected according to the following
+rules:
+
+ 1. when not using AUTO DMA, do not wrap around the end of the
+ buffer, and do not let fp move too close to the end of the
+ buffer;
+
+ 2. do not use more than half of the buffer size.
+ This serves to allow room for a next write operation concurrent
+ with the dma transfer, and to reduce the time which is necessary
+ to wait before a pending dma will end (optionally, the max
+ size could be further limited to a fixed amount of play time,
+ depending on number of channels, sample size and sample speed);
+
+ 3. use the current blocksize (either specified by the user, or
+ corresponding roughly to 0.25s of data);
+
+ *
+ */
+
+
+/*
+ * dsp_wr_dmadone moves the write DMA region into the FREE region.
+ * It is assumed to be called at spltty() and with a write dma
+ * previously started.
+ */
+static void
+dsp_wr_dmadone(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ isa_dmadone(B_WRITE, b->buf + b->dp, b->dl, d->dma1);
+ b->fl += b->dl ; /* make dl bytes free */
+ /*
+ * XXX here it would be more efficient to record if there
+ * actually is a sleeping process, but this should still work.
+ */
+ wakeup(b); /* wakeup possible sleepers */
+ if (d->wsel.si_pid &&
+ ( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
+ selwakeup( & d->wsel );
+ b->dp = b->rp ;
+ b->dl0 = b->dl = 0 ;
+}
+
+/*
+ * The following function tracks the status of a (write) dma transfer,
+ * and moves the boundary between the FREE and the DMA regions.
+ * It works under the following assumptions:
+ * - the DMA engine is running;
+ * - the routine is called with interrupts blocked.
+ * BEWARE: when using AUTO DMA, dl can go negative! We assume that it
+ * does not wrap!
+ */
+void
+dsp_wr_dmaupdate(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_out) ;
+ int tmp;
+
+ tmp = b->dl - isa_dmastatus1(d->dma1) ;
+ tmp &= DMA_ALIGN_MASK; /* align... */
+ if (tmp > 0) { /* we have some new data */
+ b->dp += tmp;
+ if (b->dp >= b->bufsize)
+ b->dp -= b->bufsize;
+ b->dl -= tmp ;
+ b->fl += tmp ;
+ }
+}
+
+/*
+ * Write interrupt routine. Can be called from other places, but
+ * with interrupts disabled.
+ */
+void
+dsp_wrintr(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_out) ;
+ int cb_reason = SND_CB_WR ;
+
+ DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
+ b->dl, b->rl, b->fl));
+ if (d->flags & SND_F_WR_DMA) { /* dma was active */
+ b->int_count++;
+ d->flags &= ~SND_F_WR_DMA;
+ cb_reason = SND_CB_WR | SND_CB_RESTART ;
+ dsp_wr_dmadone(d);
+ } else
+ cb_reason = SND_CB_WR | SND_CB_START ;
+
+ /*
+ * start another dma operation only if have ready data in the
+ * buffer, there is no pending abort, have a full-duplex device
+ * (dma1 != dma2) or have half duplex device and there is no
+ * pending op on the other side.
+ *
+ * Force transfer to be aligned to a boundary of 4, which is
+ * needed when doing stereo and 16-bit. We could make this
+ * adaptive, but why bother for now...
+ */
+ if ( b->rl >= DMA_ALIGN_THRESHOLD &&
+ ! (d->flags & SND_F_ABORTING) &&
+ ( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
+ b->dl = min(b->rl, b->bufsize - b->rp ) ; /* do not wrap */
+ b->dl = min(b->dl, d->play_blocksize ); /* avoid too large transfer */
+ b->dl &= DMA_ALIGN_MASK ; /* realign things */
+ b->rl -= b->dl ;
+ b->rp += b->dl ;
+ if (b->rp == b->bufsize)
+ b->rp = 0 ;
+ /*
+ * now try to avoid too small dma transfers in the near future.
+ * This can happen if I let rp start too close to the end of
+ * the buffer. If this happens, and have enough data, try to
+ * split the available block in two approx. equal parts.
+ * XXX this code can go when we use auto dma.
+ */
+ if (b->bufsize - b->rp < MIN_CHUNK_SIZE &&
+ b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) {
+ b->dl = (b->bufsize - b->dp) / 2;
+ b->dl &= ~3 ; /* align to a boundary of 4 */
+ b->rl += (b->rp - (b->dp + b->dl) ) ;
+ b->rp = b->dp + b->dl ; /* no need to check for wraps */
+ }
+ /*
+ * how important is the order of operations ?
+ */
+ if (b->dl == 0) {
+ printf("ouch... want to start for 0 bytes!\n");
+ goto ferma;
+ }
+ b->dl0 = b->dl ; /* XXX */
+ if (d->callback)
+ d->callback(d, cb_reason ); /* start/restart dma */
+ isa_dmastart( B_WRITE , b->buf + b->dp, b->dl, d->dma1);
+ d->flags |= SND_F_WR_DMA;
+ } else {
+ferma:
+ if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART )
+ d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
+ /*
+ * if switching to read, should start the read dma...
+ */
+ if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
+ dsp_rdintr(d);
+ DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n",
+ d->flags, isa_dmastatus1(d->dma1), b->rl));
+ }
+}
+
+/*
+ * user write routine
+ *
+ * advance the boundary between READY and FREE, fill the space with
+ * uiomove(), and possibly start DMA. Do the above until the transfer
+ * is complete.
+ *
+ * To minimize latency in case a pending DMA transfer is about to end,
+ * we do the transfer in pieces of increasing sizes, extending the
+ * READY area at every checkpoint. In the (necessary) assumption that
+ * memory bandwidth is larger than the rate at which the dma consumes
+ * data, we reduce the latency to something proportional to the length
+ * of the first piece, while keeping the overhead low and being able
+ * to feed the DMA with large blocks.
+ */
+
+int
+dsp_write_body(snddev_info *d, struct uio *buf)
+{
+ int timeout = 1, n, l, bsz, ret = 0 ;
+ long s;
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ /* assume d->flags |= SND_F_WRITING ; has been done before */
+ /*
+ * bsz is the max size for the next transfer. If the dma was
+ * idle, we want it as large as possible. Otherwise, start with
+ * a small block to avoid underruns if we are close to the end of
+ * the previous operation.
+ */
+ bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
+ while ( n = buf->uio_resid ) {
+ l = min (n, bsz); /* at most n bytes ... */
+ s = spltty(); /* no interrupts here ... */
+ /*
+ * if i) the dma engine is running, ii) we do not have enough space
+ * in the FREE region, and iii) the current DMA transfer might let
+ * us complete the _whole_ transfer without sleeping, or we are doing
+ * non-blocking I/O, then try to extend the FREE region.
+ * Otherwise do not bother, we will need to sleep anyways, and
+ * make the timeout longer.
+ */
+
+ if ( d->flags & SND_F_WR_DMA && b->fl < l &&
+ ( b->fl + b->dl >= n || d->flags & SND_F_NBIO ) )
+ dsp_wr_dmaupdate(d); /* should really change timeout... */
+ else
+ timeout = hz;
+ l = min( l, b->fl ); /* no more than avail. space */
+ l = min( l, b->bufsize - b->fp ); /* do not wrap ... */
+ DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
+ /*
+ * at this point, we assume that if l==0 the dma engine
+ * must (or will, in cause it is paused) be running.
+ */
+ if (l == 0) { /* no space, must sleep */
+ if (d->flags & SND_F_NBIO) {
+ /* unless of course we are doing non-blocking i/o */
+ splx(s);
+ break;
+ }
+ DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
+ ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
+ if (ret == EINTR)
+ d->flags |= SND_F_ABORTING ;
+ splx(s);
+ if (ret == ERESTART || ret == EINTR)
+ break ;
+ timeout = min(2*timeout, hz);
+ continue;
+ }
+ splx(s);
+ timeout = 1 ; /* we got some data... */
+
+ /*
+ * copy data to the buffer, and possibly do format
+ * conversions (here, from ULAW to U8).
+ * NOTE: I can use fp here since it is not modified by the
+ * interrupt routines.
+ */
+ ret = uiomove(b->buf + b->fp, l, buf) ;
+ if (ret !=0 ) { /* an error occurred ... */
+ printf("uiomove error %d\n", ret);
+ break ;
+ }
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l);
+
+ s = spltty(); /* no interrupts here ... */
+ b->rl += l ; /* this more ready bytes */
+ b->fl -= l ; /* this less free bytes */
+ b->fp += l ;
+ if (b->fp >= b->bufsize) /* handle wraps */
+ b->fp -= b->bufsize ;
+ if ( !(d->flags & SND_F_WR_DMA) ) {/* dma was idle, restart it */
+ if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
+ SND_F_INIT) {
+ /* want to init but no pending DMA activity */
+ splx(s);
+ d->callback(d, SND_CB_INIT); /* this is slow! */
+ s = spltty();
+ }
+ dsp_wrintr(d) ;
+ }
+ splx(s) ;
+ bsz = min(b->bufsize, bsz*2);
+ }
+ s = spltty(); /* no interrupts here ... */
+ d->flags &= ~SND_F_WRITING ;
+ if (d->flags & SND_F_ABORTING) {
+ d->flags &= ~SND_F_ABORTING;
+ splx(s);
+ dsp_wrabort(d);
+ }
+ splx(s) ;
+ return ret ;
+}
+
+/*
+ * SOUND INPUT
+ *
+
+The input part is similar to the output one. The only difference is in
+the ordering of regions, which is the following:
+
+ 0 rp,rl dp,dl fp,fl bufsize
+ |__________|____________|______>______|________|
+ FREE READY DMA FREE
+
+and the fact that input data are in the READY region.
+
+At initialization, as for the write routine, DMA and READY are empty,
+and FREE takes all the space:
+
+ dp = rp = fp = 0 ; -- beginning of buffer
+ dl0 = dl = 0 ; -- meaning no dma activity
+ rl = 0 ; -- meaning no data ready
+ fl = bufsize ;
+
+Operation is as follows: upon user read (dsp_read_body()) a DMA read
+is started if not already active (marked by d->flags & SND_F_RD_DMA),
+then as soon as data are available in the READY region they are
+transferred to the user buffer, thus advancing the boundary between FREE
+and READY. Upon interrupts, caused by a completion of a DMA transfer,
+the READY region is extended and possibly a new transfer is started.
+
+When necessary, isa_dmastatus() is called to advance the boundary
+between READY and DMA to the real position.
+
+The rules to choose the size of the new DMA area are similar to
+the other case, i.e:
+
+ 1. if not using AUTO mode, do not wrap around the end of the
+ buffer, and do not let fp move too close to the end of the
+ buffer;
+
+ 2. do not use more than half the buffer size; this serves to
+ leave room for the next dma operation.
+
+ 3. use the default blocksize, either user-specified, or
+ corresponding to 0.25s of data;
+
+ *
+ */
+
+/*
+ * dsp_rd_dmadone moves bytes in the input buffer from DMA region to
+ * READY region. We assume it is called at spltty() and with dl>0
+ */
+static void
+dsp_rd_dmadone(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ isa_dmadone(B_READ, b->buf + b->dp, b->dl, d->dma2);
+ b->rl += b->dl ; /* make dl bytes available */
+ wakeup(b) ; /* wakeup possibly sleeping processes */
+ if (d->rsel.si_pid &&
+ ( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
+ selwakeup( & d->rsel );
+ b->dp = b->fp ;
+ b->dl0 = b->dl = 0 ;
+}
+
+/*
+ * The following function tracks the status of a (read) dma transfer,
+ * and moves the boundary between the READY and the DMA regions.
+ * It works under the following assumptions:
+ * - the DMA engine is running;
+ * - the function is called with interrupts blocked.
+ */
+void
+dsp_rd_dmaupdate(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_in) ;
+ int tmp ;
+
+ tmp = b->dl - isa_dmastatus1(d->dma2) ;
+ tmp &= DMA_ALIGN_MASK; /* align... */
+ if (tmp > 0) { /* we have some data */
+ b->dp += tmp;
+ if (b->dp >= b->bufsize)
+ b->dp -= b->bufsize;
+ b->dl -= tmp ;
+ b->rl += tmp ;
+ }
+}
+
+/*
+ * read interrupt routine. Must be called with interrupts blocked.
+ */
+void
+dsp_rdintr(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_in) ;
+ int cb_reason = SND_CB_RD ;
+
+ DEB(printf("dsp_rdintr: start dl = %d fp %d blocksize %d\n",
+ b->dl, b->fp, d->rec_blocksize));
+ if (d->flags & SND_F_RD_DMA) { /* dma was active */
+ b->int_count++;
+ d->flags &= ~SND_F_RD_DMA;
+ cb_reason = SND_CB_RD | SND_CB_RESTART ;
+ dsp_rd_dmadone(d);
+ } else
+ cb_reason = SND_CB_RD | SND_CB_START ;
+ /*
+ * Same checks as in the write case (mutatis mutandis) to decide
+ * whether or not to restart a dma transfer.
+ */
+ if ( b->fl >= DMA_ALIGN_THRESHOLD &&
+ ((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
+ ( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
+ b->dl = min(b->fl, b->bufsize - b->fp ) ; /* do not wrap */
+ b->dl = min(b->dl, d->rec_blocksize );
+ b->dl &= DMA_ALIGN_MASK ; /* realign sizes */
+ b->fl -= b->dl ;
+ b->fp += b->dl ;
+ if (b->fp == b->bufsize)
+ b->fp = 0 ;
+ /*
+ * now try to avoid too small dma transfers in the near future.
+ * This can happen if I let fp start too close to the end of
+ * the buffer. If this happens, and have enough data, try to
+ * split the available block in two approx. equal parts.
+ */
+ if (b->bufsize - b->fp < MIN_CHUNK_SIZE &&
+ b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) {
+ b->dl = (b->bufsize - b->dp) / 2;
+ b->dl &= DMA_ALIGN_MASK ; /* align to multiples of 3 */
+ b->fl += (b->fp - (b->dp + b->dl) ) ;
+ b->fp = b->dp + b->dl ; /* no need to check that fp wraps */
+ }
+ if (b->dl == 0) {
+ printf("ouch! want to read 0 bytes\n");
+ goto ferma;
+ }
+ b->dl0 = b->dl ; /* XXX */
+ if (d->callback)
+ d->callback(d, cb_reason); /* restart_dma(); */
+ isa_dmastart( B_READ , b->buf + b->dp, b->dl, d->dma2);
+ d->flags |= SND_F_RD_DMA;
+ } else {
+ferma:
+ if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART)
+ d->callback(d, SND_CB_RD | SND_CB_STOP);
+ /*
+ * if switching to write, start write dma engine
+ */
+ if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
+ dsp_wrintr(d) ;
+ DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n",
+ d->flags, isa_dmastatus1(d->dma2), b->fl));
+ }
+}
+
+/*
+ * body of user-read routine
+ *
+ * Start DMA if not active; wait for READY not empty.
+ * Transfer data from READY region using uiomove(), advance boundary
+ * between FREE and READY. Repeat until transfer is complete.
+ *
+ * To avoid excessive latency in freeing up space for the DMA
+ * engine, transfers are done in blocks of increasing size, so that
+ * the latency is proportional to the size of the smallest block, but
+ * we have a low overhead and are able to feed the dma engine with
+ * large blocks.
+ *
+ * When we enter this routine, we assume that d->flags |= SND_F_READING
+ * was done before.
+ *
+ * NOTE: in the current version, read will not return more than
+ * blocksize bytes at once (unless more are already available), to
+ * avoid that requests using very large buffers block for too long.
+ */
+
+int
+dsp_read_body(snddev_info *d, struct uio *buf)
+{
+ int limit, l, n, bsz, ret = 0 ;
+ long s;
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ int timeout = 1 ; /* counter of how many ticks we sleep */
+
+ /*
+ * "limit" serves to return after at most one blocksize of data
+ * (unless more are already available). Otherwise, things like
+ * cat /dev/audio would use a 64K buffer and would start returning
+ * data after a _very_ long time...
+ * Note -- some applications depend on reads not returning short
+ * blocks. But I believe these apps are broken, since interrupted
+ * system calls might return short reads anyways, and the
+ * application should better check that.
+ */
+
+ if (buf->uio_resid > d->rec_blocksize)
+ limit = buf->uio_resid - d->rec_blocksize;
+ else
+ limit = 0;
+ bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
+ while ( (n = buf->uio_resid) > limit ) {
+ DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
+ /*
+ * here compute how many bytes to transfer, enforcing various
+ * limitations:
+ */
+ l = min (n, bsz); /* 1': at most bsz bytes ... */
+ s = spltty(); /* no interrupts here ! */
+ /*
+ * if i) the dma engine is running, ii) we do not have enough
+ * ready bytes, and iii) the current DMA transfer could give
+ * us what we need, or we are doing non-blocking IO, then try
+ * to extend the READY region.
+ * Otherwise do not bother, we will need to sleep anyways,
+ * and make the timeout longer.
+ */
+
+ if ( d->flags & SND_F_RD_DMA && b->rl < l &&
+ ( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
+ dsp_rd_dmaupdate(d);
+ else
+ timeout = hz ;
+ l = min( l, b->rl ); /* 2': no more than avail. data */
+ l = min( l, b->bufsize - b->rp ); /* 3': do not wrap buffer. */
+ /* the above limitation can be removed if we use auto DMA
+ * on the ISA controller. But then we have to make a check
+ * when doing the uiomove...
+ */
+ if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
+ /*
+ * There are two reasons the dma can be idle: either this
+ * is the first read, or the buffer has become full. In
+ * the latter case, the dma cannot be restarted until
+ * we have removed some data, which will be true at the
+ * second round.
+ *
+ * Call dsp_rdintr to start the dma. It would be nice to
+ * have a "need" field in the snd_dbuf, so that we do
+ * not start a long operation unnecessarily. However,
+ * the restart code will ask for at most d->blocksize
+ * bytes, and since we are sure we are the only reader,
+ * and the routine is not interrupted, we patch and
+ * restore d->blocksize around the call. A bit dirty,
+ * but it works, and saves some overhead :)
+ */
+
+ int old_bs = d->rec_blocksize;
+
+ if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
+ SND_F_INIT) {
+ /* want to init but no pending DMA activity */
+ splx(s);
+ d->callback(d, SND_CB_INIT); /* this is slow! */
+ s = spltty();
+ }
+ if (l < MIN_CHUNK_SIZE)
+ d->rec_blocksize = MIN_CHUNK_SIZE ;
+ else if (l < d->rec_blocksize)
+ d->rec_blocksize = l ;
+ dsp_rdintr(d);
+ d->rec_blocksize = old_bs ;
+ }
+
+ if (l == 0) {
+ /*
+ * If, after all these efforts, we still have no data ready,
+ * then we must sleep (unless of course we have doing
+ * non-blocking i/o. But use exponential delays, starting
+ * at 1 tick and doubling each time.
+ */
+ if (d->flags & SND_F_NBIO) {
+ splx(s);
+ break;
+ }
+ DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
+ timeout, buf->uio_resid));
+ ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
+ if (ret == EINTR)
+ d->flags |= SND_F_ABORTING ;
+ splx(s); /* necessary before the goto again... */
+ if (ret == ERESTART || ret == EINTR)
+ break ;
+ DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
+ timeout = min(timeout*2, hz);
+ continue;
+ }
+ splx(s);
+
+ timeout = 1 ; /* we got some data, so reset exp. wait */
+ /*
+ * if we are using /dev/audio and the device does not
+ * support it natively, we should do a format conversion.
+ * (in this case from uLAW to natural format).
+ * This can be messy in that it can require an intermediate
+ * buffer, and also screw up the byte count.
+ */
+ /*
+ * NOTE: I _can_ use rp here because it is not modified by the
+ * interrupt routines.
+ */
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(dsp_ulaw, b->buf + b->rp, l);
+ ret = uiomove(b->buf + b->rp, l, buf) ;
+ if (ret !=0 ) /* an error occurred ... */
+ break ;
+
+ s = spltty(); /* no interrupts here ... */
+ b->fl += l ; /* this more free bytes */
+ b->rl -= l ; /* this less ready bytes */
+ b->rp += l ; /* advance ready pointer */
+ if (b->rp == b->bufsize) /* handle wraps */
+ b->rp = 0 ;
+ splx(s) ;
+ bsz = min(b->bufsize, bsz*2);
+ }
+ s = spltty(); /* no interrupts here ... */
+ d->flags &= ~SND_F_READING ;
+ if (d->flags & SND_F_ABORTING) {
+ d->flags |= ~SND_F_ABORTING;
+ splx(s);
+ dsp_rdabort(d);
+ }
+ splx(s) ;
+ return ret ;
+}
+
+
+/*
+ * short routine to initialize a dma buffer descriptor (usually
+ * located in the XXX_desc structure). The first parameter is
+ * the buffer size, the second one specifies the dma channel in use
+ * At the moment we do not support more than 64K, since for some
+ * cards I need to switch between dma1 and dma2. The channel is
+ * unused.
+ */
+void
+alloc_dbuf(snd_dbuf *b, int size, int chan)
+{
+ if (size > 0x10000)
+ panic("max supported size is 64k");
+ b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
+ 0ul, 0xfffffful, 1ul, 0x10000ul);
+ /* should check that it does not fail... */
+ b->dp = b->rp = b->fp = 0 ;
+ b->dl0 = b->dl = b->rl = 0 ;
+ b->bufsize = b->fl = size ;
+}
+
+void
+reset_dbuf(snd_dbuf *b)
+{
+ b->dp = b->rp = b->fp = 0 ;
+ b->dl0 = b->dl = b->rl = 0 ;
+ b->fl = b->bufsize ;
+}
+
+/*
+ * snd_sync waits until the space in the given channel goes above
+ * a threshold. chan = 1 : play, 2: capture. The threshold is
+ * checked against fl or rl respectively.
+ * Assume that the condition can become true, do not check here...
+ */
+int
+snd_sync(snddev_info *d, int chan, int threshold)
+{
+ u_long s;
+ int ret;
+ snd_dbuf *b;
+
+ b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;
+
+ for (;;) {
+ s=spltty();
+ if ( chan==1 )
+ dsp_wr_dmaupdate(d);
+ else
+ dsp_rd_dmaupdate(d);
+ if ( (chan == 1 && b->fl <= threshold) ||
+ (chan == 2 && b->rl <= threshold) ) {
+ ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
+ splx(s);
+ if (ret == ERESTART || ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1 ;
+ }
+ } else
+ break;
+ }
+ splx(s);
+ return 0 ;
+}
+
+/*
+ * dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
+ * which abort a pending DMA transfer and flush the buffers.
+ */
+int
+dsp_wrabort(snddev_info *d)
+{
+ long s;
+ int missing = 0;
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ s = spltty();
+ if ( d->flags & SND_F_WR_DMA ) {
+ d->flags &= ~(SND_F_WR_DMA | SND_F_WRITING);
+ if (d->callback)
+ d->callback(d, SND_CB_WR | SND_CB_ABORT);
+ missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
+
+ b->rl += missing ; /* which are part of the ready area */
+ b->rp -= missing ;
+ if (b->rp < 0)
+ b->rp += b->bufsize;
+ DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
+ b->dl - missing, b->dl));
+ b->dl -= missing;
+ dsp_wr_dmadone(d);
+ missing = b->rl;
+ }
+ reset_dbuf(b);
+ splx(s);
+ return missing;
+}
+
+int
+dsp_rdabort(snddev_info *d)
+{
+ long s;
+ int missing = 0;
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ s = spltty();
+ if ( d->flags & SND_F_RD_DMA ) {
+ d->flags &= ~(SND_F_RD_DMA | SND_F_READING);
+ if (d->callback)
+ d->callback(d, SND_CB_RD | SND_CB_ABORT);
+ missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
+
+ b->fl += missing ; /* which are part of the free area */
+ b->fp -= missing ;
+ if (b->fp < 0)
+ b->fp += b->bufsize;
+ DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
+ b->dl - missing, b->dl));
+ b->dl -= missing;
+ dsp_rd_dmadone(d);
+ missing = b->rl ;
+ }
+ reset_dbuf(b);
+ splx(s);
+ return missing;
+}
+
+/*
+ * this routine tries to flush the dma transfer. It is called
+ * on a close. The caller must set SND_F_CLOSING, and insure that
+ * interrupts are enabled. We immediately abort any read DMA
+ * operation, and then wait for the play buffer to drain.
+ */
+
+int
+snd_flush(snddev_info *d)
+{
+ int ret, res, res1;
+ int count=10;
+
+DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
+ dsp_rdabort(d);
+ if ( d->flags & SND_F_WR_DMA ) {
+ /* close write */
+ while ( d->flags & SND_F_WR_DMA ) {
+ /*
+ * still pending output data.
+ */
+ ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
+ if (ret == ERESTART || ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1 ;
+ }
+ if ( ret && --count == 0) {
+ printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n",
+ isa_dmastatus1(d->dma1), d->flags);
+ return -1 ;
+ }
+ }
+ d->flags &= ~SND_F_CLOSING ;
+ }
+ reset_dbuf(& (d->dbuf_out) );
+ return 0 ;
+}
+
+/*
+ * end of new code for dma buffer handling
+ */
diff --git a/sys/i386/isa/snd/dmabuf_auto.c b/sys/i386/isa/snd/dmabuf_auto.c
new file mode 100644
index 0000000..3972be8
--- /dev/null
+++ b/sys/i386/isa/snd/dmabuf_auto.c
@@ -0,0 +1,802 @@
+/*
+ * snd/dmabuf.c
+ *
+ * New DMA routines -- Luigi Rizzo, 05 sep 97
+ * This file implements the new DMA routines for the sound driver.
+ * AUTO DMA MODE (ISA DMA SIDE).
+ *
+ * Copyright by Luigi Rizzo - 1997
+ *
+ * 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.
+ *
+ */
+
+#include <i386/isa/snd/sound.h>
+#include <i386/isa/snd/ulaw.h>
+
+#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
+#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */
+#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS)
+#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1))
+
+static void dsp_wr_dmadone(snddev_info *d);
+static void dsp_rd_dmadone(snddev_info *d);
+
+/*
+ *
+
+SOUND OUTPUT
+
+We use a circular buffer to store samples directed to the DAC.
+The buffer is split into two variable-size regions, each identified
+by an offset in the buffer (rp,fp) and a lenght (rl,fl).
+
+ 0 rp,rl fp,fl bufsize
+ |__________|____________|________|
+ FREE d> READY w> FREE
+
+ READY region: contains data written from the process and ready
+ to be sent to the DAC;
+
+ FREE region: is the empty region of the buffer, where a process
+ can write new data.
+
+The first block of the READY region is used for DMA transfers. The
+transfer is started at rp and with chunks of length dl0 computed
+according to some rules. During DMA operations, rp advances (d>)
+and rl,fl are updated by dsp_wr_dmaupdate(). When a new block is
+written, fp advances (w>) and rl,fl are updated accordingly.
+
+Both the "READY" and "FREE" regions can wrap around the end of the
+buffer.
+
+At initialization, READY is empty, FREE takes all the
+available space, and dma is idle
+
+dl0 contains the new blocksize to be used in dma transfers (passed to
+the callback). dl contains the previous blocksize (needed when the
+codec uses AUTO DMA).
+
+ rp = fp = 0 ; -- beginning of buffer
+ dl0 = dl = 0 ;
+ rl = 0 ; -- meaning no data ready
+ fl = bufsize ;
+
+The code works as follows: the user write routine dsp_write_body()
+fills up the READY region with new data (reclaiming space from the
+FREE region) and starts the write DMA engine if inactive (activity
+is indicated by d->flags & SND_F_WR_DMA ). The size of each
+DMA transfer is chosen according to a series of rules which will be
+discussed later. When a DMA transfer is complete, an interrupt causes
+dsp_wrintr() to be called which empties the DMA region, extends
+the FREE region and possibly starts the next transfer.
+
+In some cases, the code tries to track the current status of DMA
+operations by calling isa_dmastatus() and advancing the boundary
+between FREE and DMA regions accordingly.
+
+The size of a DMA transfer is selected according to the following
+rules:
+
+ 1. when not using AUTO DMA, do not wrap around the end of the
+ buffer, and do not let fp move too close to the end of the
+ buffer;
+
+ 2. do not use more than half of the buffer size.
+ This serves to allow room for a next write operation concurrent
+ with the dma transfer, and to reduce the time which is necessary
+ to wait before a pending dma will end (optionally, the max
+ size could be further limited to a fixed amount of play time,
+ depending on number of channels, sample size and sample speed);
+
+ 3. use the current blocksize (either specified by the user, or
+ corresponding roughly to 0.25s of data);
+
+ *
+ */
+
+
+/*
+ * dsp_wr_dmadone moves the write DMA region into the FREE region.
+ * It is assumed to be called at spltty() and with a write dma
+ * previously started.
+ */
+static void
+dsp_wr_dmadone(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ dsp_wr_dmaupdate(d);
+ /*
+ * XXX here it would be more efficient to record if there
+ * actually is a sleeping process, but this should still work.
+ */
+ wakeup(b); /* wakeup possible sleepers */
+ if (d->wsel.si_pid &&
+ ( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) )
+ selwakeup( & d->wsel );
+}
+
+/*
+ * The following function tracks the status of a (write) dma transfer,
+ * and moves the boundary between the FREE and the DMA regions.
+ * It works under the following assumptions:
+ * - the DMA engine is active;
+ * - the routine is called with interrupts blocked.
+ */
+void
+dsp_wr_dmaupdate(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_out) ;
+ int tmp, delta;
+
+ tmp = b->bufsize - isa_dmastatus1(d->dma1) ;
+ tmp &= DMA_ALIGN_MASK; /* align... */
+ delta = tmp - b->rp;
+ if (delta < 0) /* wrapped */
+ delta += b->bufsize ;
+ b->rp = tmp;
+ b->rl -= delta ;
+ b->fl += delta ;
+}
+
+/*
+ * Write interrupt routine. Can be called from other places, but
+ * with interrupts disabled.
+ */
+void
+dsp_wrintr(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n",
+ b->dl, b->rl, b->fl));
+ if (d->flags & SND_F_WR_DMA) { /* dma was active */
+ b->int_count++;
+ d->flags &= ~SND_F_WR_DMA;
+ dsp_wr_dmadone(d);
+ } else
+ b->dl = 0 ;
+
+ /*
+ * start another dma operation only if have ready data in the
+ * buffer, there is no pending abort, have a full-duplex device
+ * (dma1 != dma2) or have half duplex device and there is no
+ * pending op on the other side.
+ *
+ * Force transfer to be aligned to a boundary of 4, which is
+ * needed when doing stereo and 16-bit. We could make this
+ * adaptive, but why bother for now...
+ */
+ if ( b->rl >= DMA_ALIGN_THRESHOLD &&
+ ! (d->flags & SND_F_ABORTING) &&
+ ( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) {
+ int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */
+ l &= DMA_ALIGN_MASK ; /* realign things */
+
+ b->dl0 = l ;
+ if (d->callback)
+ d->callback(d, SND_CB_WR | SND_CB_START );
+ d->flags |= SND_F_WR_DMA;
+ b->dl = b->dl0;
+ } else {
+ if (b->dl > 0) { /* was active */
+ b->dl0 = 0;
+ d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */
+ }
+ /*
+ * if switching to read, should start the read dma...
+ */
+ if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) )
+ dsp_rdintr(d);
+ DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n",
+ d->flags, isa_dmastatus1(d->dma1), b->rl));
+ }
+}
+
+/*
+ * user write routine
+ *
+ * advance the boundary between READY and FREE, fill the space with
+ * uiomove(), and possibly start DMA. Do the above until the transfer
+ * is complete.
+ *
+ * To minimize latency in case a pending DMA transfer is about to end,
+ * we do the transfer in pieces of increasing sizes, extending the
+ * READY area at every checkpoint. In the (necessary) assumption that
+ * memory bandwidth is larger than the rate at which the dma consumes
+ * data, we reduce the latency to something proportional to the length
+ * of the first piece, while keeping the overhead low and being able
+ * to feed the DMA with large blocks.
+ */
+
+int
+dsp_write_body(snddev_info *d, struct uio *buf)
+{
+ int timeout = 1, n, l, bsz, ret = 0 ;
+ long s;
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ /* assume d->flags |= SND_F_WRITING ; has been done before */
+ /*
+ * bsz is the max size for the next transfer. If the dma was
+ * idle, we want it as large as possible. Otherwise, start with
+ * a small block to avoid underruns if we are close to the end of
+ * the previous operation.
+ */
+ bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ;
+ while ( n = buf->uio_resid ) {
+ l = min (n, bsz); /* at most n bytes ... */
+ s = spltty(); /* no interrupts here ... */
+ /*
+ * if i) the dma engine is running, ii) we do not have enough space
+ * in the FREE region, and iii) the current DMA transfer might let
+ * us complete the _whole_ transfer without sleeping, or we are doing
+ * non-blocking I/O, then try to extend the FREE region.
+ * Otherwise do not bother, we will need to sleep anyways, and
+ * make the timeout longer.
+ * Note that the check for iii) is only approximate since we
+ * might have fewer than dl0 residual bytes.
+ */
+
+ if ( d->flags & SND_F_WR_DMA && b->fl < l &&
+ ( b->fl + b->dl0 >= n || d->flags & SND_F_NBIO ) )
+ dsp_wr_dmaupdate(d); /* should really change timeout... */
+ else
+ timeout = hz;
+ l = min( l, b->fl ); /* no more than avail. space */
+ DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n));
+ /*
+ * at this point, we assume that if l==0 the dma engine
+ * must (or will, in cause it is paused) be running.
+ */
+ if (l == 0) { /* no space, must sleep */
+ if (d->flags & SND_F_NBIO) {
+ /* unless of course we are doing non-blocking i/o */
+ splx(s);
+ break;
+ }
+ DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl));
+ ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout);
+ if (ret == EINTR)
+ d->flags |= SND_F_ABORTING ;
+ splx(s);
+ if (ret == ERESTART || ret == EINTR)
+ break ;
+ timeout = min(2*timeout, hz);
+ continue;
+ }
+ splx(s);
+ timeout = 1 ; /* we got some data... */
+
+ /*
+ * copy data to the buffer, and possibly do format
+ * conversions (here, from ULAW to U8).
+ * NOTE: I can use fp here since it is not modified by the
+ * interrupt routines.
+ */
+ if (b->fp + l > b->bufsize) {
+ int l1 = b->bufsize - b->fp ;
+ uiomove(b->buf + b->fp, l1, buf) ;
+ uiomove(b->buf, l - l1, buf) ;
+ if (d->flags & SND_F_XLAT8) {
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l1);
+ translate_bytes(ulaw_dsp, b->buf , l - l1);
+ }
+ } else {
+ uiomove(b->buf + b->fp, l, buf) ;
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l);
+ }
+
+ s = spltty(); /* no interrupts here ... */
+ b->rl += l ; /* this more ready bytes */
+ b->fl -= l ; /* this less free bytes */
+ b->fp += l ;
+ if (b->fp >= b->bufsize) /* handle wraps */
+ b->fp -= b->bufsize ;
+ if ( !(d->flags & SND_F_WR_DMA) ) /* dma was idle, restart it */
+ dsp_wrintr(d) ;
+ splx(s) ;
+ bsz = min(b->bufsize, bsz*2);
+ }
+ s = spltty(); /* no interrupts here ... */
+ d->flags &= ~SND_F_WRITING ;
+ if (d->flags & SND_F_ABORTING) {
+ d->flags &= ~SND_F_ABORTING;
+ splx(s);
+ dsp_wrabort(d);
+ }
+ splx(s) ;
+ return ret ;
+}
+
+/*
+ * SOUND INPUT
+ *
+
+The input part is similar to the output one. The only difference is in
+the ordering of regions, which is the following:
+
+ 0 rp,rl fp,fl bufsize
+ |__________|____________|________|
+ FREE r> READY d> FREE
+
+and the fact that input data are in the READY region.
+
+At initialization, as for the write routine, READY is empty,
+and FREE takes all the space:
+
+ rp = fp = 0 ; -- beginning of buffer
+ dl0 = dl = 0 ;
+ rl = 0 ; -- meaning no data ready
+ fl = bufsize ;
+
+Operation is as follows: upon user read (dsp_read_body()) a DMA read
+is started if not already active (marked by d->flags & SND_F_RD_DMA),
+then as soon as data are available in the READY region they are
+transferred to the user buffer, thus advancing the boundary between FREE
+and READY. Upon interrupts, caused by a completion of a DMA transfer,
+the READY region is extended and possibly a new transfer is started.
+
+When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
+rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
+updated accordingly.
+
+
+The rules to choose the size of the new DMA area are similar to
+the other case, i.e:
+
+ 1. if not using AUTO mode, do not wrap around the end of the
+ buffer, and do not let fp move too close to the end of the
+ buffer;
+
+ 2. do not use more than half the buffer size; this serves to
+ leave room for the next dma operation.
+
+ 3. use the default blocksize, either user-specified, or
+ corresponding to 0.25s of data;
+
+ *
+ */
+
+/*
+ * dsp_rd_dmadone moves bytes in the input buffer from DMA region to
+ * READY region. We assume it is called at spltty() and with dl>0
+ */
+static void
+dsp_rd_dmadone(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ dsp_rd_dmaupdate(d);
+ b->rl += b->dl ; /* make dl bytes available */
+ wakeup(b) ; /* wakeup possibly sleeping processes */
+ if (d->rsel.si_pid &&
+ ( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )
+ selwakeup( & d->rsel );
+ b->dp = b->fp ;
+ b->dl0 = b->dl = 0 ;
+}
+
+/*
+ * The following function tracks the status of a (read) dma transfer,
+ * and moves the boundary between the READY and the DMA regions.
+ * It works under the following assumptions:
+ * - the DMA engine is running;
+ * - the function is called with interrupts blocked.
+ */
+void
+dsp_rd_dmaupdate(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_in) ;
+ int tmp ;
+
+ tmp = b->bufsize - isa_dmastatus1(d->dma2) ;
+ tmp &= DMA_ALIGN_MASK; /* align... */
+ delta = tmp - b->fp;
+ if (delta < 0) /* wrapped */
+ delta += b->bufsize ;
+ b->fp = tmp;
+ b->fl -= delta ;
+ b->rl += delta ;
+}
+
+/*
+ * read interrupt routine. Must be called with interrupts blocked.
+ */
+void
+dsp_rdintr(snddev_info *d)
+{
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ if (d->flags & SND_F_RD_DMA) { /* dma was active */
+ b->int_count++;
+ d->flags &= ~SND_F_RD_DMA;
+ dsp_rd_dmadone(d);
+ } else
+ b->dl = 0 ;
+ /*
+ * Same checks as in the write case (mutatis mutandis) to decide
+ * whether or not to restart a dma transfer.
+ */
+ if ( b->fl >= DMA_ALIGN_THRESHOLD &&
+ ((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) &&
+ ( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) {
+ int l = min(b->fl, d->rec_blocksize);
+ l &= DMA_ALIGN_MASK ; /* realign sizes */
+ b->dl0 = l ;
+ if (d->callback)
+ d->callback(d, SND_CB_RD | SND_CB_START );
+ d->flags |= SND_F_RD_DMA;
+ b->dl = b->dl0;
+ } else {
+ if (b->dl > 0) {
+ b->dl0 = 0 ; /* was active */
+ d->callback(d, SND_CB_RD | SND_CB_STOP);
+ }
+ /*
+ * if switching to write, start write dma engine
+ */
+ if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) )
+ dsp_wrintr(d) ;
+ DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n",
+ d->flags, isa_dmastatus1(d->dma2), b->fl));
+ }
+}
+
+/*
+ * body of user-read routine
+ *
+ * Start DMA if not active; wait for READY not empty.
+ * Transfer data from READY region using uiomove(), advance boundary
+ * between FREE and READY. Repeat until transfer is complete.
+ *
+ * To avoid excessive latency in freeing up space for the DMA
+ * engine, transfers are done in blocks of increasing size, so that
+ * the latency is proportional to the size of the smallest block, but
+ * we have a low overhead and are able to feed the dma engine with
+ * large blocks.
+ *
+ * When we enter this routine, we assume that d->flags |= SND_F_READING
+ * was done before.
+ *
+ * NOTE: in the current version, read will not return more than
+ * blocksize bytes at once (unless more are already available), to
+ * avoid that requests using very large buffers block for too long.
+ */
+
+int
+dsp_read_body(snddev_info *d, struct uio *buf)
+{
+ int limit, l, n, bsz, ret = 0 ;
+ long s;
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ int timeout = 1 ; /* counter of how many ticks we sleep */
+
+ /*
+ * "limit" serves to return after at most one blocksize of data
+ * (unless more are already available). Otherwise, things like
+ * cat /dev/audio would use a 64K buffer and would start returning
+ * data after a _very_ long time...
+ * Note -- some applications depend on reads not returning short
+ * blocks. But I believe these apps are broken, since interrupted
+ * system calls might return short reads anyways, and the
+ * application should better check that.
+ */
+
+ if (buf->uio_resid > d->rec_blocksize)
+ limit = buf->uio_resid - d->rec_blocksize;
+ else
+ limit = 0;
+ bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */
+ while ( (n = buf->uio_resid) > limit ) {
+ DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));
+ /*
+ * here compute how many bytes to transfer, enforcing various
+ * limitations:
+ */
+ l = min (n, bsz); /* 1': at most bsz bytes ... */
+ s = spltty(); /* no interrupts here ! */
+ /*
+ * if i) the dma engine is running, ii) we do not have enough
+ * ready bytes, and iii) the current DMA transfer could give
+ * us what we need, or we are doing non-blocking IO, then try
+ * to extend the READY region.
+ * Otherwise do not bother, we will need to sleep anyways,
+ * and make the timeout longer.
+ */
+
+ if ( d->flags & SND_F_RD_DMA && b->rl < l &&
+ ( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) )
+ dsp_rd_dmaupdate(d);
+ else
+ timeout = hz ;
+ l = min( l, b->rl ); /* 2': no more than avail. data */
+ if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */
+ /*
+ * There are two reasons the dma can be idle: either this
+ * is the first read, or the buffer has become full. In
+ * the latter case, the dma cannot be restarted until
+ * we have removed some data, which will be true at the
+ * second round.
+ *
+ * Call dsp_rdintr to start the dma. It would be nice to
+ * have a "need" field in the snd_dbuf, so that we do
+ * not start a long operation unnecessarily. However,
+ * the restart code will ask for at most d->blocksize
+ * bytes, and since we are sure we are the only reader,
+ * and the routine is not interrupted, we patch and
+ * restore d->blocksize around the call. A bit dirty,
+ * but it works, and saves some overhead :)
+ */
+
+ int old_bs = d->rec_blocksize;
+ if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) ==
+ SND_F_INIT) {
+ /* want to init but no pending DMA activity */
+ splx(s);
+ d->callback(d, SND_CB_INIT); /* this is slow! */
+ s = spltty();
+ }
+ if (l < MIN_CHUNK_SIZE)
+ d->rec_blocksize = MIN_CHUNK_SIZE ;
+ else if (l < d->rec_blocksize)
+ d->rec_blocksize = l ;
+ d->dl0 = d->rec_blocksize ;
+ dsp_rdintr(d);
+ d->rec_blocksize = old_bs ;
+ }
+
+ if (l == 0) {
+ /*
+ * If, after all these efforts, we still have no data ready,
+ * then we must sleep (unless of course we have doing
+ * non-blocking i/o. But use exponential delays, starting
+ * at 1 tick and doubling each time.
+ */
+ if (d->flags & SND_F_NBIO) {
+ splx(s);
+ break;
+ }
+ DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n",
+ timeout, buf->uio_resid));
+ ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;
+ if (ret == EINTR)
+ d->flags |= SND_F_ABORTING ;
+ splx(s); /* necessary before the goto again... */
+ if (ret == ERESTART || ret == EINTR)
+ break ;
+ DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl));
+ timeout = min(timeout*2, hz);
+ continue;
+ }
+ splx(s);
+
+ timeout = 1 ; /* we got some data, so reset exp. wait */
+ /*
+ * if we are using /dev/audio and the device does not
+ * support it natively, we should do a format conversion.
+ * (in this case from uLAW to natural format).
+ * This can be messy in that it can require an intermediate
+ * buffer, and also screw up the byte count.
+ */
+ /*
+ * NOTE: I _can_ use rp here because it is not modified by the
+ * interrupt routines.
+ */
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(dsp_ulaw, b->buf + b->rp, l);
+ ret = uiomove(b->buf + b->rp, l, buf) ;
+ if (ret !=0 ) /* an error occurred ... */
+ break ;
+
+ s = spltty(); /* no interrupts here ... */
+ b->fl += l ; /* this more free bytes */
+ b->rl -= l ; /* this less ready bytes */
+ b->rp += l ; /* advance ready pointer */
+ if (b->rp == b->bufsize) /* handle wraps */
+ b->rp = 0 ;
+ splx(s) ;
+ bsz = min(b->bufsize, bsz*2);
+ }
+ s = spltty(); /* no interrupts here ... */
+ d->flags &= ~SND_F_READING ;
+ if (d->flags & SND_F_ABORTING) {
+ d->flags |= ~SND_F_ABORTING;
+ splx(s);
+ dsp_rdabort(d);
+ }
+ splx(s) ;
+ return ret ;
+}
+
+
+/*
+ * short routine to initialize a dma buffer descriptor (usually
+ * located in the XXX_desc structure). The first parameter is
+ * the buffer size, the second one specifies that a 16-bit dma channel
+ * is used (hence the buffer must be properly aligned).
+ */
+void
+alloc_dbuf(snd_dbuf *b, int size, int chan)
+{
+ if (size > 0x10000)
+ panic("max supported size is 64k");
+ b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,
+ 0ul, 0xfffffful, 1ul, 0x10000ul);
+ /* should check that it does not fail... */
+ b->dp = b->rp = b->fp = 0 ;
+ b->dl0 = b->dl = b->rl = 0 ;
+ b->bufsize = b->fl = size ;
+}
+
+void
+reset_dbuf(snd_dbuf *b)
+{
+ b->dp = b->rp = b->fp = 0 ;
+ b->dl0 = b->dl = b->rl = 0 ;
+ b->fl = b->bufsize ;
+}
+
+/*
+ * snd_sync waits until the space in the given channel goes above
+ * a threshold. chan = 1 : play, 2: capture. The threshold is
+ * checked against fl or rl respectively.
+ * Assume that the condition can become true, do not check here...
+ */
+int
+snd_sync(snddev_info *d, int chan, int threshold)
+{
+ u_long s;
+ int ret;
+ snd_dbuf *b;
+
+ b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;
+
+ for (;;) {
+ s=spltty();
+ if ( chan==1 )
+ dsp_wr_dmaupdate(d);
+ else
+ dsp_rd_dmaupdate(d);
+ if ( (chan == 1 && b->fl <= threshold) ||
+ (chan == 2 && b->rl <= threshold) ) {
+ ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);
+ splx(s);
+ if (ret == ERESTART || ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1 ;
+ }
+ } else
+ break;
+ }
+ splx(s);
+ return 0 ;
+}
+
+/*
+ * dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions
+ * which abort a pending DMA transfer and flush the buffers.
+ */
+int
+dsp_wrabort(snddev_info *d)
+{
+ long s;
+ int missing = 0;
+ snd_dbuf *b = & (d->dbuf_out) ;
+
+ s = spltty();
+ if ( d->flags & SND_F_WR_DMA ) {
+ d->flags &= ~ ( SND_F_WR_DMA | SND_F_WRITING ) ;
+ if (d->callback)
+ d->callback(d, SND_CB_WR | SND_CB_ABORT);
+ missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */
+
+ b->rl += missing ; /* which are part of the ready area */
+ b->rp -= missing ;
+ if (b->rp < 0)
+ b->rp += b->bufsize;
+ DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n",
+ b->dl - missing, b->dl));
+ b->dl -= missing;
+ dsp_wr_dmadone(d);
+ missing = b->rl;
+ }
+ reset_dbuf(b);
+ splx(s);
+ return missing;
+}
+
+int
+dsp_rdabort(snddev_info *d)
+{
+ long s;
+ int missing = 0;
+ snd_dbuf *b = & (d->dbuf_in) ;
+
+ s = spltty();
+ if ( d->flags & SND_F_RD_DMA ) {
+ d->flags &= ~ ( SND_F_RD_DMA | SND_F_READING ) ;
+ if (d->callback)
+ d->callback(d, SND_CB_RD | SND_CB_ABORT);
+ missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */
+
+ b->fl += missing ; /* which are part of the free area */
+ b->fp -= missing ;
+ if (b->fp < 0)
+ b->fp += b->bufsize;
+ DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n",
+ b->dl - missing, b->dl));
+ b->dl -= missing;
+ dsp_rd_dmadone(d);
+ missing = b->rl ;
+ }
+ reset_dbuf(b);
+ splx(s);
+ return missing;
+}
+
+/*
+ * this routine tries to flush the dma transfer. It is called
+ * on a close. The caller must set SND_F_CLOSING, and insure that
+ * interrupts are enabled. We immediately abort any read DMA
+ * operation, and then wait for the play buffer to drain.
+ */
+
+int
+snd_flush(snddev_info *d)
+{
+ int ret, res, res1;
+ int count=10;
+
+ DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));
+ dsp_rdabort(d);
+ if ( d->flags & SND_F_WR_DMA ) {
+ /* close write */
+ while ( d->flags & SND_F_WR_DMA ) {
+ /*
+ * still pending output data.
+ */
+ ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz);
+ if (ret == ERESTART || ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1 ;
+ }
+ if ( ret && --count == 0) {
+ printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n",
+ isa_dmastatus1(d->dma1), d->flags);
+ return -1 ;
+ }
+ }
+ d->flags &= ~SND_F_CLOSING ;
+ }
+ reset_dbuf(& (d->dbuf_out) );
+ return 0 ;
+}
+
+/*
+ * end of new code for dma buffer handling
+ */
diff --git a/sys/i386/isa/snd/mss.h b/sys/i386/isa/snd/mss.h
new file mode 100644
index 0000000..4b34e51
--- /dev/null
+++ b/sys/i386/isa/snd/mss.h
@@ -0,0 +1,250 @@
+/*
+ * file: mss.h
+ *
+ * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
+ *
+ * This file contains information and macro definitions for
+ * AD1848-compatible devices, used in the MSS/WSS compatible boards.
+ *
+ */
+
+/*
+ *
+
+The codec part of the board is seen as a set of 4 registers mapped
+at the base address for the board (default 0x534). Note that some
+(early) boards implemented 4 additional registers 4 location before
+(usually 0x530) to store configuration information. This is a source
+of confusion in that one never knows what address to specify. The
+(current) convention is to use the old address (0x530) in the kernel
+configuration file and consider MSS registers start four location
+ahead.
+
+ *
+ */
+
+/*
+ * The four visible registers of the MSS :
+ *
+ */
+
+#define io_Index_Addr(d) ((d)->io_base + 4)
+#define IA_BUSY 0x80 /* readonly, set when busy */
+#define IA_MCE 0x40 /* the MCE bit. */
+ /*
+ * the MCE bit must be set whenever the current mode of the
+ * codec is changed; this in particular is true for the
+ * Data Format (I8, I28) and Interface Config(I9) registers.
+ * Only exception are CEN and PEN which can be changed on the fly.
+ * The DAC output is muted when MCE is set.
+ */
+#define IA_TRD 0x20 /* Transfer request disable */
+ /*
+ * When TRD is set, DMA transfers cease when the INT bit in
+ * the MSS status reg is set. Must be cleared for automode
+ * DMA, set otherwise.
+ */
+#define IA_AMASK 0x1f /* mask for indirect address */
+
+#define io_Indexed_Data(d) ((d)->io_base+1+4)
+ /*
+ * data to be transferred to the indirect register addressed
+ * by index addr. During init and sw. powerdown, cannot be
+ * written to, and is always read as 0x80 (consistent with the
+ * busy flag).
+ */
+
+#define io_Status(d) ((d)->io_base+2+4)
+
+#define IS_CUL 0x80 /* capture upper/lower */
+#define IS_CLR 0x40 /* capture left/right */
+#define IS_CRDY 0x20 /* capture ready for programmed i/o */
+#define IS_SER 0x10 /* sample error (overrun/underrun) */
+#define IS_PUL 0x08 /* playback upper/lower */
+#define IS_PLR 0x04 /* playback left/right */
+#define IS_PRDY 0x02 /* playback ready for programmed i/o */
+#define IS_INT 0x01 /* int status (1 = active) */
+ /*
+ * IS_INT is clreared by any write to the status register.
+ */
+
+#define io_Polled_IO(d) ((d)->io_base+3+4)
+ /*
+ * this register is used in case of polled i/o
+ */
+
+/*
+ * The MSS has a set of 16 (or 32 depending on the model) indirect
+ * registers accessible through the data port by specifying the
+ * appropriate address in the address register.
+ *
+ * The 16 low registers are uniformly handled in AD1848/CS4248 compatible
+ * mode (often called MODE1). For the upper 16 registers there are
+ * some differences among different products, mainly Crystal uses them
+ * differently from OPTi.
+ *
+ */
+
+/*
+ * volume registers
+ */
+
+#define I6_MUTE 0x80
+
+/*
+ * register I9 -- interface configuration.
+ */
+
+#define I9_PEN 0x01 /* playback enable */
+#define I9_CEN 0x02 /* capture enable */
+
+/*
+ * values used in bd_flags
+ */
+#define BD_F_MCE_BIT 0x0001
+#define BD_F_IRQ_OK 0x0002
+#define BD_F_TMR_RUN 0x0004
+
+
+/*
+ * sound/ad1848_mixer.h
+ *
+ * Definitions for the mixer of AD1848 and compatible codecs.
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * 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.
+ */
+/*
+ * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
+ * Soundcard manufacturers have connected actual inputs (CD, synth, line,
+ * etc) to these inputs in different order. Therefore it's difficult
+ * to assign mixer channels to to these inputs correctly. The following
+ * contains two alternative mappings. The first one is for GUS MAX and
+ * the second is just a generic one (line1, line2 and line3).
+ * (Actually this is not a mapping but rather some kind of interleaving
+ * solution).
+ */
+#define GUSMAX_MIXER
+#ifdef GUSMAX_MIXER
+#define MODE1_REC_DEVICES \
+ (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX)
+
+#define MODE1_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX)
+
+#define MODE2_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#else /* Generic mapping */
+
+#define MODE1_REC_DEVICES \
+ (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX)
+
+#define MODE1_MIXER_DEVICES \
+ (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
+ SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#define MODE2_MIXER_DEVICES \
+ (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \
+ SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX)
+#endif
+
+#define OPTI931_MIXER_DEVICES \
+ (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN )
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual soundcards is not fixed.
+ * Some cards have assigned different meanings for the AUX1, AUX2
+ * and LINE inputs. Some have different features...
+ * The current version doesn't try to compensate this.
+ *
+ */
+
+mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
+MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
+MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
+MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+};
+
+mixer_ent opti931_devices[32][2] = { /* for the opti931 */
+MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5),
+MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4),
+MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1),
+MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
+MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+};
+
+static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
+ 0x5a5a, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4040, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x2020, /* Ext Line */
+ 0x4040, /* Mic */
+ 0x4b4b, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x2525, /* Input gain */
+ 0x0000, /* Output gain */
+ /* 0x4040, Line1 */
+ 0x0000, /* Line1 */
+ 0x0000, /* Line2 */
+ 0x1515 /* Line3 (usually line in)*/
+};
+
diff --git a/sys/i386/isa/snd/sb_dsp.c b/sys/i386/isa/snd/sb_dsp.c
new file mode 100644
index 0000000..4ca1f1f
--- /dev/null
+++ b/sys/i386/isa/snd/sb_dsp.c
@@ -0,0 +1,1080 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * driver for the SoundBlaster and clones.
+ *
+ * Copyright 1997 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);
+
+/*
+ * 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.
+ *
+ * Remebber, isa probe routines are supposed to return the
+ * size of io space used.
+ */
+
+static int
+sb_probe(struct isa_device *dev)
+{
+ if (dev->id_iobase == -1) {
+ dev->id_iobase = 0x220;
+ 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 int
+sb_attach(struct isa_device *dev)
+{
+ snddev_info *d = &pcm_info[dev->id_unit] ;
+
+ sb_dsp_init(d, dev);
+ return 0 ;
+}
+
+/*
+ * here are the main routines from the switches.
+ */
+
+static int
+sb_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->esel.si_pid = 0;
+ d->esel.si_flags = 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 ;
+ }
+
+ d->flags |= SND_F_BUSY ;
+ d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
+
+ if (flags & O_NONBLOCK)
+ d->flags |= SND_F_NBIO ;
+
+ reset_dbuf(& (d->dbuf_in) );
+ reset_dbuf(& (d->dbuf_out) );
+ sb_reset_dsp(d->io_base);
+ ask_init(d);
+
+ return 0;
+}
+
+static int
+sb_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 int
+sb_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.
+ */
+
+ return ENOSYS ;
+}
+
+static void
+sbintr(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%08x\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.
+ */
+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->dma1 < 4)
+ reason |= 1;
+ if (d->dma2 < 4)
+ reason |= 2;
+ }
+ if ( c & 2 ) { /* 16-bit dma */
+ if (d->dma1 >= 4)
+ reason |= 1;
+ if (d->dma2 >= 4)
+ reason |= 2;
+ }
+ }
+ if ( c & 2 )
+ inb(DSP_DATA_AVL16); /* 16-bit int ack */
+ if (c & 1)
+ inb(DSP_DATA_AVAIL); /* 8-bit int ack */
+
+DEB(printf("sbintr, flags 0x%08x reason %d\n", d->flags, reason));
+ if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
+ dsp_wrintr(d);
+ if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
+ dsp_rdintr(d);
+
+ /*
+ * 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 done 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 int
+sb_callback(snddev_info *d, int reason)
+{
+ u_long s ;
+ int rd = reason & SND_CB_RD ;
+ int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+
+ switch (reason & SND_CB_REASON_MASK) {
+ case SND_CB_INIT : /* called with int enabled and no pending io */
+ dsp_speed(d);
+ snd_set_blocksize(d);
+ if (d->play_fmt & AFMT_MU_LAW)
+ d->flags |= SND_F_XLAT8 ;
+ else
+ d->flags &= ~SND_F_XLAT8 ;
+ return 1;
+ break ;
+
+ case SND_CB_START : /* called with int disabled */
+ sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
+ d->flags &= ~SND_F_INIT ;
+ if (d->bd_flags & BD_F_SB16) {
+ /* the SB16 can do full duplex using one 16-bit channel
+ * and one 8-bit channel. It needs to be programmed to
+ * use split format though.
+ */
+ int b16 ;
+ int swap = 0 ;
+
+ b16 = (rd) ? d->rec_fmt : d->play_fmt ;
+ b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
+ /*
+ * check if I have to swap dma channels. Swap if
+ * - !rd, dma1 <4, b16
+ * - !rd, dma1 >=4, !b16
+ * - rd, dma2 <4, b16
+ * - rd, dma2 >=4, !b16
+ */
+ if (!rd) {
+ if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ } else {
+ if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ }
+ /*
+ * before swapping should make sure that there is no
+ * pending DMA on the other channel...
+ */
+ if (swap) {
+ int c = d->dma2 ;
+ d->dma2 = d->dma1;
+ d->dma1 = c ;
+ }
+ DEB(printf("sb_init: play %d rec %d dma1 %d dma2 %d\n",
+ d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
+ }
+ /* fallthrough */
+ case SND_CB_RESTART:
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
+ /*
+ * SB16 support still not completely working!!!
+ *
+ * in principle, on the SB16, I could support simultaneous
+ * play & rec.
+ * However, there is no way to ask explicitly for 8 or
+ * 16 bit transfer. As a consequence, if we do 8-bit,
+ * we need to use the 8-bit channel, and if we do 16-bit,
+ * we need to use the other one. The only way I find to
+ * do this is to swap d->dma1 and d->dma2 ...
+ *
+ */
+
+ if (rd) {
+ c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_FIFO_ON | DSP_F16_ADC ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ if (d->rec_fmt == AFMT_S16_LE)
+ l /= 2 ;
+ } else {
+ c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_FIFO_ON | DSP_F16_DAC ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ if (d->play_fmt == AFMT_S16_LE)
+ l /= 2 ;
+ }
+
+ if (d->flags & SND_F_STEREO)
+ c1 |= DSP_F16_STEREO ;
+
+ sb_cmd(d->io_base, c );
+ sb_cmd3(d->io_base, c1 , l - 1) ;
+ } else {
+ u_char c ;
+ if (d->bd_flags & BD_F_HISPEED)
+ c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ else
+ c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ sb_cmd3(d->io_base, c , l - 1) ;
+ }
+ break;
+
+ case SND_CB_STOP :
+ /* sb_cmd(d->io_base, DSP_CMD_SPKOFF); /* speaker off */
+ break ;
+
+ }
+ return 0 ;
+}
+
+/*
+ * The second part of the file contains all functions specific to
+ * the board and (usually) not exported to other modules.
+ */
+
+int
+sb_reset_dsp(int io_base)
+{
+ int loopc;
+
+ outb(DSP_RESET, 1);
+ DELAY(100);
+ outb(DSP_RESET, 0);
+ for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
+ DELAY(30);
+
+ if (inb(DSP_READ) != 0xAA) {
+ DEB(printf("sb_reset_dsp failed\n"));
+ return 0; /* Sorry */
+ }
+ return 1;
+}
+
+/*
+ * only used in sb_attach from here.
+ */
+
+static void
+sb_dsp_init(snddev_info *d, struct isa_device *dev)
+{
+ int i, x;
+ char *fmt = NULL ;
+ int io_base = dev->id_iobase ;
+
+ d->bd_id = 0 ;
+
+ sb_reset_dsp(io_base);
+ sb_cmd(io_base, DSP_CMD_GETVER); /* Get version */
+
+ for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */
+ if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */
+ if ( (d->bd_id & 0xff00) == 0)
+ d->bd_id = inb(DSP_READ) << 8; /* major */
+ else {
+ d->bd_id |= inb(DSP_READ); /* minor */
+ break;
+ }
+ } else
+ DELAY(20);
+ }
+
+ /*
+ * now do various initializations depending on board id.
+ */
+
+ fmt = "SoundBlaster %d.%d" ; /* default */
+
+ switch ( d->bd_id >> 8 ) {
+ case 0 :
+ printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n",
+ inb(DSP_DATA_AVAIL));
+ d->bd_id = 0x100;
+ case 1 : /* old sound blaster has nothing... */
+ break ;
+
+ case 2 :
+ d->dma2 = d->dma1 ; /* half duplex */
+ d->bd_flags |= BD_F_DUP_MIDI ;
+
+ if (d->bd_id == 0x200)
+ break ; /* no mixer on the 2.0 */
+ d->bd_flags &= ~BD_F_MIX_MASK ;
+ d->bd_flags |= BD_F_MIX_CT1335 ;
+
+ break ;
+ case 4 :
+ fmt = "SoundBlaster 16 %d.%d";
+ d->audio_fmt |= AFMT_FULLDUPLEX | AFMT_WEIRD | AFMT_S8 | AFMT_S16_LE;
+ d->bd_flags |= BD_F_SB16;
+ d->bd_flags &= ~BD_F_MIX_MASK ;
+ d->bd_flags |= BD_F_MIX_CT1745 ;
+
+ /* soft irq/dma configuration */
+ x = -1 ;
+ if (d->irq == 5) x = 2;
+ else if (d->irq == 7) x = 4;
+ else if (d->irq == 9) x = 1;
+ else if (d->irq == 10) x = 8;
+ if (x == -1)
+ printf("<%s>%d: bad irq %d (only 5,7,9,10 allowed)\n",
+ d->name, dev->id_unit, d->irq);
+ else
+ sb_setmixer(io_base, IRQ_NR, x);
+
+ sb_setmixer(io_base, DMA_NR, (1 << d->dma1) | (1 << d->dma2));
+ break ;
+
+ case 3 :
+ d->dma2 = d->dma1 ; /* half duplex */
+ fmt = "SoundBlaster Pro %d.%d";
+ d->bd_flags |= BD_F_DUP_MIDI ;
+ d->bd_flags &= ~BD_F_MIX_MASK ;
+ d->bd_flags |= BD_F_MIX_CT1345 ;
+ if (d->bd_id == 0x301) {
+ int ess_major = 0, ess_minor = 0;
+
+ /*
+ * Try to detect ESS chips.
+ */
+
+ sb_cmd(io_base, DSP_CMD_GETID); /* Return ident. bytes. */
+
+ for (i = 1000; i; i--) {
+ if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */
+ if (ess_major == 0)
+ ess_major = inb(DSP_READ);
+ else {
+ ess_minor = inb(DSP_READ);
+ break;
+ }
+ }
+ }
+
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ printf("Hmm... Could this be an ESS488 based card (rev %d)\n",
+ ess_minor & 0x0f);
+ else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
+ printf("Hmm... Could this be an ESS688 based card (rev %d)\n",
+ ess_minor & 0x0f);
+ }
+
+ if (d->bd_flags & BD_F_JAZZ16) {
+ if (d->bd_flags & BD_F_JAZZ16_2)
+ fmt = "SoundMan Wave %d.%d";
+ else
+ fmt = "MV Jazz16 %d.%d";
+ d->audio_fmt |= AFMT_S16_LE; /* 16 bits */
+ }
+ }
+
+ sprintf(d->name, fmt, (d->bd_id >> 8) &0xff, d->bd_id & 0xff);
+
+ sb_mix_init(d);
+}
+
+static void
+sb_mix_init(snddev_info *d)
+{
+ switch (d->bd_flags & BD_F_MIX_MASK) {
+ case BD_F_MIX_CT1345 : /* SB 3.0 has 1345 mixer */
+
+ d->mix_devs = SBPRO_MIXER_DEVICES ;
+ d->mix_rec_devs = SBPRO_RECORDING_DEVICES ;
+ d->mix_recsrc = SOUND_MASK_MIC ;
+
+ sb_setmixer(d->io_base, 0, 1 ); /* reset mixer */
+ sb_setmixer(d->io_base, MIC_VOL , 0x6 ); /* mic volume max */
+ sb_setmixer(d->io_base, RECORD_SRC , 0x0 ); /* mic source */
+ sb_setmixer(d->io_base, FM_VOL , 0x0 ); /* no midi */
+ break ;
+
+ case BD_F_MIX_CT1745 : /* SB16 mixer ... */
+
+ d->mix_devs = SB16_MIXER_DEVICES ;
+ d->mix_rec_devs = SB16_RECORDING_DEVICES ;
+ d->mix_recsrc = SOUND_MASK_MIC ;
+ }
+ sb_mixer_reset(d);
+}
+
+/*
+ * Common code for the midi and pcm functions
+ */
+
+int
+sb_cmd(int io_base, u_char val)
+{
+ int i;
+
+ for (i = 0; i < 1000 ; i++) {
+ if ((inb(DSP_STATUS) & 0x80) == 0) {
+ outb(DSP_COMMAND, val);
+ return 1;
+ }
+ if (i > 10)
+ DELAY (i > 100 ? 1000 : 10 );
+ }
+
+ printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val);
+ return 0;
+}
+
+int
+sb_cmd3(int io_base, u_char cmd, int val)
+{
+ if (sb_cmd(io_base, cmd)) {
+ sb_cmd(io_base, val & 0xff );
+ sb_cmd(io_base, (val>>8) & 0xff );
+ return 1 ;
+ } else
+ return 0;
+}
+
+int
+sb_cmd2(int io_base, u_char cmd, int val)
+{
+ if (sb_cmd(io_base, cmd)) {
+ sb_cmd(io_base, val & 0xff );
+ return 1 ;
+ } else
+ return 0;
+}
+
+void
+sb_setmixer(int io_base, u_int port, u_int value)
+{
+ u_long flags;
+
+ flags = spltty();
+ outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ DELAY(10);
+ outb(MIXER_DATA, (u_char) (value & 0xff));
+ DELAY(10);
+ splx(flags);
+}
+
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+int
+sb_getmixer(int io_base, u_int port)
+{
+ int val;
+ u_long flags;
+
+ flags = spltty();
+ outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ DELAY(10);
+ val = inb(MIXER_DATA);
+ DELAY(10);
+ splx(flags);
+
+ return val;
+}
+
+
+/*
+ * various utility functions for the DSP
+ */
+
+/*
+ * dsp_speed updates the speed setting from the descriptor. make sure
+ * it is called at spltty().
+ * Besides, it takes care of stereo setting.
+ */
+static int
+dsp_speed(snddev_info *d)
+{
+ u_char tconst;
+ u_long flags;
+ int max_speed = 44100, speed = d->play_speed ;
+
+ if (d->bd_flags & BD_F_SB16) {
+ RANGE (speed, 5000, 45000);
+ d->play_speed = d->rec_speed = speed ;
+ sb_cmd(d->io_base, 0x41);
+ sb_cmd(d->io_base, d->play_speed >> 8 );
+ sb_cmd(d->io_base, d->play_speed & 0xff );
+ sb_cmd(d->io_base, 0x42);
+ sb_cmd(d->io_base, d->rec_speed >> 8 );
+ sb_cmd(d->io_base, d->rec_speed & 0xff );
+ return speed ;
+ }
+ /*
+ * only some models can do stereo, and only if not
+ * simultaneously using midi.
+ */
+ if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
+ d->flags &= ~SND_F_STEREO;
+
+ /*
+ * here enforce speed limitations.
+ */
+ if (d->bd_id <= 0x200)
+ max_speed = 22050; /* max 22050 on SB 1.X */
+
+ /*
+ * SB models earlier than SB Pro have low limit for the
+ * input rate. Note that this is only for input, but since
+ * we do not support separate values for rec & play....
+ */
+ if (d->bd_id <= 0x200)
+ max_speed = 13000;
+ else if (d->bd_id < 0x300)
+ max_speed = 15000;
+
+ RANGE(speed, 4000, max_speed);
+
+ /*
+ * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
+ * stereo
+ */
+#if !defined (SM_GAMES)
+ /*
+ * Max. stereo speed is 22050
+ */
+ if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
+ speed = 22050;
+#endif
+
+ if ((speed > 22050) && d->bd_flags & BD_F_MIDIBUSY)
+ speed = 22050;
+
+ if (d->flags & SND_F_STEREO)
+ speed *= 2;
+
+ /*
+ * Now the speed should be valid. Compute the value to be
+ * programmed into the board.
+ */
+
+ if (speed > 22050) { /* High speed mode on 2.01/3.xx */
+ int tmp;
+
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ d->bd_flags |= BD_F_HISPEED ;
+
+ flags = spltty();
+ sb_cmd2(d->io_base, DSP_CMD_TCONST, tconst);
+ splx(flags);
+
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
+ } else {
+ int tmp;
+
+ d->bd_flags &= ~BD_F_HISPEED ;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+
+ flags = spltty();
+ sb_cmd2(d->io_base, DSP_CMD_TCONST, tconst);
+ splx(flags);
+
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
+ }
+
+ if (d->flags & SND_F_STEREO)
+ speed /= 2;
+
+ d->play_speed = d->rec_speed = speed;
+ return speed;
+}
+
+/*
+ * mixer support, originally in sb_mixer.c
+ */
+
+static void
+sb_set_recsrc(snddev_info *d, int mask)
+{
+ u_char outmask;
+ u_char recdev ;
+
+ mask &= d->mix_rec_devs;
+ switch (d->bd_flags & BD_F_MIX_MASK) {
+ case BD_F_MIX_CT1345 :
+ if (mask == SOUND_MASK_LINE)
+ recdev = 6 ;
+ else if (mask == SOUND_MASK_CD)
+ recdev = 2 ;
+ else { /* default: mic */
+ mask = SOUND_MASK_MIC ;
+ recdev = 0 ;
+ }
+ sb_setmixer(d->io_base, RECORD_SRC,
+ recdev | (sb_getmixer(d->io_base, RECORD_SRC) & ~7 ));
+ break ;
+ case BD_F_MIX_CT1745 : /* sb16 */
+ if (mask == 0)
+ mask = SOUND_MASK_MIC ; /* XXX For compatibility. Bug ? */
+ recdev = 0 ;
+ if (mask & SOUND_MASK_MIC)
+ recdev |= 1 ;
+ if (mask & SOUND_MASK_CD)
+ recdev |= 6 ; /* l+r cd */
+ if (mask & SOUND_MASK_LINE)
+ recdev |= 0x18 ; /* l+r line */
+ if (mask & SOUND_MASK_SYNTH)
+ recdev |= 0x60 ; /* l+r midi */
+ sb_setmixer(d->io_base, SB16_IMASK_L, recdev);
+ sb_setmixer(d->io_base, SB16_IMASK_R, recdev);
+ /*
+ * since the same volume controls apply to the input and
+ * output sections, the best approach to have a consistent
+ * behaviour among cards would be to disable the output path
+ * on devices which are used to record.
+ * However, since users like to have feedback, we only disable
+ * the mike -- permanently.
+ */
+ sb_setmixer(d->io_base, SB16_OMASK, 0x1f & ~1);
+ break ;
+ }
+ d->mix_recsrc = mask;
+}
+
+static void
+sb_mixer_reset(snddev_info *d)
+{
+ int i;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ sb_mixer_set(d, i, levels[i]);
+ if (d->bd_flags & BD_F_SB16) {
+ sb_setmixer(d->io_base, 0x3c, 0x1f); /* make all output active */
+ sb_setmixer(d->io_base, 0x3d, 0); /* make all inputs-l off */
+ sb_setmixer(d->io_base, 0x3e, 0); /* make all inputs-r off */
+ }
+ sb_set_recsrc(d, SOUND_MASK_MIC);
+}
+
+static int
+sb_mixer_set(snddev_info *d, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int regoffs;
+ u_char val;
+ mixer_tab *iomap;
+
+#ifdef JAZZ16
+ if (d->bd_flags & BD_F_JAZZ16 && d->bd_flags & BD_F_JAZZ16_2)
+ return smw_mixer_set(dev, value);
+#endif
+
+ if (dev == SOUND_MIXER_RECSRC) {
+ sb_set_recsrc(d, value);
+ return 0 ;
+ }
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return EINVAL ;
+
+ if (!(d->mix_devs & (1 << dev))) /* Not supported */
+ return EINVAL;
+
+ switch ( d->bd_flags & BD_F_MIX_MASK ) {
+ default:
+ /* mixer unknown, fail... */
+ return EINVAL ;/* XXX change this */
+ case BD_F_MIX_CT1345 :
+ iomap = &sbpro_mix ;
+ break;
+ case BD_F_MIX_CT1745 :
+ iomap = &sb16_mix ;
+ break;
+ /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */
+ }
+ regoffs = (*iomap)[dev][LEFT_CHN].regno;
+ if (regoffs == 0)
+ return EINVAL;
+
+ val = sb_getmixer(d->io_base, regoffs);
+
+ change_bits(iomap, &val, dev, LEFT_CHN, left);
+
+ d->mix_levels[dev] = left | (left << 8);
+
+ if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */
+ sb_setmixer(d->io_base, regoffs, val); /* Save the old one */
+ regoffs = (*iomap)[dev][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return 0 ; /* Just left channel present */
+
+ val = sb_getmixer(d->io_base, regoffs); /* Read the new one */
+ }
+ change_bits(iomap, &val, dev, RIGHT_CHN, right);
+
+ sb_setmixer(d->io_base, regoffs, val);
+
+ d->mix_levels[dev] = left | (right << 8);
+ return 0 ; /* ok */
+}
+
+/*
+ * now support for some PnP boards.
+ */
+
+#if NPNP > 0
+static char *opti925_probe(u_long csn, u_long vend_id);
+static void opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device opti925 = {
+ "opti925",
+ opti925_probe,
+ opti925_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, opti925);
+
+static char *
+opti925_probe(u_long csn, u_long vend_id)
+{
+ if (vend_id == 0x2509143e) {
+ struct pnp_cinfo d ;
+ read_pnp_parms ( &d , 1 ) ;
+ if (d.enable == 0) {
+ printf("This is an OPTi925, but LDN 1 is disabled\n");
+ return NULL;
+ }
+ return "OPTi925" ;
+ }
+ return NULL ;
+}
+
+static void
+opti925_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+ int the_irq = 0 ;
+
+ tmp_d = sb_op_desc;
+ snddev_last_probed = &tmp_d;
+
+ read_pnp_parms ( &d , 3 ); /* disable LDN 3 */
+ the_irq = d.irq[0];
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 3 );
+
+ read_pnp_parms ( &d , 2 ); /* disable LDN 2 */
+ d.port[0] = 0 ;
+ d.enable = 0 ;
+ write_pnp_parms ( &d , 2 );
+
+ read_pnp_parms ( &d , 1 ) ;
+ d.irq[0] = the_irq ;
+ dev->id_iobase = d.port[0];
+ write_pnp_parms ( &d , 1 );
+ enable_pnp_card();
+
+ tmp_d.conf_base = d.port[3];
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
+
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+
+ pcmattach(dev);
+
+}
+
+/*
+ * A driver for some SB16pnp and compatibles...
+ *
+ * Avance Asound 100 -- 0x01009305
+ * xxx -- 0x2b008c0e
+ *
+ */
+
+static char *sb16pnp_probe(u_long csn, u_long vend_id);
+static void sb16pnp_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device sb16pnp = {
+ "SB16pnp",
+ sb16pnp_probe,
+ sb16pnp_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, sb16pnp);
+
+static char *
+sb16pnp_probe(u_long csn, u_long vend_id)
+{
+ char *s = NULL ;
+ if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
+ if (vend_id == 0x2b008c0e)
+ s = "SB16 Value PnP" ;
+ /*
+ * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * the vendor id, so I have just masked it for the time being...
+ * Reported values are:
+ * SB16 Value PnP: 0x2b008c0e
+ * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ */
+ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ s = "SB AWE64 PnP";
+ if (s) {
+ struct pnp_cinfo d;
+ read_pnp_parms(&d, 0);
+ if (d.enable == 0) {
+ printf("This is a %s, but LDN 0 is disabled\n", s);
+ return NULL ;
+ }
+ return s ;
+ }
+ return NULL ;
+}
+
+static void
+sb16pnp_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev)
+{
+ struct pnp_cinfo d ;
+ snddev_info tmp_d ; /* patched copy of the basic snddev_info */
+
+ tmp_d = sb_op_desc;
+ snddev_last_probed = &tmp_d;
+
+ read_pnp_parms ( &d , 0 ) ;
+ d.port[1] = 0 ; /* only the first address is used */
+ dev->id_iobase = d.port[0];
+ write_pnp_parms ( &d , 0 );
+ enable_pnp_card();
+
+ dev->id_drq = d.drq[0] ; /* primary dma */
+ dev->id_irq = (1 << d.irq[0] ) ;
+ dev->id_intr = pcmintr ;
+ dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ;
+
+ pcm_info[dev->id_unit] = tmp_d;
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+
+ pcmattach(dev);
+}
+#endif /* NPNP */
+
+#endif
diff --git a/sys/i386/isa/snd/sbcard.h b/sys/i386/isa/snd/sbcard.h
new file mode 100644
index 0000000..8548997
--- /dev/null
+++ b/sys/i386/isa/snd/sbcard.h
@@ -0,0 +1,387 @@
+/*
+ * file: sbcard.h
+ */
+
+typedef struct _sbdev_info {
+
+} sbdev_info ;
+
+extern int sbc_major, sbc_minor ;
+/*
+ * sound blaster registers
+ */
+
+#define DSP_RESET (io_base + 0x6)
+#define DSP_READ (io_base + 0xA)
+#define DSP_WRITE (io_base + 0xC)
+#define DSP_COMMAND (io_base + 0xC)
+#define DSP_STATUS (io_base + 0xC)
+#define DSP_DATA_AVAIL (io_base + 0xE)
+#define DSP_DATA_AVL16 (io_base + 0xF)
+#define MIXER_ADDR (io_base + 0x4)
+#define MIXER_DATA (io_base + 0x5)
+#define OPL3_LEFT (io_base + 0x0)
+#define OPL3_RIGHT (io_base + 0x2)
+#define OPL3_BOTH (io_base + 0x8)
+
+/*
+ * DSP Commands. There are many, and in many cases they are used explicitly
+ */
+
+#define DSP_DAC8 0x10 /* direct DAC output */
+#define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */
+#define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */
+#define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */
+
+#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */
+#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */
+
+#define DSP_ADC8 0x20 /* direct ADC input */
+
+#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */
+#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */
+
+#define DSP_CMD_O16 0xb0
+#define DSP_CMD_I16 0xb8
+#define DSP_CMD_O8 0xc0
+#define DSP_CMD_I8 0xc8
+
+#define DSP_MODE_U8MONO 0x00
+#define DSP_MODE_U8STEREO 0x20
+#define DSP_MODE_S16MONO 0x10
+#define DSP_MODE_S16STEREO 0x30
+
+#define DSP_CMD_SPKON 0xD1
+#define DSP_CMD_SPKOFF 0xD3
+#define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2))
+#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */
+#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */
+
+#define DSP_CMD_DMAHALT 0xD0
+#define DSP_CMD_TCONST 0x40 /* set time constant */
+#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */
+#define DSP_CMD_HSDAC 0x91 /* high speed dac */
+#define DSP_CMD_HSADC 0x99 /* high speed adc */
+
+#define DSP_CMD_GETVER 0xE1
+#define DSP_CMD_GETID 0xE7 /* return id bytes */
+
+ /* prepare for dma input */
+#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0))
+
+#define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */
+#define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */
+#if 0 /*** unknown ***/
+ /*
+ * D9 and D5 are used on the sb16 on close... maybe a reset of
+ * some subsystem ?
+ */
+#define DSP_CMD_D9 0xD9
+#define DSP_CMD_D5 0xD5
+#define DSP_CMD_FA 0xFA /* get version from prosonic*/
+#define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/
+#endif
+
+/*
+ * in fact, for the SB16, dma commands are as follows:
+ *
+ * cmd, mode, len_low, len_high.
+ *
+ * cmd is a combination of DSP_DMA16 or DSP_DMA8 and
+ */
+
+#define DSP_DMA16 0xb0
+#define DSP_DMA8 0xc0
+# define DSP_F16_DAC 0x00
+# define DSP_F16_ADC 0x08
+# define DSP_F16_AUTO 0x04
+# define DSP_F16_FIFO_ON 0x02
+
+/*
+ * mode is a combination of the following:
+ */
+#define DSP_F16_STEREO 0x20
+#define DSP_F16_SIGNED 0x10
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
+#define IMODE_INPUT PCM_ENABLE_INPUT
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+#define NORMAL_MIDI 0
+#define UART_MIDI 1
+
+/*
+ * values used for bd_flags in SoundBlaster driver
+ */
+#define BD_F_HISPEED 0x0001 /* doing high speed ... */
+
+#define BD_F_JAZZ16 0x0002 /* jazz16 detected */
+#define BD_F_JAZZ16_2 0x0004 /* jazz16 type 2 */
+
+#define BD_F_DUP_MIDI 0x0008 /* duplex midi */
+
+#define BD_F_MIX_MASK 0x0070 /* up to 8 mixers (I know of 3) */
+#define BD_F_MIX_CT1335 0x0010 /* CT1335 */
+#define BD_F_MIX_CT1345 0x0020 /* CT1345 */
+#define BD_F_MIX_CT1745 0x0030 /* CT1745 */
+
+#define BD_F_SB16 0x0100 /* this is a SB16 */
+#define BD_F_NOREC 0x0200 /* recording not supported on this board */
+#define BD_F_MIDIBUSY 0x0400 /* midi busy */
+
+
+/*
+ * sound/sb_mixer.h
+ *
+ * Definitions for the SB Pro and SB16 mixers
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro
+ * mixer.
+ *
+ */
+
+#define SBPRO_RECORDING_DEVICES \
+ (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
+#define SBPRO_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/*
+ * SG NX Pro has treble and bass settings on the mixer. The 'speaker' channel
+ * is the COVOX/DisneySoundSource emulation volume control on the mixer. It
+ * does NOT control speaker volume. Should have own mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES \
+ (SBPRO_MIXER_DEVICES | SOUND_MASK_BASS | \
+ SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER )
+
+#define SB16_RECORDING_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+#define SB16_MIXER_DEVICES \
+ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
+ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
+
+/*
+ * Mixer registers
+ *
+ * NOTE! RECORD_SRC == IN_FILTER
+ */
+
+/*
+ * Mixer registers of SB Pro
+ */
+#define VOC_VOL 0x04
+#define MIC_VOL 0x0A
+#define MIC_MIX 0x0A
+#define RECORD_SRC 0x0C
+#define IN_FILTER 0x0C
+#define OUT_FILTER 0x0E
+#define MASTER_VOL 0x22
+#define FM_VOL 0x26
+#define CD_VOL 0x28
+#define LINE_VOL 0x2E
+#define IRQ_NR 0x80
+#define DMA_NR 0x81
+#define IRQ_STAT 0x82
+
+/*
+ * Additional registers on the SG NX Pro
+ */
+#define COVOX_VOL 0x42
+#define TREBLE_LVL 0x44
+#define BASS_LVL 0x46
+
+#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
+#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
+#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
+#define FILT_OFF (1 << 5)
+
+#define MONO_DAC 0x00
+#define STEREO_DAC 0x02
+
+/*
+ * Mixer registers of SB16
+ */
+#define SB16_IMASK_L 0x3d
+#define SB16_IMASK_R 0x3e
+#define SB16_OMASK 0x3c
+
+
+#ifndef __SB_MIXER_C__
+mixer_tab sbpro_mix;
+mixer_tab sb16_mix;
+#ifdef __SGNXPRO__
+mixer_tab sgnxpro_mix;
+#endif
+static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES];
+static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES];
+#else /* __SB_MIXER_C__ defined */
+mixer_tab sbpro_mix = {
+ PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+ PMIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+ PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+ PMIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+ PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+ PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+
+#ifdef __SGNXPRO__
+mixer_tab sgnxpro_mix = {
+ PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+ PMIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+ PMIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+ PMIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+ PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+ PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
+
+mixer_tab sb16_mix = {
+ PMIX_ENT(SOUND_MIXER_VOLUME, 0x30, 3, 5, 0x31, 3, 5),
+ PMIX_ENT(SOUND_MIXER_BASS, 0x46, 4, 4, 0x47, 4, 4),
+ PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 4, 4, 0x45, 4, 4),
+ PMIX_ENT(SOUND_MIXER_SYNTH, 0x34, 3, 5, 0x35, 3, 5),
+ PMIX_ENT(SOUND_MIXER_PCM, 0x32, 3, 5, 0x33, 3, 5),
+ PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 6, 2, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_LINE, 0x38, 3, 5, 0x39, 3, 5),
+ PMIX_ENT(SOUND_MIXER_MIC, 0x3a, 3, 5, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_CD, 0x36, 3, 5, 0x37, 3, 5),
+ PMIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+ PMIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 6, 2, 0x40, 6, 2), /* Obsol,Use IGAIN*/
+ PMIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 6, 2, 0x40, 6, 2),
+ PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2)
+};
+
+#ifdef SM_GAMES /* Master volume is lower and PCM & FM
+ * volumes higher than with SB Pro. This
+ * improves the sound quality */
+
+static u_short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x2020, /* Master Volume */
+ 0x4b4b, /* Bass */
+ 0x4b4b, /* Treble */
+ 0x6464, /* FM */
+ 0x6464, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+0x4b4b}; /* Output gain */
+
+#else /* If the user selected just plain SB Pro */
+
+static u_short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x5a5a, /* Master Volume */
+ 0x4b4b, /* Bass */
+ 0x4b4b, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4b4b, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x1010, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+0x4b4b}; /* Output gain */
+#endif /* SM_GAMES */
+
+static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x40, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x10, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x04, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00, /* SOUND_MIXER_RECLEV */
+ 0x00, /* SOUND_MIXER_IGAIN */
+ 0x00 /* SOUND_MIXER_OGAIN */
+};
+
+static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x20, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x08, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x02, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00, /* SOUND_MIXER_RECLEV */
+ 0x00, /* SOUND_MIXER_IGAIN */
+ 0x00 /* SOUND_MIXER_OGAIN */
+};
+
+/*
+ * Recording sources (SB Pro)
+ */
+#endif /* __SB_MIXER_C__ */
+
+#define SRC_MIC 1 /* Select Microphone recording source */
+#define SRC_CD 3 /* Select CD recording source */
+#define SRC_LINE 7 /* Use Line-in for recording source */
+
+
diff --git a/sys/i386/isa/snd/sound.c b/sys/i386/isa/snd/sound.c
new file mode 100644
index 0000000..a9b5fd5
--- /dev/null
+++ b/sys/i386/isa/snd/sound.c
@@ -0,0 +1,1254 @@
+/*
+ * sound/sound.c
+ *
+ * Main sound driver for FreeBSD. This file provides the main
+ * entry points for probe/attach and all i/o demultiplexing, including
+ * default routines for generic devices.
+ *
+ * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
+ *
+ * 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.
+ *
+ *
+ * For each board type a template "snddev_info" structure contains
+ * all the relevant parameters, both for configuration and runtime.
+ *
+ * In this file we build tables of pointers to the descriptors for
+ * the various supported boards. The generic probe routine scans
+ * the table(s) looking for a matching entry, then invokes the
+ * board-specific probe routine. If successful, a pointer to the
+ * correct snddev_info is stored in snddev_last_probed, for subsequent
+ * use in the attach routine. The generic attach routine copies
+ * the template to a permanent descriptor (pcm_info[unit] and
+ * friends), initializes all generic parameters, and calls the
+ * board-specific attach routine.
+ *
+ * On device calls, the generic routines do the checks on unit and
+ * device parameters, then call the board-specific routines if
+ * available, or try to perform the task using the default code.
+ *
+ */
+
+#include <i386/isa/snd/sound.h>
+
+#if NPCM > 0 /* from "snd.h" */
+
+#define SNDSTAT_BUF_SIZE 4000
+static char status_buf[SNDSTAT_BUF_SIZE] ;
+static int status_len = 0 ;
+static void init_status(snddev_info *d);
+
+static d_open_t sndopen;
+static d_close_t sndclose;
+static d_ioctl_t sndioctl;
+static d_read_t sndread;
+static d_write_t sndwrite;
+static d_mmap_t sndmmap;
+
+#define CDEV_MAJOR 30
+static struct cdevsw snd_cdevsw = {
+ sndopen, sndclose, sndread, sndwrite,
+ sndioctl, nxstop, nxreset, nxdevtotty,
+ sndselect, sndmmap, nxstrategy, "snd",
+ NULL, -1,
+};
+
+/*
+ * descriptors for active devices.
+ *
+ */
+snddev_info pcm_info[NPCM_MAX] ;
+snddev_info midi_info[NPCM_MAX] ;
+snddev_info synth_info[NPCM_MAX] ;
+
+u_long nsnd = NPCM ; /* total number of sound devices */
+
+/*
+ * the probe routine can only return an int to the upper layer. Hence,
+ * it leaves the pointer to the last successfully
+ * probed device descriptor in snddev_last_probed
+ */
+snddev_info *snddev_last_probed = NULL ;
+
+static void print_isadev_info(struct isa_device *d, char *s);
+static snddev_info *
+generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s);
+
+/*
+ * here are the lists of known devices. Similar devices (e.g. all
+ * sb clones, all mss clones, ... are in the same array.
+ * All lists of devices of the same type (eg. all pcm, all midi...)
+ * are in the same array.
+ * Each probe for a device type gets the pointer to the main array
+ * and then scans the sublists.
+ *
+ * XXX should use DATA_SET to create a linker set for sb_devs and other
+ * such structures.
+ */
+
+extern snddev_info sb_op_desc;
+extern snddev_info mss_op_desc;
+
+static snddev_info *sb_devs[] = {
+ &sb_op_desc,
+ NULL,
+} ;
+
+static snddev_info *mss_devs[] = {
+ /* MSS-like devices */
+ &mss_op_desc,
+ NULL,
+} ;
+
+static snddev_info **pcm_devslist[] = {
+ /* all pcm devices */
+ mss_devs,
+ sb_devs,
+ NULL
+} ;
+
+
+int
+pcmprobe(struct isa_device * dev)
+{
+ bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) );
+ return generic_snd_probe(dev, pcm_devslist, "pcm") ? 1 : 0 ;
+}
+
+static snddev_info **midi_devslist[] = {
+ /* all midi devices */
+ NULL
+} ;
+
+int
+midiprobe(struct isa_device * dev)
+{
+ bzero(&midi_info[dev->id_unit], sizeof(midi_info[dev->id_unit]) );
+ return 0 ;
+ return generic_snd_probe(dev, midi_devslist, "midi") ? 1 : 0 ;
+}
+
+int
+synthprobe(struct isa_device * dev)
+{
+ bzero(&synth_info[dev->id_unit], sizeof(synth_info[dev->id_unit]) );
+ return 0 ;
+}
+
+/*
+ * this is the generic attach routine
+ */
+
+int
+pcmattach(struct isa_device * dev)
+{
+ snddev_info *d = NULL ;
+ struct isa_device *dvp;
+ int stat = 0;
+ dev_t isadev;
+
+ if ( (dev->id_unit >= NPCM_MAX) || /* too many devs */
+ (snddev_last_probed == NULL) || /* last probe failed */
+ (snddev_last_probed->attach==NULL) ) /* no attach routine */
+ return 0 ; /* fail */
+
+ /*
+ * default initialization: copy generic parameters for the routine,
+ * initialize from the isa_device structure, and allocate memory.
+ * If everything succeeds, then call the attach routine for
+ * further initialization.
+ */
+ pcm_info[dev->id_unit] = *snddev_last_probed ;
+ d = &pcm_info[dev->id_unit] ;
+
+ d->io_base = dev->id_iobase ;
+ d->irq = ffs(dev->id_irq) - 1 ;
+ d->dma1 = dev->id_drq ;
+ if (dev->id_flags != -1 && dev->id_flags & DV_F_DUAL_DMA) /* enable dma2 */
+ d->dma2 = dev->id_flags & DV_F_DRQ_MASK ;
+ else
+ d->dma2 = d->dma1 ;
+ /* XXX should also set bd_id from flags ? */
+ d->status_ptr = 0;
+
+ /*
+ * Allocates memory and initializes the dma structs properly. We
+ * use independent buffers for each channel. For the time being,
+ * this is done independently of the dma setting. In future
+ * revisions, if we see that we have a single dma, we might decide
+ * to use a single buffer to save memory.
+ */
+ alloc_dbuf( &(d->dbuf_out), d->bufsize, d->dma1 );
+ alloc_dbuf( &(d->dbuf_in), d->bufsize, d->dma2 );
+
+ isa_dma_acquire(d->dma1);
+ if (d->dma2 != d->dma1)
+ isa_dma_acquire(d->dma2);
+ /*
+ * initialize standard parameters for the device. This can be
+ * overridden by device-specific configurations but better do
+ * here the generic things.
+ */
+
+ d->play_speed = d->rec_speed = 8000 ;
+ d->play_blocksize = d->rec_blocksize = 2048 ;
+ d->play_fmt = d->rec_fmt = AFMT_MU_LAW ;
+
+ isadev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&isadev, &snd_cdevsw, NULL);
+
+ /*
+ * should try and find a suitable value for id_id, otherwise
+ * the interrupt is not registered and dispatched properly.
+ * This is important for PnP devices, where "dev" is built on
+ * the fly and many field are not initialized.
+ */
+ if (dev->id_driver == NULL) {
+ dev->id_driver = &pcmdriver ;
+ dvp=find_isadev(isa_devtab_tty, &pcmdriver, 0);
+ if (dvp)
+ dev->id_id = dvp->id_id;
+ }
+
+ /*
+ * and finally, call the device attach routine
+ */
+ stat = snddev_last_probed->attach(dev);
+ snddev_last_probed = NULL ;
+
+ return stat ;
+}
+
+int midiattach(struct isa_device * dev) { return 0 ; }
+int synthattach(struct isa_device * dev) { return 0 ; }
+
+struct isa_driver pcmdriver = { pcmprobe, pcmattach, "pcm" } ;
+
+struct isa_driver mididriver = { midiprobe, midiattach, "midi" } ;
+struct isa_driver synthdriver = { synthprobe, synthattach, "synth" } ;
+
+void
+pcmintr(int unit)
+{
+ DEB(printf("__/\\/ pcmintr -- unit %d\n", unit));
+ pcm_info[unit].interrupts++;
+ if (pcm_info[unit].isr)
+ pcm_info[unit].isr(unit);
+ if (midi_info[unit].isr)
+ midi_info[unit].isr(unit);
+ if (synth_info[unit].isr)
+ synth_info[unit].isr(unit);
+}
+
+static void
+print_isadev_info(struct isa_device *d, char *s)
+{
+ if (d == NULL )
+ return ;
+ printf("%s%d at 0x%x irq %d drq %d mem 0x%x flags 0x%x en %d confl %d\n",
+ d->id_driver ? d->id_driver->name : "NONAME",
+ d->id_unit,
+ (u_short)(d->id_iobase), ffs(d->id_irq) - 1 ,
+ d->id_drq, d->id_maddr, d->id_flags,
+ d->id_enabled, d->id_conflicts);
+}
+
+
+static snddev_info *
+generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s)
+{
+ snddev_info **q ;
+ struct isa_device saved_dev ;
+
+ snddev_last_probed = NULL ;
+
+ print_isadev_info(dev, s);
+
+ saved_dev = *dev ; /* the probe routine might alter parameters */
+
+ for ( ; p[0] != NULL ; p++ )
+ for ( q = *p ; q[0] ; q++ )
+ if (q[0]->probe && q[0]->probe(dev))
+ return (snddev_last_probed = q[0]) ;
+ else
+ *dev = saved_dev ;
+
+ return NULL ;
+}
+
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated snddev_info struct, and sets the unit
+ * number.
+ */
+static snddev_info *
+get_snddev_info(dev_t dev, int *unit)
+{
+ int u;
+ snddev_info *d = NULL ;
+
+ dev = minor(dev);
+ u = dev >> 4 ;
+ if (unit)
+ *unit = u ;
+
+ if (u > NPCM_MAX)
+ return NULL ;
+ switch(dev & 0x0f) {
+ case SND_DEV_CTL : /* /dev/mixer handled by pcm */
+ case SND_DEV_STATUS : /* /dev/sndstat handled by pcm */
+ case SND_DEV_SNDPROC : /* /dev/sndproc handled by pcm */
+ case SND_DEV_DSP :
+ case SND_DEV_DSP16 :
+ case SND_DEV_AUDIO :
+ d = & pcm_info[u] ;
+ break ;
+ case SND_DEV_SEQ :
+ case SND_DEV_SEQ2 :
+ d = & synth_info[u] ;
+ break ;
+ case SND_DEV_MIDIN:
+ d = & midi_info[u] ;
+ break ;
+ default:
+ return NULL ;
+ }
+ return d ;
+}
+
+/*
+ * here are the switches for the main functions. The switches do
+ * all necessary checks on the device number to make sure
+ * that the device is configured. They also provide some default
+ * functionalities so that device-specific drivers have to deal
+ * only with special cases.
+ */
+
+static int
+sndopen(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ int dev, unit ;
+ snddev_info *d;
+
+ dev = minor(i_dev);
+ d = get_snddev_info(dev, &unit);
+
+ DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
+ unit, dev & 0xf, flags, mode));
+
+ if (d == NULL) {
+ printf("open: unit %d dev %d not configured, perhaps you want unit %d ?\n",
+ unit, dev & 0xf, unit + 1 );
+ return (ENXIO) ;
+ }
+
+ switch(dev & 0x0f) {
+ case SND_DEV_CTL : /* mixer ... */
+ return 0 ; /* always succeed */
+
+ case SND_DEV_STATUS : /* implemented right here */
+ init_status(&pcm_info[unit]);
+ d->status_ptr = 0 ;
+ return 0 ;
+
+ default:
+ if (d->open == NULL) {
+ printf("open: missing for unit %d\n", unit );
+ return (ENXIO) ;
+ } else
+ return d->open(i_dev, flags, mode, p);
+ }
+ return ENXIO ;
+}
+
+static int
+sndclose(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ int dev, unit ;
+ snddev_info *d;
+
+ dev = minor(i_dev);
+ d = get_snddev_info(dev, &unit);
+
+ DEB(printf("close snd%d subdev %d\n", unit, dev & 0xf));
+
+ if (d == NULL) {
+ printf("close: unit %d dev %d not configured\n", unit, dev & 0xf );
+ return (ENXIO) ;
+ }
+ switch(dev & 0x0f) { /* only those for which close makes sense */
+ case SND_DEV_AUDIO :
+ case SND_DEV_DSP :
+ case SND_DEV_DSP16 :
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ case SND_DEV_MIDIN:
+ if (d->close)
+ return d->close(i_dev, flags, mode, p);
+ }
+ return 0 ;
+}
+
+static int
+sndread(dev_t i_dev, struct uio * buf, int flag)
+{
+ int ret, dev, unit;
+ snddev_info *d ;
+ u_long s;
+
+ dev = minor(i_dev);
+
+ d = get_snddev_info(dev, &unit);
+ DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ if (d == NULL) {
+ printf("read: unit %d not configured\n", unit );
+ return ENXIO ;
+ }
+ if ( (dev & 0x0f) == SND_DEV_STATUS ) {
+ int l, c;
+ u_char *p;
+
+ l = buf->uio_resid;
+ s=spltty();
+ c = status_len - d->status_ptr ;
+ if (c < 0) /* should not happen! */
+ c = 0 ;
+ if (c < l)
+ l = c ;
+ p = status_buf + d->status_ptr ;
+ d->status_ptr += l ;
+ splx(s);
+ ret = uiomove(p, l, buf) ;
+ if (ret)
+ printf("pcm-stat: bad copyout\n");
+ return ret ;
+ }
+ if (d->read) /* device-specific read */
+ return d->read(i_dev, buf, flag);
+
+ /*
+ * the generic read routine. device-specific stuff should only
+ * be in the dma-handling procedures.
+ */
+ s = spltty();
+ if ( d->flags & SND_F_READING ) {
+ /* another reader is in, deny request */
+ splx(s);
+ DDB(printf("read denied, another reader is in\n"));
+ return EBUSY ;
+ }
+ if (d->dma1 == d->dma2) { /* half duplex */
+ if ( d->flags & SND_F_WRITING ) {
+ /* another writer is in, deny request */
+ splx(s);
+ DDB(printf("read denied, half duplex and a writer is in\n"));
+ return EBUSY ;
+ }
+ while ( d->flags & SND_F_WR_DMA ) {
+ /*
+ * we have a pending dma operation, post a read request
+ * and wait for the write to complete.
+ */
+ d->flags |= SND_F_READING ;
+ DDB(printf("sndread: sleeping waiting for write to end\n"));
+ ret = tsleep( (caddr_t)&(d->dbuf_out),
+ PRIBIO | PCATCH , "sndrdw", hz ) ;
+ if (ret == ERESTART || ret == EINTR) {
+ d->flags &= ~SND_F_READING ;
+ splx(s);
+ return EINTR ;
+ }
+ }
+ }
+ d->flags |= SND_F_READING ;
+ splx(s);
+
+ return dsp_read_body(d, buf);
+}
+
+static int
+sndwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ int ret, dev, unit;
+ snddev_info *d;
+ u_long s;
+
+ dev = minor(i_dev);
+ d = get_snddev_info(dev, &unit);
+
+ DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ if (d == NULL) {
+ printf("write: unit %d not configured\n", unit );
+ return (ENXIO) ;
+ }
+ switch( dev & 0x0f) { /* only writeable devices */
+ case SND_DEV_MIDIN: /* XXX is this writable ? */
+ case SND_DEV_SEQ :
+ case SND_DEV_SEQ2 :
+ case SND_DEV_DSP :
+ case SND_DEV_DSP16 :
+ case SND_DEV_AUDIO :
+ break ;
+ default:
+ return EPERM ; /* for non-writeable devices ; */
+ }
+ if (d->write)
+ return d->write(i_dev, buf, flag);
+
+ /*
+ * Otherwise, use the generic write routine. device-specific
+ * stuff should only be in the dma-handling procedures.
+ */
+
+ s = spltty();
+ if ( d->flags & SND_F_WRITING ) {
+ /* another writer is in, deny request */
+ splx(s);
+ DDB(printf("write denied, another writer is in\n"));
+ return EBUSY ;
+ }
+ if (d->dma1 == d->dma2) { /* half duplex */
+ if ( d->flags & SND_F_READING ) {
+ DDB(printf("write denied, half duplex and a reader is in\n"));
+ /* another reader is in, deny request */
+ splx(s);
+ return EBUSY ;
+ }
+ while ( d->flags & SND_F_RD_DMA ) {
+ /*
+ * we have a pending read dma. Post a write request
+ * and wait for the read to complete (in fact I could
+ * abort the read dma...
+ */
+ d->flags |= SND_F_WRITING ;
+ DEB(printf("sndwrite: sleeping waiting for read to end\n"));
+ ret = tsleep( (caddr_t)&(d->dbuf_out),
+ PRIBIO | PCATCH , "sndwr", hz ) ;
+ if (ret == ERESTART || ret == EINTR) {
+ d->flags &= ~SND_F_WRITING ;
+ splx(s);
+ return EINTR ;
+ }
+ }
+ }
+ d->flags |= SND_F_WRITING ;
+ splx(s);
+
+ return dsp_write_body(d, buf);
+}
+
+/*
+ * generic sound ioctl. Functions of the default driver can be
+ * overridden by the device-specific ioctl call.
+ * If a device-specific call returns ENOSYS (Function not implemented),
+ * the default driver is called. Otherwise, the returned value
+ * is passed up.
+ *
+ * The default handler, for many parameters, sets the value in the
+ * descriptor, sets SND_F_INIT, and calls the callback function with
+ * reason INIT. If successful, the callback returns 1 and the caller
+ * can update the parameter.
+ */
+
+static int
+sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p)
+{
+ int ret = ENOSYS, dev, unit ;
+ snddev_info *d;
+ u_long s;
+
+ dev = minor(i_dev);
+ d = get_snddev_info(dev, &unit);
+
+ if (d == NULL) {
+ printf("ioctl: unit %d not configured\n", unit );
+ return (ENXIO) ;
+ }
+ if (d->ioctl)
+ ret = d->ioctl(dev, cmd, arg, mode, p);
+ if (ret != ENOSYS)
+ return ret ;
+
+ /*
+ * pass control to the default ioctl handler. Set ret to 0 now.
+ */
+ ret = 0 ;
+
+ /*
+ * The linux ioctl interface for the sound driver has a thousand
+ * different calls, and it is unpractical to put the names in
+ * the switch(). So we have some tests before for common routines,
+ * such as the ones related to the mixer. But we really ought
+ * to redesign the interface!
+ *
+ * Reading from the mixer just requires to look at the cached
+ * copy in d->mix_levels[dev], so this routine should cover
+ * practically all needs for mixer reading.
+ */
+ if ( (cmd & MIXER_READ(0)) == MIXER_READ(0) && (cmd & 0xff) < 32 ) {
+ int dev = cmd & 0x1f ;
+ if ( d->mix_devs & (1<<dev) ) { /* supported */
+ *(int *)arg = d->mix_levels[dev];
+ return 0 ;
+ } else
+ return EINVAL ;
+ }
+
+ /*
+ * all routines are called with int. blocked. Make sure that
+ * ints are re-enabled when calling slow or blocking functions!
+ */
+ s = spltty();
+ switch(cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE : /* how many bytes can write ? */
+ if (d->flags & SND_F_WR_DMA)
+ dsp_wr_dmaupdate(d);
+ *(int *)arg = d->dbuf_out.fl;
+ break;
+
+ case AIOSSIZE : /* set the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+ if (p->play_size <= 1 && p->rec_size <= 1) { /* means no blocks */
+ d->flags &= ~SND_F_HAS_SIZE ;
+ } else {
+ RANGE (p->play_size, 40, d->bufsize /4);
+ d->play_blocksize = p->play_size & ~3 ;
+ RANGE (p->rec_size, 40, d->bufsize /4);
+ d->rec_blocksize = p->rec_size & ~3 ;
+ d->flags |= SND_F_HAS_SIZE ;
+ }
+ }
+ splx(s);
+ ask_init(d);
+ /* FALLTHROUGH */
+ case AIOGSIZE : /* get the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+ p->play_size = d->play_blocksize ;
+ p->rec_size = d->rec_blocksize ;
+ }
+ break ;
+
+ case AIOSFMT :
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+ /*
+ * at the moment, only support same format on play & rec
+ */
+ d->play_speed = p->play_rate;
+ d->rec_speed = p->play_rate; /* XXX */
+ if (p->play_format & SND_F_STEREO)
+ d->flags |= SND_F_STEREO ;
+ else
+ d->flags &= ~SND_F_STEREO ;
+ d->play_fmt = p->play_format & ~AFMT_STEREO ;
+ d->rec_fmt = p->rec_format & ~AFMT_STEREO ;
+ }
+ splx(s);
+ if (!ask_init(d))
+ break ; /* could not reinit */
+ /* FALLTHROUGH */
+
+ case AIOGFMT :
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+ p->play_rate = d->play_speed;
+ p->rec_rate = d->rec_speed;
+ p->play_format = d->play_fmt;
+ p->rec_format = d->rec_fmt;
+ if (d->flags & SND_F_STEREO) {
+ p->play_format |= AFMT_STEREO ;
+ p->rec_format |= AFMT_STEREO ;
+ }
+ }
+ break;
+
+ case AIOGCAP : /* get capabilities */
+ /* this should really be implemented by the driver */
+ {
+ snd_capabilities *p = (snd_capabilities *)arg;
+ p->rate_min = 5000;
+ p->rate_max = 48000; /* default */
+ p->bufsize = d->bufsize;
+ p->formats = d->audio_fmt; /* default */
+ p->mixers = 1 ; /* default: one mixer */
+ p->inputs = d->mix_devs ;
+ p->left = p->right = 255 ;
+ }
+ break ;
+
+ case AIOSTOP:
+ if (*(int *)arg == AIOSYNC_PLAY) /* play */
+ *(int *)arg = dsp_wrabort(d);
+ else if (*(int *)arg == AIOSYNC_CAPTURE)
+ *(int *)arg = dsp_rdabort(d);
+ else {
+ splx(s);
+ printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg);
+ *(int *)arg = 0 ;
+ }
+ break ;
+
+ case AIOSYNC:
+ printf("AIOSYNC chan 0x%03x pos %d unimplemented\n",
+ ((snd_sync_parm *)arg)->chan,
+ ((snd_sync_parm *)arg)->pos);
+ break;
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD : /* get # bytes to read */
+ if ( d->flags & SND_F_RD_DMA )
+ dsp_rd_dmaupdate(d);
+ *(int *)arg = d->dbuf_in.rl;
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ printf("FIOASYNC\n");
+ break;
+
+ case SNDCTL_DSP_NONBLOCK :
+ case FIONBIO : /* set/clear non-blocking i/o */
+ if ( *(int *)arg == 0 )
+ d->flags &= ~SND_F_NBIO ;
+ else
+ d->flags |= SND_F_NBIO ;
+ break ;
+
+ /*
+ * Finally, here is the linux-compatible ioctl interface
+ */
+ case SNDCTL_DSP_GETBLKSIZE:
+ *(int *) arg = d->play_blocksize ;
+ break ;
+
+ case SNDCTL_DSP_SETBLKSIZE :
+ {
+ int t = *(int *)arg;
+ if (t <= 1) { /* means no blocks */
+ d->flags &= ~SND_F_HAS_SIZE ;
+ } else {
+ RANGE (t, 40, d->bufsize /4);
+ d->play_blocksize =
+ d->rec_blocksize = t & ~3 ; /* align to multiple of 4 */
+ d->flags |= SND_F_HAS_SIZE ;
+ }
+ }
+ splx(s);
+ ask_init(d);
+ break ;
+ case SNDCTL_DSP_RESET:
+ printf("dsp reset\n");
+ dsp_wrabort(d);
+ dsp_rdabort(d);
+ break ;
+
+ case SNDCTL_DSP_SYNC:
+ printf("dsp sync\n");
+ splx(s);
+ snd_sync(d, 1, d->bufsize - 4); /* DMA does not start with <4 bytes */
+ break ;
+
+ case SNDCTL_DSP_SPEED:
+ d->play_speed = d->rec_speed = *(int *)arg ;
+ splx(s);
+ if (ask_init(d))
+ *(int *)arg = d->play_speed ;
+ break ;
+
+ case SNDCTL_DSP_STEREO:
+ if ( *(int *)arg == 0 )
+ d->flags &= ~SND_F_STEREO ; /* mono */
+ else if ( *(int *)arg == 1 )
+ d->flags |= SND_F_STEREO ; /* stereo */
+ else {
+ printf("dsp stereo: %d is invalid, assuming 1\n", *(int *)arg );
+ d->flags |= SND_F_STEREO ; /* stereo */
+ }
+ splx(s);
+ if (ask_init(d))
+ *(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ;
+ break ;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ printf("dsp write channels %d\n", *(int *)arg);
+ if ( *(int *)arg == 1)
+ d->flags &= ~SND_F_STEREO ; /* mono */
+ else if ( *(int *)arg == 2)
+ d->flags |= SND_F_STEREO ; /* stereo */
+ else {
+ ret = EINVAL ;
+ break ;
+ }
+ splx(s);
+ if (ask_init(d))
+ *(int *)arg = (d->flags & SND_F_STEREO) ? 2 : 1 ;
+ break ;
+
+ case SOUND_PCM_READ_RATE:
+ *(int *)arg = d->play_speed;
+ break ;
+
+ case SOUND_PCM_READ_CHANNELS:
+ *(int *)arg = (d->flags & SND_F_STEREO) ? 2 : 1;
+ break ;
+
+ case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
+ *(int *)arg = (int)d->audio_fmt ;
+ break ;
+
+ case SNDCTL_DSP_SETFMT: /* sets _one_ format */
+ d->play_fmt = d->rec_fmt = *(int *)arg ;
+ splx(s);
+ if (ask_init(d))
+ *(int *)arg = d->play_fmt ;
+ break ;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ /* XXX watch out, this is RW! */
+ printf("SNDCTL_DSP_SUBDIVIDE yet unimplemented\n");
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ /* XXX watch out, this is RW! */
+ printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg);
+ {
+ int bytes, count;
+ bytes = *(int *)arg & 0xffff ;
+ count = ( *(int *)arg >> 16) & 0xffff ;
+ if (bytes < 7 || bytes > 15)
+ return EINVAL ;
+ d->play_blocksize =
+ d->rec_blocksize = min ( 1<< bytes, d->dbuf_in.bufsize) ;
+ count = d->dbuf_in.bufsize / d->play_blocksize ;
+ bytes = ffs(d->play_blocksize) - 1;
+#if 0
+ /*
+ * don't change arg, since it's fake anyways and some
+ * programs might fail if we do.
+ */
+ *(int *)arg = (count << 16) | bytes ;
+#endif
+ }
+ break ;
+
+ case SNDCTL_DSP_GETISPACE:
+ /* return space available in the input queue */
+ ((audio_buf_info *)arg)->bytes = d->dbuf_in.fl ;
+ ((audio_buf_info *)arg)->fragments = 1 ;
+ ((audio_buf_info *)arg)->fragstotal =
+ d->bufsize / d->play_blocksize ;
+ ((audio_buf_info *)arg)->fragsize = d->play_blocksize ;
+ break ;
+
+ case SNDCTL_DSP_GETOSPACE:
+ /* return space available in the output queue */
+ ((audio_buf_info *)arg)->bytes = d->dbuf_out.fl ;
+ ((audio_buf_info *)arg)->fragments = 1 ;
+ ((audio_buf_info *)arg)->fragstotal =
+ d->bufsize / d->play_blocksize ;
+ ((audio_buf_info *)arg)->fragsize = d->play_blocksize ;
+ break ;
+
+ case SNDCTL_DSP_GETCAPS :
+ printf("dsp getcaps\n");
+ break ;
+
+ case SOUND_PCM_READ_BITS:
+ if (d->play_fmt == AFMT_S16_LE)
+ *(int *) arg = 16 ;
+ else
+ *(int *) arg = 8 ;
+ break ;
+
+ /*
+ * mixer calls
+ */
+
+ case SOUND_MIXER_READ_DEVMASK :
+ case SOUND_MIXER_READ_STEREODEVS :
+ *(int *)arg = d->mix_devs;
+ break ;
+
+ case SOUND_MIXER_READ_RECMASK :
+ *(int *)arg = d->mix_rec_devs;
+ break ;
+
+ case SOUND_MIXER_READ_RECSRC :
+ *(int *)arg = d->mix_recsrc ;
+ break;
+
+ default:
+ DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n",
+ unit, dev & 0xf, cmd));
+ ret = EINVAL;
+ break ;
+ }
+ splx(s);
+ return ret ;
+}
+
+int
+sndselect(dev_t i_dev, int rw, struct proc * p)
+{
+ int dev, unit, c = 1 /* default: success */ ;
+ snddev_info *d ;
+ u_long flags;
+
+ dev = minor(i_dev);
+ d = get_snddev_info(dev, &unit);
+ DEB(printf("sndselect dev 0x%04x rw 0x%08x\n",i_dev, rw));
+ if (d == NULL ) {
+ printf("select: unit %d not configured\n", unit );
+ return (ENXIO) ;
+ }
+ if (d->select == NULL)
+ return 1 ; /* always success ? */
+ else if (d->select != sndselect )
+ return d->select(i_dev, rw, p);
+ else {
+ /* handle it here with the generic code */
+
+ int lim ;
+
+ /*
+ * if the user selected a block size, then we want to use the
+ * device as a block device, and select will return ready when
+ * we have a full block.
+ * In all other cases, select will return when 1 byte is ready.
+ */
+ lim = 1;
+ switch(rw) {
+ case FWRITE :
+ if ( d->flags & SND_F_HAS_SIZE )
+ lim = d->play_blocksize ;
+ /* XXX fix the test here for half duplex devices */
+ if (1 /* write is compatible with current mode */) {
+ flags = spltty();
+ if (d->flags & SND_F_WR_DMA)
+ dsp_wr_dmaupdate(d);
+ c = d->dbuf_out.fl ;
+ if (c < lim) /* no space available */
+ selrecord(p, & (d->wsel));
+ splx(flags);
+ }
+ return c < lim ? 0 : 1 ;
+
+ case FREAD :
+ if ( d->flags & SND_F_HAS_SIZE )
+ lim = d->rec_blocksize ;
+ /* XXX fix the test here */
+ if (1 /* read is compatible with current mode */) {
+ flags = spltty();
+ if ( !(d->flags & SND_F_RD_DMA) ) /* dma idle, restart it */
+ dsp_rdintr(d);
+ else
+ dsp_rd_dmaupdate(d);
+ c = d->dbuf_in.rl ;
+ if (c < lim) /* no data available */
+ selrecord(p, & (d->rsel));
+ splx(flags);
+ }
+ DEB(printf("sndselect on read: %d >= %d flags 0x%08x\n",
+ c, lim, d->flags));
+ return c < lim ? 0 : 1 ;
+
+ case 0 :
+ DDB(printf("select on exceptions, unimplemented\n"));
+ return 1;
+ }
+ }
+ return ENXIO ; /* notreached */
+}
+
+/*
+ * The mmap interface allows access to the play and read buffer,
+ * plus the device descriptor.
+ * The various blocks are accessible at the following offsets:
+ *
+ * 0x00000000 ( 0 ) : write buffer ;
+ * 0x01000000 (16 MB) : read buffer ;
+ * 0x02000000 (32 MB) : device descriptor (dangerous!)
+ *
+ * WARNING: the mmap routines assume memory areas are aligned. This
+ * is true (probably) for the dma buffers, but likely false for the
+ * device descriptor. As a consequence, we do not know where it is
+ * located in the requested area.
+ */
+#include <sys/mman.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+static int
+sndmmap(dev_t dev, int offset, int nprot)
+{
+ snddev_info *d = get_snddev_info(dev, NULL);
+
+ DEB(printf("sndmmap d 0x%08x dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
+ d, dev, offset, nprot));
+
+ if (d == NULL || nprot & PROT_EXEC)
+ return -1 ; /* forbidden */
+
+ if (offset > d->bufsize && (nprot & PROT_WRITE) )
+ return -1 ; /* can only write to the first block */
+
+ if (offset < d->bufsize)
+ return i386_btop(vtophys(d->dbuf_out.buf + offset));
+ offset -= 1 << 24;
+ if ( (offset >= 0) && (offset < d->bufsize))
+ return i386_btop(vtophys(d->dbuf_in.buf + offset));
+ offset -= 1 << 24;
+ if ( (offset >= 0) && (offset < 0x2000)) {
+ printf("name %s\n", d->name);
+ return i386_btop(vtophys( ((int)d & ~0xfff) + offset));
+ }
+ return -1 ;
+}
+
+
+/*
+ * ask_init sets the init flag in the device descriptor, and
+ * possibly calls the appropriate callback routine, returning 1
+ * if the callback was successful. This enables ioctls handler for
+ * rw parameters to read back the updated value.
+ * Since the init callback can be slow, ask_init() should be called
+ * with interrupts enabled.
+ */
+
+int
+ask_init(snddev_info *d)
+{
+ u_long s;
+
+ if ( d->callback == NULL )
+ return 0 ;
+ s = spltty();
+ if ( d->flags & SND_F_PENDING_IO ) {
+ /* cannot do it now, record the request and return */
+ d->flags |= SND_F_INIT ;
+ splx(s);
+ return 0 ;
+ } else {
+ splx(s);
+ d->callback(d, SND_CB_INIT );
+ return 1;
+ }
+}
+
+/*
+ * these are the functions for the soundstat device. We copy parameters
+ * from the device info structure to static variables, and from there
+ * back to the structure when done.
+ */
+
+static void
+init_status(snddev_info *d)
+{
+ /*
+ * Write the status information to the status_buf and update
+ * status_len. There is a limit of SNDSTAT_BUF_SIZE bytes for the data.
+ * put_status handles this and returns 0 in case of failure. Since
+ * it never oveflows the buffer, we do not care to check.
+ */
+
+ int i;
+
+ if (status_len != 0) /* only do init once */
+ return ;
+ sprintf(status_buf,
+ "FreeBSD Sound Driver " __DATE__ " " __TIME__ "\n"
+ "Installed devices:\n");
+
+ for (i = 0; i < NPCM_MAX; i++) {
+ if (pcm_info[i].open)
+ sprintf(status_buf + strlen(status_buf),
+ "pcm%d: <%s> at 0x%x irq %d dma %d:%d\n",
+ i, pcm_info[i].name, pcm_info[i].io_base,
+ pcm_info[i].irq,
+ pcm_info[i].dma1, pcm_info[i].dma2);
+ if (midi_info[i].open)
+ sprintf(status_buf + strlen(status_buf),
+ "midi%d: <%s> at 0x%x irq %d dma %d:%d\n",
+ i, midi_info[i].name, midi_info[i].io_base,
+ midi_info[i].irq,
+ midi_info[i].dma1, midi_info[i].dma2);
+ if (synth_info[i].open)
+ sprintf(status_buf + strlen(status_buf),
+ "synth%d: <%s> at 0x%x irq %d dma %d:%d\n",
+ i, synth_info[i].name, synth_info[i].io_base,
+ synth_info[i].irq,
+ synth_info[i].dma1, synth_info[i].dma2);
+ }
+ status_len = strlen(status_buf) ;
+}
+
+/*
+ * finally, some "libraries"
+ */
+
+/*
+ * isa_dmastatus1() is a wrapper for isa_dmastatus(), which
+ * might return -1 or -2 in some cases (errors). Since for the
+ * user code it is more comfortable not to check for these cases,
+ * negative values are mapped back to 0 (which is reasonable).
+ */
+
+int
+isa_dmastatus1(int channel)
+{
+ int r = isa_dmastatus(channel);
+ if (r<0) r = 0;
+ return r;
+}
+
+/*
+ * snd_conflict scans already-attached boards to see if
+ * the current address is conflicting with one of the already
+ * assigned ones. Returns 1 if a conflict is detected.
+ */
+int
+snd_conflict(int io_base)
+{
+ int i;
+ for (i=0; i< NPCM_MAX ; i++) {
+ if ( (io_base == pcm_info[i].io_base ) ||
+ (io_base == pcm_info[i].alt_base ) ||
+ (io_base == pcm_info[i].conf_base) ||
+ (io_base == pcm_info[i].mix_base ) ||
+ (io_base == pcm_info[i].midi_base) ||
+ (io_base == pcm_info[i].synth_base) ) {
+ printf("device at 0x%x already attached as unit %d\n",
+ io_base, i);
+ return 1 ;
+ }
+ }
+ return 0;
+}
+
+void
+snd_set_blocksize(snddev_info *d)
+{
+ /*
+ * now set the blocksize so as to guarantee approx 1/4s
+ * between callbacks.
+ */
+ if ( (d->flags & SND_F_HAS_SIZE) == 0) {
+ /* make blocksize adaptive to 250ms or max 1/4 of the buf */
+ int tmp ;
+ tmp = d->play_speed;
+ if (d->flags & SND_F_STEREO) tmp += tmp;
+ if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
+ d->play_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */
+ RANGE (d->play_blocksize, 1024, (d->bufsize / 4) & ~3);
+
+ tmp = d->rec_speed;
+ if (d->flags & SND_F_STEREO) tmp += tmp;
+ if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;
+ d->rec_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */
+ RANGE (d->rec_blocksize, 1024, (d->bufsize / 4) & ~3);
+ }
+}
+
+/*
+ * The various mixers use a variety of bitmasks etc. The Voxware
+ * driver had a very nice technique to describe a mixer and interface
+ * to it. A table defines, for each channel, which register, bits,
+ * offset, polarity to use. This procedure creates the new value
+ * using the table and the old value.
+ */
+
+void
+change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
+{
+ u_char mask;
+ int shift;
+
+ DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
+ "r %d p %d bit %d off %d\n",
+ dev, chn, newval, *regval,
+ (*t)[dev][chn].regno, (*t)[dev][chn].polarity,
+ (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
+
+ if ( (*t)[dev][chn].polarity == 1) /* reverse */
+ newval = 100 - newval ;
+
+ mask = (1 << (*t)[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
+ shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
+
+ *regval &= ~(mask << shift); /* Filter out the previous value */
+ *regval |= (newval & mask) << shift; /* Set the new value */
+}
+
+
+/*
+ * code for translating between U8 and ULAW. Needed to support
+ * /dev/audio on the SoundBlaster. Actually, we would also need
+ * ulaw -> 16 bits (for the soundblaster as well, when used in
+ * full-duplex)
+ */
+
+#if 1
+void
+translate_bytes (u_char *table, u_char *buff, int n)
+{
+ u_long i;
+
+ if (n <= 0)
+ return;
+
+ for (i = 0; i < n; ++i)
+ buff[i] = table[buff[i]];
+}
+#else
+/* inline */
+void
+translate_bytes (const void *table, void *buff, int n)
+{
+ if (n > 0) {
+ __asm__ ( " cld\n"
+ "1: lodsb\n"
+ " xlatb\n"
+ " stosb\n"
+ " loop 1b\n":
+ : "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
+ : "bx", "cx", "di", "si", "ax");
+ }
+}
+
+#endif
+
+#endif /* NPCM > 0 */
diff --git a/sys/i386/isa/snd/sound.h b/sys/i386/isa/snd/sound.h
new file mode 100644
index 0000000..00e15ed
--- /dev/null
+++ b/sys/i386/isa/snd/sound.h
@@ -0,0 +1,492 @@
+/*
+ * sound.h
+ *
+ * include file for kernel sources, sound driver.
+ *
+ * Copyright by Hannu Savolainen 1995
+ *
+ * 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.
+ *
+ */
+
+#include "pcm.h"
+#if NPCM > 0
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _OS_H_
+#define _OS_H_
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <i386/isa/isa_device.h>
+
+#include <machine/clock.h> /* for DELAY */
+
+typedef void (irq_proc_t) (int irq);
+
+#endif /* _OS_H_ */
+
+/*
+ * descriptor of a dma buffer. See dmabuf.c for documentation.
+ * (rp,rl) and (fp,fl) identify the READY and FREE regions of the
+ * buffer. (dp,dl) identify the region currently used by the DMA.
+ * Only dmabuf.c should test the value of dl. The reason is that
+ * in auto-dma mode looking at dl alone does not make much sense,
+ * since the transfer potentially spans the entire buffer.
+ */
+
+typedef struct _snd_dbuf {
+ char *buf;
+ int bufsize ;
+ volatile int dp, rp, fp;
+ volatile int dl, rl, fl;
+ volatile int dl0; /* value used last time in dl */
+ int int_count;
+} snd_dbuf ;
+
+/*
+ * descriptor of audio operations ...
+ *
+ */
+typedef struct _snddev_info snddev_info ;
+typedef int (snd_callback_t)(snddev_info *d, int reason);
+
+struct _snddev_info {
+
+ /*
+ * the first part of the descriptor is filled up from a
+ * template.
+ */
+ char name[64];
+
+ int type ;
+
+ int (*probe)(struct isa_device * dev);
+ int (*attach)(struct isa_device * dev) ;
+ d_open_t *open ;
+ d_close_t *close ;
+ d_read_t *read ;
+ d_write_t *write ;
+ d_ioctl_t *ioctl ;
+ d_select_t *select ;
+ irq_proc_t *isr ;
+ snd_callback_t *callback;
+
+ int bufsize; /* space used for buffers */
+
+ u_long audio_fmt ; /* supported audio formats */
+
+
+ /*
+ * combinations of the following flags are used as second argument in
+ * the callback from the dma module to the device-specific routines.
+ */
+
+#define SND_CB_RD 0x100 /* read callback */
+#define SND_CB_WR 0x200 /* write callback */
+#define SND_CB_REASON_MASK 0xff
+#define SND_CB_START 0x01 /* start dma op */
+#define SND_CB_RESTART 0x02 /* restart dma op */
+#define SND_CB_STOP 0x03 /* stop dma op */
+#define SND_CB_ABORT 0x04 /* abort dma op */
+#define SND_CB_INIT 0x05 /* init board parameters */
+ /* init can only be called with int enabled and
+ * no pending DMA activity.
+ */
+
+ /*
+ * whereas from here, parameters are set at runtime.
+ */
+
+ int io_base ; /* primary I/O address for the board */
+ int alt_base ; /* some codecs are accessible as SB+WSS... */
+ int conf_base ; /* and the opti931 also has a config space */
+ int mix_base ; /* base for the mixer... */
+ int midi_base ; /* base for the midi */
+ int synth_base ; /* base for the synth */
+
+ int irq ;
+ int dma1, dma2 ; /* dma2=dma1 for half-duplex cards */
+
+ int bd_id ; /* used to hold board-id info, eg. sb version,
+ * mss codec type, etc. etc.
+ */
+
+ snd_dbuf dbuf_out, dbuf_in;
+
+ int status_ptr; /* used to implement sndstat */
+
+ /*
+ * these parameters describe the operation of the board.
+ * Generic things like busy flag, speed, etc are here.
+ */
+
+ volatile u_long flags ; /* 32 bits, used for various purposes. */
+
+ /*
+ * we have separate flags for read and write, although in some
+ * cases this is probably not necessary (e.g. because we cannot
+ * know how many processes are using the device, we cannot
+ * distinguish if open, close, abort are for a write or for a
+ * read).
+ */
+
+ /*
+ * the following flag is used by open-close routines
+ * to mark the status of the device.
+ */
+#define SND_F_BUSY 0x0001 /* has been opened */
+ /*
+ * Only the last close for a device will propagate to the driver.
+ * Unfortunately, voxware uses 3 different minor numbers
+ * (dsp, dsp16 and audio) to access the same unit. So, if
+ * we want to support multiple opens and still keep track of
+ * what is happening, we also need a separate flag for each minor
+ * number. These are below...
+ */
+#define SND_F_BUSY_AUDIO 0x10000000
+#define SND_F_BUSY_DSP 0x20000000
+#define SND_F_BUSY_DSP16 0x40000000
+#define SND_F_BUSY_ANY 0x70000000
+ /*
+ * the next two are used to allow only one pending operation of
+ * each type.
+ */
+#define SND_F_READING 0x0004 /* have a pending read */
+#define SND_F_WRITING 0x0008 /* have a pending write */
+ /*
+ * these mark pending DMA operations. When you have pending dma ops,
+ * you might get interrupts, so some manipulations of the
+ * descriptors must be done with interrupts blocked.
+ */
+#define SND_F_RD_DMA 0x0010 /* read-dma active */
+#define SND_F_WR_DMA 0x0020 /* write-dma active */
+
+#define SND_F_PENDING_IN (SND_F_READING | SND_F_RD_DMA)
+#define SND_F_PENDING_OUT (SND_F_WRITING | SND_F_WR_DMA)
+#define SND_F_PENDING_IO (SND_F_PENDING_IN | SND_F_PENDING_OUT)
+
+ /*
+ * flag used to mark a pending close.
+ */
+#define SND_F_CLOSING 0x0040 /* a pending close */
+
+ /*
+ * if user has not set block size, then make it adaptive
+ * (0.25s, or the perhaps last read/write ?)
+ */
+#define SND_F_HAS_SIZE 0x0080 /* user set block size */
+ /*
+ * assorted flags related to operating mode.
+ */
+#define SND_F_STEREO 0x0100 /* doing stereo */
+#define SND_F_NBIO 0x0200 /* do non-blocking i/o */
+
+ /*
+ * the user requested ulaw, but the board does not support it
+ * natively, so a (software) format conversion is necessary.
+ * The kernel is not really the place to do this, but since
+ * many applications expect to use /dev/audio , we do it for
+ * portability.
+ */
+#define SND_F_XLAT8 0x0400 /* u-law <--> 8-bit unsigned */
+#define SND_F_XLAT16 0x0800 /* u-law <--> 16-bit signed */
+
+ /*
+ * these flags mark a pending abort on a r/w operation.
+ */
+#define SND_F_ABORTING 0x1000 /* a pending abort */
+
+ /*
+ * this is used to mark that board initialization is needed, e.g.
+ * because of a change in sampling rate, format, etc. -- It will
+ * be done at the next convenient time.
+ */
+#define SND_F_INIT 0x4000 /* changed parameters. need init */
+#define SND_F_AUTO_DMA 0x8000 /* use auto-dma */
+
+ u_long bd_flags; /* board-specific flags */
+ int play_speed, rec_speed;
+
+ int play_blocksize, rec_blocksize; /* blocksize for io and dma ops */
+ u_long play_fmt, rec_fmt ; /* current audio format */
+
+ /*
+ * mixer parameters
+ */
+ u_long mix_devs; /* existing devices for mixer */
+ u_long mix_rec_devs; /* possible recording sources */
+ u_long mix_recsrc; /* current recording source(s) */
+ u_short mix_levels[32];
+
+ struct selinfo wsel, rsel, esel ;
+ u_long interrupts; /* counter of interrupts */
+ void *device_data ; /* just in case it is needed...*/
+} ;
+
+/*
+ * then ioctls and other stuff
+ */
+
+#define NPCM_MAX 8 /* Number of supported devices */
+
+/*
+ * Supported card ID numbers (were in soundcard.h...)
+ */
+
+#define SNDCARD_ADLIB 1
+#define SNDCARD_SB 2
+#define SNDCARD_PAS 3
+#define SNDCARD_GUS 4
+#define SNDCARD_MPU401 5
+#define SNDCARD_SB16 6
+#define SNDCARD_SB16MIDI 7
+#define SNDCARD_UART6850 8
+#define SNDCARD_GUS16 9
+#define SNDCARD_MSS 10
+#define SNDCARD_PSS 11
+#define SNDCARD_SSCAPE 12
+#define SNDCARD_PSS_MPU 13
+#define SNDCARD_PSS_MSS 14
+#define SNDCARD_SSCAPE_MSS 15
+#define SNDCARD_TRXPRO 16
+#define SNDCARD_TRXPRO_SB 17
+#define SNDCARD_TRXPRO_MPU 18
+#define SNDCARD_MAD16 19
+#define SNDCARD_MAD16_MPU 20
+#define SNDCARD_CS4232 21
+#define SNDCARD_CS4232_MPU 22
+#define SNDCARD_MAUI 23
+#define SNDCARD_PSEUDO_MSS 24 /* MSS without WSS regs.*/
+#define SNDCARD_AWE32 25
+
+/*
+ * values used in bd_id for the mss boards
+ */
+#define MD_AD1848 0x91
+#define MD_AD1845 0x92
+#define MD_CS4248 0xA1
+#define MD_CS4231 0xA2
+#define MD_CS4231A 0xA3
+#define MD_CS4232 0xA4
+#define MD_CS4232A 0xA5
+#define MD_CS4236 0xA6
+#define MD_OPTI931 0xB1
+
+/*
+ * TODO: add some card classes rather than specific types.
+ */
+#include <i386/isa/snd/soundcard.h>
+
+/*
+ * many variables should be reduced to a range. Here define a macro
+ */
+
+#define RANGE(var, low, high) (var) = \
+ ((var)<(low)?(low) : (var)>(high)?(high) : (var))
+
+/*
+ * finally, all default parameters
+ */
+#define DSP_BUFFSIZE 65536 /* XXX */
+
+#if 1 /* prepare for pnp support! */
+#include "pnp.h"
+#if NPNP > 0
+#include <i386/isa/pnp.h> /* XXX pnp support */
+#endif
+#endif
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+ /* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+
+#define SYNTH_MAX_VOICES 32
+
+struct voice_alloc_info {
+ int max_voice;
+ int used_voices;
+ int ptr; /* For device specific use */
+ u_short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+ int timestamp;
+ int alloc_times[SYNTH_MAX_VOICES];
+};
+
+struct channel_info {
+ int pgm_num;
+ int bender_value;
+ u_char controllers[128];
+};
+
+/*
+ * mixer description structure and macros
+ */
+
+struct mixer_def {
+ u_int regno:7;
+ u_int polarity:1; /* 1 means reversed */
+ u_int bitoffs:4;
+ u_int nbits:4;
+};
+typedef struct mixer_def mixer_ent;
+typedef struct mixer_def mixer_tab[32][2];
+
+#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
+ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
+#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
+ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
+
+#define DDB(x) x /* XXX */
+
+#ifndef DEB
+#define DEB(x)
+#endif
+#ifndef DDB
+#define DDB(x)
+#endif
+
+extern snddev_info pcm_info[NPCM_MAX] ;
+extern snddev_info midi_info[NPCM_MAX] ;
+extern snddev_info synth_info[NPCM_MAX] ;
+
+extern u_long nsnd ;
+extern snddev_info *snddev_last_probed;
+
+int pcmprobe(struct isa_device * dev);
+int midiprobe(struct isa_device * dev);
+int synthprobe(struct isa_device * dev);
+int pcmattach(struct isa_device * dev);
+int midiattach(struct isa_device * dev);
+int synthattach(struct isa_device * dev);
+
+/*
+ * functions in isa.c
+ */
+
+int isa_dmastatus(int chan);
+int isa_dmastop(int chan);
+/*
+ * DMA buffer calls
+ */
+
+void dsp_wrintr(snddev_info *d);
+void dsp_rdintr(snddev_info *d);
+int dsp_write_body(snddev_info *d, struct uio *buf);
+int dsp_read_body(snddev_info *d, struct uio *buf);
+void alloc_dbuf(snd_dbuf *d, int size, int b16);
+void reset_dbuf(snd_dbuf *b);
+int snd_flush(snddev_info *d);
+int snd_sync(snddev_info *d, int chan, int threshold);
+int dsp_wrabort(snddev_info *d);
+int dsp_rdabort(snddev_info *d);
+void dsp_wr_dmaupdate(snddev_info *d);
+void dsp_rd_dmaupdate(snddev_info *d);
+
+d_select_t sndselect;
+
+/*
+ * library functions (in sound.c)
+ */
+
+int ask_init(snddev_info *d);
+void translate_bytes(u_char *table, u_char *buff, int n);
+void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
+int snd_conflict(int io_base);
+void snd_set_blocksize(snddev_info *d);
+int isa_dmastatus1(int channel);
+/*
+ * routines in ad1848.c and sb_dsp.c which others might use
+ */
+int mss_detect (struct isa_device *dev);
+int sb_cmd (int io_base, u_char cmd);
+int sb_cmd2 (int io_base, u_char cmd, int val);
+int sb_cmd3 (int io_base, u_char cmd, int val);
+int sb_reset_dsp (int io_base);
+void sb_setmixer (int io_base, u_int port, u_int value);
+int sb_getmixer (int io_base, u_int port);
+
+
+/*
+ * usage of flags in device config entry (config file)
+ */
+
+#define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */
+#define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */
+#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
+#define DV_F_DEV_SHIFT 8 /* force device type/class */
+
+/*
+ * some flags are used in a device-specific manner, so that values can
+ * be used multiple times.
+ */
+
+#define DV_F_TRUE_MSS 0x00010000 /* mss _with_ base regs */
+ /* almost all modern cards do not have this set of registers,
+ * so it is better to make this the default behaviour
+ */
+
+#endif
diff --git a/sys/i386/isa/snd/soundcard.h b/sys/i386/isa/snd/soundcard.h
new file mode 100644
index 0000000..276b936
--- /dev/null
+++ b/sys/i386/isa/snd/soundcard.h
@@ -0,0 +1,1297 @@
+/*
+ * soundcard.h
+ *
+ * Copyright by Hannu Savolainen 1993
+ * Modified for the new FreeBSD sound driver by Luigi Rizzo, 1997
+ *
+ * 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.
+ */
+
+#ifndef SOUNDCARD_H
+#define SOUNDCARD_H
+ /*
+ * If you make modifications to this file, please contact me before
+ * distributing the modified version. There is already enough
+ * diversity in the world.
+ *
+ * Regards,
+ * Hannu Savolainen
+ * hannu@voxware.pp.fi
+ *
+ **********************************************************************
+ * PS. The Hacker's Guide to VoxWare available from
+ * nic.funet.fi:pub/OS/Linux/ALPHA/sound. The file is
+ * snd-sdk-doc-0.1.ps.gz (gzipped postscript). It contains
+ * some useful information about programming with VoxWare.
+ * (NOTE! The pub/OS/Linux/ALPHA/ directories are hidden. You have
+ * to cd inside them before the files are accessible.)
+ **********************************************************************
+ */
+
+#include <sys/types.h>
+#ifndef _IOWR
+#include <sys/ioccom.h>
+#endif /* !_IOWR */
+
+/*
+ * The first part of this file contains the new FreeBSD sound ioctl
+ * interface. Tries to minimize the number of different ioctls, and
+ * to be reasonably general.
+ *
+ * 970821: some of the new calls have not been implemented yet.
+ */
+
+/*
+ * the following three calls extend the generic file descriptor
+ * interface. AIONWRITE is the dual of FIONREAD, i.e. returns the max
+ * number of bytes for a write operation to be non-blocking.
+ *
+ * AIOGSIZE/AIOSSIZE are used to change the behaviour of the device,
+ * from a character device (default) to a block device. In block mode,
+ * (not to be confused with blocking mode) the main difference for the
+ * application is that select() will return only when a complete
+ * block can be read/written to the device, whereas in character mode
+ * select will return true when one byte can be exchanged. For audio
+ * devices, character mode makes select almost useless since one byte
+ * will always be ready by the next sample time (which is often only a
+ * handful of microseconds away).
+ * Use a size of 0 or 1 to return to character mode.
+ */
+#define AIONWRITE _IOR('A', 10, int) /* get # bytes to write */
+struct snd_size {
+ int play_size;
+ int rec_size;
+};
+#define AIOGSIZE _IOR('A', 11, struct snd_size)/* read current blocksize */
+#define AIOSSIZE _IOWR('A', 11, struct snd_size) /* sets blocksize */
+
+/*
+ * The following constants define supported audio formats. The
+ * encoding follows voxware conventions, i.e. 1 bit for each supported
+ * format. We extend it by using bit 31 (RO) to indicate full-duplex
+ * capability, and bit 29 (RO) to indicate that the card supports/
+ * needs different formats on capture & playback channels.
+ * Bit 29 (RW) is used to indicate/ask stereo.
+ */
+
+# define AFMT_QUERY 0x00000000 /* Return current fmt */
+# define AFMT_MU_LAW 0x00000001
+# define AFMT_A_LAW 0x00000002
+# define AFMT_IMA_ADPCM 0x00000004
+# define AFMT_U8 0x00000008
+# define AFMT_S16_LE 0x00000010 /* Little endian signed 16*/
+# define AFMT_S16_BE 0x00000020 /* Big endian signed 16 */
+# define AFMT_S8 0x00000040
+# define AFMT_U16_LE 0x00000080 /* Little endian U16 */
+# define AFMT_U16_BE 0x00000100 /* Big endian U16 */
+# define AFMT_MPEG 0x00000200 /* MPEG (2) audio */
+
+# define AFMT_STEREO 0x10000000 /* can do/want stereo */
+
+/*
+ * the following are really capabilities
+ */
+# define AFMT_WEIRD 0x20000000 /* weird hardware... */
+ /*
+ * AFMT_WEIRD reports that the hardware might need to operate
+ * with different formats in the playback and capture
+ * channels when operating in full duplex.
+ * As an example, SoundBlaster16 cards only support U8 in one
+ * direction and S16 in the other one, and applications should
+ * be aware of this limitation.
+ */
+# define AFMT_FULLDUPLEX 0x80000000 /* can do full duplex */
+
+/*
+ * The following structure is used to get/set format and sampling rate.
+ * While it would be better to have things such as stereo, bits per
+ * sample, endiannes, etc split in different variables, it turns out
+ * that formats are not that many, and not all combinations are possible.
+ * So we followed the Voxware approach of associating one bit to each
+ * format.
+ */
+
+typedef struct _snd_chan_param {
+ u_long play_rate; /* sampling rate */
+ u_long rec_rate; /* sampling rate */
+ u_long play_format; /* everything describing the format */
+ u_long rec_format; /* everything describing the format */
+} snd_chan_param;
+#define AIOGFMT _IOR('f', 12, snd_chan_param) /* get format */
+#define AIOSFMT _IOWR('f', 12, snd_chan_param) /* sets format */
+
+/*
+ * The following structure is used to get/set the mixer setting.
+ * Up to 32 mixers are supported, each one with up to 32 channels.
+ */
+typedef struct _snd_mix_param {
+ u_char subdev; /* which output */
+ u_char line; /* which input */
+ u_char left,right; /* volumes, 0..255, 0 = mute */
+} snd_mix_param ;
+
+/* XXX AIOGMIX, AIOSMIX not implemented yet */
+#define AIOGMIX _IOWR('A', 13, snd_mix_param) /* return mixer status */
+#define AIOSMIX _IOWR('A', 14, snd_mix_param) /* sets mixer status */
+
+/*
+ * channel specifiers used in AIOSTOP and AIOSYNC
+ */
+#define AIOSYNC_PLAY 0x1 /* play chan */
+#define AIOSYNC_CAPTURE 0x2 /* capture chan */
+/* AIOSTOP stop & flush a channel, returns the residual count */
+#define AIOSTOP _IOWR ('A', 15, int)
+
+/* alternate method used to notify the sync condition */
+#define AIOSYNC_SIGNAL 0x100
+#define AIOSYNC_SELECT 0x200
+
+/* what the 'pos' field refers to */
+#define AIOSYNC_READY 0x400
+#define AIOSYNC_FREE 0x800
+
+typedef struct _snd_sync_parm {
+ long chan ; /* play or capture channel, plus modifier */
+ long pos;
+} snd_sync_parm;
+#define AIOSYNC _IOWR ('A', 15, snd_sync_parm) /* misc. synchronization */
+
+/*
+ * The following is used to return device capabilities. If the structure
+ * passed to the ioctl is zeroed, default values are returned for rate
+ * and formats, a bitmap of available mixers is returned, and values
+ * (inputs, different levels) for the first one are returned.
+ *
+ * If formats, mixers, inputs are instantiated, then detailed info
+ * are returned depending on the call.
+ */
+typedef struct _snd_capabilities {
+ u_long rate_min, rate_max; /* min-max sampling rate */
+ u_long formats;
+ u_long bufsize; /* DMA buffer size */
+ u_long mixers; /* bitmap of available mixers */
+ u_long inputs; /* bitmap of available inputs (per mixer) */
+ u_short left, right; /* how many levels are supported */
+} snd_capabilities;
+#define AIOGCAP _IOWR('A', 15, snd_capabilities) /* get capabilities */
+
+/*
+ * here is the old (Voxware) ioctl interface
+ */
+
+/*
+ * IOCTL Commands for /dev/sequencer
+ */
+
+#define SNDCTL_SEQ_RESET _IO ('Q', 0)
+#define SNDCTL_SEQ_SYNC _IO ('Q', 1)
+#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer res.(hz) */
+#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */
+#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info)
+#define SNDCTL_SEQ_THRESHOLD _IOW ('Q',13, int)
+#define SNDCTL_SEQ_TRESHOLD SNDCTL_SEQ_THRESHOLD /* there was once a typo */
+#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */
+#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info)
+#define SNDCTL_SEQ_PANIC _IO ('Q',17)
+#define SNDCTL_SEQ_OUTOFBAND _IOW ('Q',18, struct seq_event_rec)
+
+struct seq_event_rec {
+ u_char arr[8];
+};
+
+#define SNDCTL_TMR_TIMEBASE _IOWR('T', 1, int)
+#define SNDCTL_TMR_START _IO ('T', 2)
+#define SNDCTL_TMR_STOP _IO ('T', 3)
+#define SNDCTL_TMR_CONTINUE _IO ('T', 4)
+#define SNDCTL_TMR_TEMPO _IOWR('T', 5, int)
+#define SNDCTL_TMR_SOURCE _IOWR('T', 6, int)
+# define TMR_INTERNAL 0x00000001
+# define TMR_EXTERNAL 0x00000002
+# define TMR_MODE_MIDI 0x00000010
+# define TMR_MODE_FSK 0x00000020
+# define TMR_MODE_CLS 0x00000040
+# define TMR_MODE_SMPTE 0x00000080
+#define SNDCTL_TMR_METRONOME _IOW ('T', 7, int)
+#define SNDCTL_TMR_SELECT _IOW ('T', 8, int)
+
+/*
+ * Endian aware patch key generation algorithm.
+ */
+
+#if defined(_AIX) || defined(AIX)
+# define _PATCHKEY(id) (0xfd00|id)
+#else
+# define _PATCHKEY(id) ((id<<8)|0xfd)
+#endif
+
+/*
+ * Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ * The following patch_info structure has been designed to support
+ * Gravis UltraSound. It tries to be universal format for uploading
+ * sample based patches but is propably too limited.
+ */
+
+struct patch_info {
+/* u_short key; Use GUS_PATCH here */
+ short key; /* Use GUS_PATCH here */
+#define GUS_PATCH _PATCHKEY(0x04)
+#define OBSOLETE_GUS_PATCH _PATCHKEY(0x02)
+
+ short device_no; /* Synthesizer number */
+ short instr_no; /* Midi pgm# */
+
+ u_long mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/
+#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */
+ /* (use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */
+#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */
+#define WAVE_SCALE 0x00040000 /* The scaling info is valid */
+/* Other bits must be zeroed */
+
+ long len; /* Size of the wave data in bytes */
+ long loop_start, loop_end; /* Byte offsets from the beginning */
+
+/*
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for a instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+ u_int base_freq;
+ u_long base_note;
+ u_long high_note;
+ u_long low_note;
+ int panning; /* -128=left, 127=right */
+ int detuning;
+
+/* New fields introduced in version 1.99.5 */
+
+ /* Envelope. Enabled by mode bit WAVE_ENVELOPES */
+ u_char env_rate[ 6 ]; /* GUS HW ramping rate */
+ u_char env_offset[ 6 ]; /* 255 == 100% */
+
+ /*
+ * The tremolo, vibrato and scale info are not supported yet.
+ * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+ * WAVE_SCALE
+ */
+
+ u_char tremolo_sweep;
+ u_char tremolo_rate;
+ u_char tremolo_depth;
+
+ u_char vibrato_sweep;
+ u_char vibrato_rate;
+ u_char vibrato_depth;
+
+ int scale_frequency;
+ u_int scale_factor; /* from 0 to 2048 or 0 to 2 */
+
+ int volume;
+ int spare[4];
+ char data[1]; /* The waveform data starts here */
+};
+
+struct sysex_info {
+ short key; /* Use GUS_PATCH here */
+#define SYSEX_PATCH _PATCHKEY(0x05)
+#define MAUI_PATCH _PATCHKEY(0x06)
+ short device_no; /* Synthesizer number */
+ long len; /* Size of the sysex data in bytes */
+ u_char data[1]; /* Sysex data starts here */
+};
+
+/*
+ * Patch management interface (/dev/sequencer, /dev/patmgr#)
+ * Don't use these calls if you want to maintain compatibility with
+ * the future versions of the driver.
+ */
+
+#define PS_NO_PATCHES 0 /* No patch support on device */
+#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */
+#define PS_MGR_OK 2 /* Patch manager supported */
+#define PS_MANAGED 3 /* Patch manager running */
+
+#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info)
+
+/*
+ * The patmgr_info is a fixed size structure which is used for two
+ * different purposes. The intended use is for communication between
+ * the application using /dev/sequencer and the patch manager daemon
+ * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)).
+ *
+ * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows
+ * a patch manager daemon to read and write device parameters. This
+ * ioctl available through /dev/sequencer also. Avoid using it since it's
+ * extremely hardware dependent. In addition access trough /dev/sequencer
+ * may confuse the patch manager daemon.
+ */
+
+struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */
+ u_long key; /* Don't worry. Reserved for communication
+ between the patch manager and the driver. */
+#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */
+#define PM_K_COMMAND 2 /* Request from a application */
+#define PM_K_RESPONSE 3 /* From patmgr to application */
+#define PM_ERROR 4 /* Error returned by the patmgr */
+ int device;
+ int command;
+
+/*
+ * Commands 0x000 to 0xfff reserved for patch manager programs
+ */
+#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */
+#define PMTYPE_FM2 1 /* 2 OP fm */
+#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */
+#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */
+#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */
+#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */
+#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */
+#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */
+#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */
+#define PM_READ_PATCH 7 /* Read patch (wave) data */
+#define PM_WRITE_PATCH 8 /* Write patch (wave) data */
+
+/*
+ * Commands 0x1000 to 0xffff are for communication between the patch manager
+ * and the client
+ */
+#define _PM_LOAD_PATCH 0x100
+
+/*
+ * Commands above 0xffff reserved for device specific use
+ */
+
+ long parm1;
+ long parm2;
+ long parm3;
+
+ union {
+ u_char data8[4000];
+ u_short data16[2000];
+ u_long data32[1000];
+ struct patch_info patch;
+ } data;
+};
+
+/*
+ * When a patch manager daemon is present, it will be informed by the
+ * driver when something important happens. For example when the
+ * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is
+ * returned. The command field contains the event type:
+ */
+#define PM_E_OPENED 1 /* /dev/sequencer opened */
+#define PM_E_CLOSED 2 /* /dev/sequencer closed */
+#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */
+#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size.
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 128,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ */
+#define SEQ_NOTEOFF 0
+#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */
+#define SEQ_NOTEON 1
+#define SEQ_FMNOTEON SEQ_NOTEON
+#define SEQ_WAIT TMR_WAIT_ABS
+#define SEQ_PGMCHANGE 3
+#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER TMR_START
+#define SEQ_MIDIPUTC 5
+#define SEQ_DRUMON 6 /*** OBSOLETE ***/
+#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/
+#define SEQ_ECHO TMR_ECHO /* For synching programs with output */
+#define SEQ_AFTERTOUCH 9
+#define SEQ_CONTROLLER 10
+
+/*
+ * Midi controller numbers
+ *
+ * Controllers 0 to 31 (0x00 to 0x1f) and 32 to 63 (0x20 to 0x3f)
+ * are continuous controllers.
+ * In the MIDI 1.0 these controllers are sent using two messages.
+ * Controller numbers 0 to 31 are used to send the MSB and the
+ * controller numbers 32 to 63 are for the LSB. Note that just 7 bits
+ * are used in MIDI bytes.
+ */
+
+#define CTL_BANK_SELECT 0x00
+#define CTL_MODWHEEL 0x01
+#define CTL_BREATH 0x02
+/* undefined 0x03 */
+#define CTL_FOOT 0x04
+#define CTL_PORTAMENTO_TIME 0x05
+#define CTL_DATA_ENTRY 0x06
+#define CTL_MAIN_VOLUME 0x07
+#define CTL_BALANCE 0x08
+/* undefined 0x09 */
+#define CTL_PAN 0x0a
+#define CTL_EXPRESSION 0x0b
+/* undefined 0x0c - 0x0f */
+#define CTL_GENERAL_PURPOSE1 0x10
+#define CTL_GENERAL_PURPOSE2 0x11
+#define CTL_GENERAL_PURPOSE3 0x12
+#define CTL_GENERAL_PURPOSE4 0x13
+/* undefined 0x14 - 0x1f */
+
+/* undefined 0x20 */
+
+/*
+ * The controller numbers 0x21 to 0x3f are reserved for the
+ * least significant bytes of the controllers 0x00 to 0x1f.
+ * These controllers are not recognised by the driver.
+ *
+ * Controllers 64 to 69 (0x40 to 0x45) are on/off switches.
+ * 0=OFF and 127=ON (intermediate values are possible)
+ */
+#define CTL_DAMPER_PEDAL 0x40
+#define CTL_SUSTAIN CTL_DAMPER_PEDAL /* Alias */
+#define CTL_HOLD CTL_DAMPER_PEDAL /* Alias */
+#define CTL_PORTAMENTO 0x41
+#define CTL_SOSTENUTO 0x42
+#define CTL_SOFT_PEDAL 0x43
+/* undefined 0x44 */
+#define CTL_HOLD2 0x45
+/* undefined 0x46 - 0x4f */
+
+#define CTL_GENERAL_PURPOSE5 0x50
+#define CTL_GENERAL_PURPOSE6 0x51
+#define CTL_GENERAL_PURPOSE7 0x52
+#define CTL_GENERAL_PURPOSE8 0x53
+/* undefined 0x54 - 0x5a */
+#define CTL_EXT_EFF_DEPTH 0x5b
+#define CTL_TREMOLO_DEPTH 0x5c
+#define CTL_CHORUS_DEPTH 0x5d
+#define CTL_DETUNE_DEPTH 0x5e
+#define CTL_CELESTE_DEPTH CTL_DETUNE_DEPTH /* Alias for the above one */
+#define CTL_PHASER_DEPTH 0x5f
+#define CTL_DATA_INCREMENT 0x60
+#define CTL_DATA_DECREMENT 0x61
+#define CTL_NONREG_PARM_NUM_LSB 0x62
+#define CTL_NONREG_PARM_NUM_MSB 0x63
+#define CTL_REGIST_PARM_NUM_LSB 0x64
+#define CTL_REGIST_PARM_NUM_MSB 0x65
+/* undefined 0x66 - 0x78 */
+/* reserved 0x79 - 0x7f */
+
+/* Pseudo controllers (not midi compatible) */
+#define CTRL_PITCH_BENDER 255
+#define CTRL_PITCH_BENDER_RANGE 254
+#define CTRL_EXPRESSION 253 /* Obsolete */
+#define CTRL_MAIN_VOLUME 252 /* Obsolete */
+
+#define SEQ_BALANCE 11
+#define SEQ_VOLMODE 12
+
+/*
+ * Volume mode decides how volumes are used
+ */
+
+#define VOL_METHOD_ADAGIO 1
+#define VOL_METHOD_LINEAR 2
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ * input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE 0xfd /* Long events */
+/*
+ * SEQ_FULLSIZE events are used for loading patches/samples to the
+ * synthesizer devices. These events are passed directly to the driver
+ * of the associated synthesizer device. There is no limit to the size
+ * of the extended events. These events are not queued but executed
+ * immediately when the write() is called (execution can take several
+ * seconds of time).
+ *
+ * When a SEQ_FULLSIZE message is written to the device, it must
+ * be written using exactly one write() call. Other events cannot
+ * be mixed to the same write.
+ *
+ * For FM synths (YM3812/OPL3) use struct sbi_instrument and write
+ * it to the /dev/sequencer. Don't write other data together with
+ * the instrument structure Set the key field of the structure to
+ * FM_PATCH. The device field is used to route the patch to the
+ * corresponding device.
+ *
+ * For Gravis UltraSound use struct patch_info. Initialize the key field
+ * to GUS_PATCH.
+ */
+#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) OBSOLETE */
+
+/*
+ * Record for FM patches
+ */
+
+typedef u_char sbi_instr_data[32];
+
+struct sbi_instrument {
+ u_short key; /* FM_PATCH or OPL3_PATCH */
+#define FM_PATCH _PATCHKEY(0x01)
+#define OPL3_PATCH _PATCHKEY(0x03)
+ short device; /* Synth# (0-4) */
+ int channel; /* Program# to be initialized */
+ sbi_instr_data operators; /* Reg. settings for operator cells
+ * (.SBI format) */
+};
+
+struct synth_info { /* Read only */
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ int synth_type;
+#define SYNTH_TYPE_FM 0
+#define SYNTH_TYPE_SAMPLE 1
+#define SYNTH_TYPE_MIDI 2 /* Midi interface */
+
+ int synth_subtype;
+#define FM_TYPE_ADLIB 0x00
+#define FM_TYPE_OPL3 0x01
+
+#define SAMPLE_TYPE_GUS 0x10
+
+ int perc_mode; /* No longer supported */
+ int nr_voices;
+ int nr_drums; /* Obsolete field */
+ int instr_bank_size;
+ u_long capabilities;
+#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */
+#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */
+#define SYNTH_CAP_INPUT 0x00000004 /* Input (MIDI) device */
+ int dummies[19]; /* Reserve space */
+};
+
+struct sound_timer_info {
+ char name[32];
+ int caps;
+};
+
+#define MIDI_CAP_MPU401 1 /* MPU-401 intelligent mode */
+
+struct midi_info {
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ u_long capabilities; /* To be defined later */
+ int dev_type;
+ int dummies[18]; /* Reserve space */
+};
+
+/*
+ * ioctl commands for the /dev/midi##
+ */
+typedef struct {
+ u_char cmd;
+ char nr_args, nr_returns;
+ u_char data[30];
+} mpu_command_rec;
+
+#define SNDCTL_MIDI_PRETIME _IOWR('m', 0, int)
+#define SNDCTL_MIDI_MPUMODE _IOWR('m', 1, int)
+#define SNDCTL_MIDI_MPUCMD _IOWR('m', 2, mpu_command_rec)
+
+/*
+ * IOCTL commands for /dev/dsp and /dev/audio
+ */
+
+#define SNDCTL_DSP_RESET _IO ('P', 0)
+#define SNDCTL_DSP_SYNC _IO ('P', 1)
+#define SNDCTL_DSP_SPEED _IOWR('P', 2, int)
+#define SNDCTL_DSP_STEREO _IOWR('P', 3, int)
+#define SNDCTL_DSP_GETBLKSIZE _IOR('P', 4, int)
+#define SNDCTL_DSP_SETBLKSIZE _IOW('P', 4, int)
+#define SNDCTL_DSP_SETFMT _IOWR('P',5, int) /* Selects ONE fmt*/
+
+/*
+ * SOUND_PCM_WRITE_CHANNELS is not that different
+ * from SNDCTL_DSP_STEREO
+ */
+#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int)
+#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int)
+#define SNDCTL_DSP_POST _IO ('P', 8)
+
+/*
+ * SNDCTL_DSP_SETBLKSIZE and the following two calls mostly do
+ * the same thing, i.e. set the block size used in DMA transfers.
+ */
+#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int)
+#define SNDCTL_DSP_SETFRAGMENT _IOWR('P',10, int)
+
+
+#define SNDCTL_DSP_GETFMTS _IOR ('P',11, int) /* Returns a mask */
+/*
+ * Buffer status queries.
+ */
+typedef struct audio_buf_info {
+ int fragments; /* # of avail. frags (partly used ones not counted) */
+ int fragstotal; /* Total # of fragments allocated */
+ int fragsize; /* Size of a fragment in bytes */
+
+ int bytes; /* Avail. space in bytes (includes partly used fragments) */
+ /* Note! 'bytes' could be more than fragments*fragsize */
+} audio_buf_info;
+
+#define SNDCTL_DSP_GETOSPACE _IOR ('P',12, audio_buf_info)
+#define SNDCTL_DSP_GETISPACE _IOR ('P',13, audio_buf_info)
+
+/*
+ * SNDCTL_DSP_NONBLOCK is the same (but less powerful, since the
+ * action cannot be undone) of FIONBIO. The same can be achieved
+ * by opening the device with O_NDELAY
+ */
+#define SNDCTL_DSP_NONBLOCK _IO ('P',14)
+
+#define SNDCTL_DSP_GETCAPS _IOR ('P',15, int)
+#define DSP_CAP_REVISION 0x000000ff /* revision level (0 to 255) */
+#define DSP_CAP_DUPLEX 0x00000100 /* Full duplex record/playback */
+#define DSP_CAP_REALTIME 0x00000200 /* Real time capability */
+#define DSP_CAP_BATCH 0x00000400
+ /*
+ * Device has some kind of internal buffers which may
+ * cause some delays and decrease precision of timing
+ */
+#define DSP_CAP_COPROC 0x00000800
+ /* Has a coprocessor, sometimes it's a DSP but usually not */
+#define DSP_CAP_TRIGGER 0x00001000 /* Supports SETTRIGGER */
+
+/*
+ * What do these function do ?
+ */
+#define SNDCTL_DSP_GETTRIGGER _IOR ('P',16, int)
+#define SNDCTL_DSP_SETTRIGGER _IOW ('P',16, int)
+#define PCM_ENABLE_INPUT 0x00000001
+#define PCM_ENABLE_OUTPUT 0x00000002
+
+typedef struct count_info {
+ int bytes; /* Total # of bytes processed */
+ int blocks; /* # of fragment transitions since last time */
+ int ptr; /* Current DMA pointer value */
+} count_info;
+
+/*
+ * GETIPTR and GETISPACE are not that different... same for out.
+ */
+#define SNDCTL_DSP_GETIPTR _IOR ('P',17, count_info)
+#define SNDCTL_DSP_GETOPTR _IOR ('P',18, count_info)
+
+typedef struct buffmem_desc {
+ caddr_t buffer;
+ int size;
+} buffmem_desc;
+
+#define SNDCTL_DSP_MAPINBUF _IOR ('P', 19, buffmem_desc)
+#define SNDCTL_DSP_MAPOUTBUF _IOR ('P', 20, buffmem_desc)
+#define SNDCTL_DSP_SETSYNCRO _IO ('P', 21)
+
+/*
+ * I guess these are the readonly version of the same
+ * functions that exist above as SNDCTL_DSP_...
+ */
+#define SOUND_PCM_READ_RATE _IOR ('P', 2, int)
+#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int)
+#define SOUND_PCM_READ_BITS _IOR ('P', 5, int)
+#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int)
+
+/*
+ * ioctl calls to be used in communication with coprocessors and
+ * DSP chips.
+ */
+
+typedef struct copr_buffer {
+ int command; /* Set to 0 if not used */
+ int flags;
+#define CPF_NONE 0x0000
+#define CPF_FIRST 0x0001 /* First block */
+#define CPF_LAST 0x0002 /* Last block */
+ int len;
+ int offs; /* If required by the device (0 if not used) */
+
+ u_char data[4000]; /* NOTE! 4000 is not 4k */
+} copr_buffer;
+
+typedef struct copr_debug_buf {
+ int command; /* Used internally. Set to 0 */
+ int parm1;
+ int parm2;
+ int flags;
+ int len; /* Length of data in bytes */
+} copr_debug_buf;
+
+typedef struct copr_msg {
+ int len;
+ u_char data[4000];
+} copr_msg;
+
+#define SNDCTL_COPR_RESET _IO ('C', 0)
+#define SNDCTL_COPR_LOAD _IOWR('C', 1, copr_buffer)
+#define SNDCTL_COPR_RDATA _IOWR('C', 2, copr_debug_buf)
+#define SNDCTL_COPR_RCODE _IOWR('C', 3, copr_debug_buf)
+#define SNDCTL_COPR_WDATA _IOW ('C', 4, copr_debug_buf)
+#define SNDCTL_COPR_WCODE _IOW ('C', 5, copr_debug_buf)
+#define SNDCTL_COPR_RUN _IOWR('C', 6, copr_debug_buf)
+#define SNDCTL_COPR_HALT _IOWR('C', 7, copr_debug_buf)
+#define SNDCTL_COPR_SENDMSG _IOW ('C', 8, copr_msg)
+#define SNDCTL_COPR_RCVMSG _IOR ('C', 9, copr_msg)
+
+/*
+ * IOCTL commands for /dev/mixer
+ */
+
+/*
+ * Mixer devices
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum.
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ */
+
+#define SOUND_MIXER_NRDEVICES 17
+#define SOUND_MIXER_VOLUME 0
+#define SOUND_MIXER_BASS 1
+#define SOUND_MIXER_TREBLE 2
+#define SOUND_MIXER_SYNTH 3
+#define SOUND_MIXER_PCM 4
+#define SOUND_MIXER_SPEAKER 5
+#define SOUND_MIXER_LINE 6
+#define SOUND_MIXER_MIC 7
+#define SOUND_MIXER_CD 8
+#define SOUND_MIXER_IMIX 9 /* Recording monitor */
+#define SOUND_MIXER_ALTPCM 10
+#define SOUND_MIXER_RECLEV 11 /* Recording level */
+#define SOUND_MIXER_IGAIN 12 /* Input gain */
+#define SOUND_MIXER_OGAIN 13 /* Output gain */
+/*
+ * The AD1848 codec and compatibles have three line level inputs
+ * (line, aux1 and aux2). Since each card manufacturer have assigned
+ * different meanings to these inputs, it's inpractical to assign
+ * specific meanings (line, cd, synth etc.) to them.
+ */
+#define SOUND_MIXER_LINE1 14 /* Input source 1 (aux1) */
+#define SOUND_MIXER_LINE2 15 /* Input source 2 (aux2) */
+#define SOUND_MIXER_LINE3 16 /* Input source 3 (line) */
+
+/*
+ * Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX)
+ * Not counted to SOUND_MIXER_NRDEVICES, but use the same number space
+ */
+#define SOUND_ONOFF_MIN 28
+#define SOUND_ONOFF_MAX 30
+#define SOUND_MIXER_MUTE 28 /* 0 or 1 */
+#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_LOUD 30 /* 0 or 1 */
+
+/* Note! Number 31 cannot be used since the sign bit is reserved */
+
+#define SOUND_DEVICE_LABELS { \
+ "Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \
+ "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \
+ "Line1", "Line2", "Line3"}
+
+#define SOUND_DEVICE_NAMES { \
+ "vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+ "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \
+ "line1", "line2", "line3"}
+
+/* Device bitmask identifiers */
+
+#define SOUND_MIXER_RECSRC 0xff /* 1 bit per recording source */
+#define SOUND_MIXER_DEVMASK 0xfe /* 1 bit per supported device */
+#define SOUND_MIXER_RECMASK 0xfd /* 1 bit per supp. recording source */
+#define SOUND_MIXER_CAPS 0xfc
+#define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only 1 rec. src at a time */
+#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
+
+/* Device mask bits */
+
+#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD (1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV)
+#define SOUND_MASK_IGAIN (1 << SOUND_MIXER_IGAIN)
+#define SOUND_MASK_OGAIN (1 << SOUND_MIXER_OGAIN)
+#define SOUND_MASK_LINE1 (1 << SOUND_MIXER_LINE1)
+#define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2)
+#define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3)
+
+#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev) _IOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_IGAIN MIXER_READ(SOUND_MIXER_IGAIN)
+#define SOUND_MIXER_READ_OGAIN MIXER_READ(SOUND_MIXER_OGAIN)
+#define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1)
+#define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2)
+#define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3)
+#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev) _IOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_IGAIN MIXER_WRITE(SOUND_MIXER_IGAIN)
+#define SOUND_MIXER_WRITE_OGAIN MIXER_WRITE(SOUND_MIXER_OGAIN)
+#define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1)
+#define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2)
+#define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3)
+#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
+
+/*
+ * Level 2 event types for /dev/sequencer
+ */
+
+/*
+ * The 4 most significant bits of byte 0 specify the class of
+ * the event:
+ *
+ * 0x8X = system level events,
+ * 0x9X = device/port specific events, event[1] = device/port,
+ * The last 4 bits give the subtype:
+ * 0x02 = Channel event (event[3] = chn).
+ * 0x01 = note event (event[4] = note).
+ * (0x01 is not used alone but always with bit 0x02).
+ * event[2] = MIDI message code (0x80=note off etc.)
+ *
+ */
+
+#define EV_SEQ_LOCAL 0x80
+#define EV_TIMING 0x81
+#define EV_CHN_COMMON 0x92
+#define EV_CHN_VOICE 0x93
+#define EV_SYSEX 0x94
+/*
+ * Event types 200 to 220 are reserved for application use.
+ * These numbers will not be used by the driver.
+ */
+
+/*
+ * Events for event type EV_CHN_VOICE
+ */
+
+#define MIDI_NOTEOFF 0x80
+#define MIDI_NOTEON 0x90
+#define MIDI_KEY_PRESSURE 0xA0
+
+/*
+ * Events for event type EV_CHN_COMMON
+ */
+
+#define MIDI_CTL_CHANGE 0xB0
+#define MIDI_PGM_CHANGE 0xC0
+#define MIDI_CHN_PRESSURE 0xD0
+#define MIDI_PITCH_BEND 0xE0
+
+#define MIDI_SYSTEM_PREFIX 0xF0
+
+/*
+ * Timer event types
+ */
+#define TMR_WAIT_REL 1 /* Time relative to the prev time */
+#define TMR_WAIT_ABS 2 /* Absolute time since TMR_START */
+#define TMR_STOP 3
+#define TMR_START 4
+#define TMR_CONTINUE 5
+#define TMR_TEMPO 6
+#define TMR_ECHO 8
+#define TMR_CLOCK 9 /* MIDI clock */
+#define TMR_SPP 10 /* Song position pointer */
+#define TMR_TIMESIG 11 /* Time signature */
+
+/*
+ * Local event types
+ */
+#define LOCL_STARTAUDIO 1
+
+#if (!defined(__KERNEL__) && !defined(KERNEL) && !defined(INKERNEL) && !defined(_KERNEL)) || defined(USE_SEQ_MACROS)
+/*
+ * Some convenience macros to simplify programming of the
+ * /dev/sequencer interface
+ *
+ * These macros define the API which should be used when possible.
+ */
+
+#ifndef USE_SIMPLE_MACROS
+void seqbuf_dump(void); /* This function must be provided by programs */
+
+/* Sample seqbuf_dump() implementation:
+ *
+ * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes
+ *
+ * int seqfd; -- The file descriptor for /dev/sequencer.
+ *
+ * void
+ * seqbuf_dump ()
+ * {
+ * if (_seqbufptr)
+ * if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ * {
+ * perror ("write /dev/sequencer");
+ * exit (-1);
+ * }
+ * _seqbufptr = 0;
+ * }
+ */
+
+#define SEQ_DEFINEBUF(len) \
+ u_char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0
+#define SEQ_USE_EXTBUF() \
+ extern u_char _seqbuf[]; \
+ extern int _seqbuflen;extern int _seqbufptr
+#define SEQ_DECLAREBUF() SEQ_USE_EXTBUF()
+#define SEQ_PM_DEFINES struct patmgr_info _pm_info
+#define _SEQ_NEEDBUF(len) \
+ if ((_seqbufptr+(len)) > _seqbuflen) \
+ seqbuf_dump()
+#define _SEQ_ADVBUF(len) _seqbufptr += len
+#define SEQ_DUMPBUF seqbuf_dump
+#else
+/*
+ * This variation of the sequencer macros is used just to format one event
+ * using fixed buffer.
+ *
+ * The program using the macro library must define the following macros before
+ * using this library.
+ *
+ * #define _seqbuf name of the buffer (u_char[])
+ * #define _SEQ_ADVBUF(len) If the applic needs to know the exact
+ * size of the event, this macro can be used.
+ * Otherwise this must be defined as empty.
+ * #define _seqbufptr Define the name of index variable or 0 if
+ * not required.
+ */
+#define _SEQ_NEEDBUF(len) /* empty */
+#endif
+
+#define PM_LOAD_PATCH(dev, bank, pgm) \
+ (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+ _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \
+ _pm_info.parm1 = bank, _pm_info.parm2 = 1, \
+ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+#define PM_LOAD_PATCHES(dev, bank, pgm) \
+ (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
+ _pm_info.device=dev, bcopy( pgm, _pm_info.data.data8, 128), \
+ _pm_info.parm1 = bank, _pm_info.parm2 = 128, \
+ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+
+#define SEQ_VOLUME_MODE(dev, mode) { \
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (mode);\
+ _seqbuf[_seqbufptr+4] = 0;\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+/*
+ * Midi voice messages
+ */
+
+#define _CHN_VOICE(dev, event, chn, note, parm) { \
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = EV_CHN_VOICE;\
+ _seqbuf[_seqbufptr+1] = (dev);\
+ _seqbuf[_seqbufptr+2] = (event);\
+ _seqbuf[_seqbufptr+3] = (chn);\
+ _seqbuf[_seqbufptr+4] = (note);\
+ _seqbuf[_seqbufptr+5] = (parm);\
+ _seqbuf[_seqbufptr+6] = (0);\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_START_NOTE(dev, chn, note, vol) \
+ _CHN_VOICE(dev, MIDI_NOTEON, chn, note, vol)
+
+#define SEQ_STOP_NOTE(dev, chn, note, vol) \
+ _CHN_VOICE(dev, MIDI_NOTEOFF, chn, note, vol)
+
+#define SEQ_KEY_PRESSURE(dev, chn, note, pressure) \
+ _CHN_VOICE(dev, MIDI_KEY_PRESSURE, chn, note, pressure)
+
+/*
+ * Midi channel messages
+ */
+
+#define _CHN_COMMON(dev, event, chn, p1, p2, w14) { \
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = EV_CHN_COMMON;\
+ _seqbuf[_seqbufptr+1] = (dev);\
+ _seqbuf[_seqbufptr+2] = (event);\
+ _seqbuf[_seqbufptr+3] = (chn);\
+ _seqbuf[_seqbufptr+4] = (p1);\
+ _seqbuf[_seqbufptr+5] = (p2);\
+ *(short *)&_seqbuf[_seqbufptr+6] = (w14);\
+ _SEQ_ADVBUF(8);}
+/*
+ * SEQ_SYSEX permits sending of sysex messages. (It may look that it permits
+ * sending any MIDI bytes but it's absolutely not possible. Trying to do
+ * so _will_ cause problems with MPU401 intelligent mode).
+ *
+ * Sysex messages are sent in blocks of 1 to 6 bytes. Longer messages must be
+ * sent by calling SEQ_SYSEX() several times (there must be no other events
+ * between them). First sysex fragment must have 0xf0 in the first byte
+ * and the last byte (buf[len-1] of the last fragment must be 0xf7. No byte
+ * between these sysex start and end markers cannot be larger than 0x7f. Also
+ * lengths of each fragments (except the last one) must be 6.
+ *
+ * Breaking the above rules may work with some MIDI ports but is likely to
+ * cause fatal problems with some other devices (such as MPU401).
+ */
+#define SEQ_SYSEX(dev, buf, len) { \
+ int i, l=(len); if (l>6)l=6;\
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = EV_SYSEX;\
+ for(i=0;i<l;i++)_seqbuf[_seqbufptr+i+1] = (buf)[i];\
+ for(i=l;i<6;i++)_seqbuf[_seqbufptr+i+1] = 0xff;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, chn, pressure) \
+ _CHN_COMMON(dev, MIDI_CHN_PRESSURE, chn, pressure, 0, 0)
+
+#define SEQ_SET_PATCH(dev, chn, patch) \
+ _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0)
+
+#define SEQ_CONTROL(dev, chn, controller, value) \
+ _CHN_COMMON(dev, MIDI_CTL_CHANGE, chn, controller, 0, value)
+
+#define SEQ_BENDER(dev, chn, value) \
+ _CHN_COMMON(dev, MIDI_PITCH_BEND, chn, 0, 0, value)
+
+
+#define SEQ_V2_X_CONTROL(dev, voice, controller, value) { \
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (controller);\
+ *(short *)&_seqbuf[_seqbufptr+5] = (value);\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+/*
+ * The following 5 macros are incorrectly implemented and obsolete.
+ * Use SEQ_BENDER and SEQ_CONTROL (with proper controller) instead.
+ */
+
+#define SEQ_PITCHBEND(dev, voice, value) \
+ SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) \
+ SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) \
+ SEQ_CONTROL(dev, voice, CTL_EXPRESSION, value*128)
+#define SEQ_MAIN_VOLUME(dev, voice, value) \
+ SEQ_CONTROL(dev, voice, CTL_MAIN_VOLUME, (value*16383)/100)
+#define SEQ_PANNING(dev, voice, pos) \
+ SEQ_CONTROL(dev, voice, CTL_PAN, (pos+128) / 2)
+
+/*
+ * Timing and syncronization macros
+ */
+
+#define _TIMER_EVENT(ev, parm) { \
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr+0] = EV_TIMING; \
+ _seqbuf[_seqbufptr+1] = (ev); \
+ _seqbuf[_seqbufptr+2] = 0;\
+ _seqbuf[_seqbufptr+3] = 0;\
+ *(u_int *)&_seqbuf[_seqbufptr+4] = (parm); \
+ _SEQ_ADVBUF(8); \
+ }
+
+#define SEQ_START_TIMER() _TIMER_EVENT(TMR_START, 0)
+#define SEQ_STOP_TIMER() _TIMER_EVENT(TMR_STOP, 0)
+#define SEQ_CONTINUE_TIMER() _TIMER_EVENT(TMR_CONTINUE, 0)
+#define SEQ_WAIT_TIME(ticks) _TIMER_EVENT(TMR_WAIT_ABS, ticks)
+#define SEQ_DELTA_TIME(ticks) _TIMER_EVENT(TMR_WAIT_REL, ticks)
+#define SEQ_ECHO_BACK(key) _TIMER_EVENT(TMR_ECHO, key)
+#define SEQ_SET_TEMPO(value) _TIMER_EVENT(TMR_TEMPO, value)
+#define SEQ_SONGPOS(pos) _TIMER_EVENT(TMR_SPP, pos)
+#define SEQ_TIME_SIGNATURE(sig) _TIMER_EVENT(TMR_TIMESIG, sig)
+
+/*
+ * Local control events
+ */
+
+#define _LOCAL_EVENT(ev, parm) { \
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr+0] = EV_SEQ_LOCAL; \
+ _seqbuf[_seqbufptr+1] = (ev); \
+ _seqbuf[_seqbufptr+2] = 0;\
+ _seqbuf[_seqbufptr+3] = 0;\
+ *(u_int *)&_seqbuf[_seqbufptr+4] = (parm); \
+ _SEQ_ADVBUF(8); \
+ }
+
+#define SEQ_PLAYAUDIO(devmask) _LOCAL_EVENT(LOCL_STARTAUDIO, devmask)
+/*
+ * Events for the level 1 interface only
+ */
+
+#define SEQ_MIDIOUT(device, byte) { \
+ _SEQ_NEEDBUF(4);\
+ _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+ _seqbuf[_seqbufptr+1] = (byte);\
+ _seqbuf[_seqbufptr+2] = (device);\
+ _seqbuf[_seqbufptr+3] = 0;\
+ _SEQ_ADVBUF(4);}
+
+/*
+ * Patch loading.
+ */
+#define SEQ_WRPATCH(patchx, len) { \
+ if (_seqbufptr) seqbuf_dump(); \
+ if (write(seqfd, (char*)(patchx), len)==-1) \
+ perror("Write patch: /dev/sequencer"); \
+ }
+
+#define SEQ_WRPATCH2(patchx, len) \
+ ( seqbuf_dump(), write(seqfd, (char*)(patchx), len) )
+
+#endif
+
+/*
+ * Here I have moved all the aliases for ioctl names.
+ */
+
+#define SNDCTL_DSP_SAMPLESIZE SNDCTL_DSP_SETFMT
+#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SETFMT
+#define SOUND_PCM_SETFMT SNDCTL_DSP_SETFMT
+
+#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED
+#define SOUND_PCM_POST SNDCTL_DSP_POST
+#define SOUND_PCM_RESET SNDCTL_DSP_RESET
+#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC
+#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE
+#define SOUND_PCM_SETFRAGMENT SNDCTL_DSP_SETFRAGMENT
+#define SOUND_PCM_GETFMTS SNDCTL_DSP_GETFMTS
+#define SOUND_PCM_GETOSPACE SNDCTL_DSP_GETOSPACE
+#define SOUND_PCM_GETISPACE SNDCTL_DSP_GETISPACE
+#define SOUND_PCM_NONBLOCK SNDCTL_DSP_NONBLOCK
+#define SOUND_PCM_GETCAPS SNDCTL_DSP_GETCAPS
+#define SOUND_PCM_GETTRIGGER SNDCTL_DSP_GETTRIGGER
+#define SOUND_PCM_SETTRIGGER SNDCTL_DSP_SETTRIGGER
+#define SOUND_PCM_SETSYNCRO SNDCTL_DSP_SETSYNCRO
+#define SOUND_PCM_GETIPTR SNDCTL_DSP_GETIPTR
+#define SOUND_PCM_GETOPTR SNDCTL_DSP_GETOPTR
+#define SOUND_PCM_MAPINBUF SNDCTL_DSP_MAPINBUF
+#define SOUND_PCM_MAPOUTBUF SNDCTL_DSP_MAPOUTBUF
+
+#endif /* SOUNDCARD_H */
diff --git a/sys/i386/isa/snd/ulaw.h b/sys/i386/isa/snd/ulaw.h
new file mode 100644
index 0000000..8971f63
--- /dev/null
+++ b/sys/i386/isa/snd/ulaw.h
@@ -0,0 +1,93 @@
+/*
+ * on entry: ulaw, on exit: unsigned 8 bit.
+ */
+static unsigned char ulaw_dsp[] = {
+ 3, 7, 11, 15, 19, 23, 27, 31,
+ 35, 39, 43, 47, 51, 55, 59, 63,
+ 66, 68, 70, 72, 74, 76, 78, 80,
+ 82, 84, 86, 88, 90, 92, 94, 96,
+
+ 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113,
+ 113, 114, 114, 115, 115, 116, 116, 117,
+ 117, 118, 118, 119, 119, 120, 120, 121,
+
+ 121, 121, 122, 122, 122, 122, 123, 123,
+ 123, 123, 124, 124, 124, 124, 125, 125,
+ 125, 125, 125, 125, 126, 126, 126, 126,
+ 126, 126, 126, 126, 127, 127, 127, 127,
+
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+
+
+ 253, 249, 245, 241, 237, 233, 229, 225,
+ 221, 217, 213, 209, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 174, 172, 170, 168, 166, 164, 162, 160,
+
+ 158, 157, 156, 155, 154, 153, 152, 151,
+ 150, 149, 148, 147, 146, 145, 144, 143,
+ 143, 142, 142, 141, 141, 140, 140, 139,
+ 139, 138, 138, 137, 137, 136, 136, 135,
+
+ 135, 135, 134, 134, 134, 134, 133, 133,
+ 133, 133, 132, 132, 132, 132, 131, 131,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 130, 130, 130, 130, 129, 129, 129, 129,
+
+ 129, 129, 129, 129, 129, 129, 129, 129,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+};
+
+#ifndef DSP_ULAW_NOT_WANTED
+/*
+ * on entry: unsigned 8-bit, on exit: ulaw.
+ */
+static unsigned char dsp_ulaw[] = {
+ 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 2, 2, 2, 2, 3, 3, 3,
+ 3, 4, 4, 4, 4, 5, 5, 5,
+ 5, 6, 6, 6, 6, 7, 7, 7,
+
+ 7, 8, 8, 8, 8, 9, 9, 9,
+ 9, 10, 10, 10, 10, 11, 11, 11,
+ 11, 12, 12, 12, 12, 13, 13, 13,
+ 13, 14, 14, 14, 14, 15, 15, 15,
+
+ 15, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 22, 23,
+ 23, 24, 24, 25, 25, 26, 26, 27,
+ 27, 28, 28, 29, 29, 30, 30, 31,
+
+ 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 49, 51, 53, 55, 57, 59, 61,
+ 63, 66, 70, 74, 78, 84, 92, 104,
+
+
+ 254, 231, 219, 211, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 175, 174, 173, 172, 171, 170, 169, 168,
+ 167, 166, 165, 164, 163, 162, 161, 160,
+
+ 159, 159, 158, 158, 157, 157, 156, 156,
+ 155, 155, 154, 154, 153, 153, 152, 152,
+ 151, 151, 150, 150, 149, 149, 148, 148,
+ 147, 147, 146, 146, 145, 145, 144, 144,
+
+ 143, 143, 143, 143, 142, 142, 142, 142,
+ 141, 141, 141, 141, 140, 140, 140, 140,
+ 139, 139, 139, 139, 138, 138, 138, 138,
+ 137, 137, 137, 137, 136, 136, 136, 136,
+
+ 135, 135, 135, 135, 134, 134, 134, 134,
+ 133, 133, 133, 133, 132, 132, 132, 132,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+#endif /* !DSP_ULAW_NOT_WANTED */
OpenPOWER on IntegriCloud