aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorSteven Wallace <swallace@FreeBSD.org>1994-10-01 01:33:47 +0000
committerSteven Wallace <swallace@FreeBSD.org>1994-10-01 01:33:47 +0000
commitfbb347fe32427a1b3fb420db2f83b7cfefaa7890 (patch)
treef28f4deb3347ad09067512ea8870b2769fd0cca6 /sys
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/i386/isa/sound/CHANGELOG110
-rw-r--r--sys/i386/isa/sound/COPYING25
-rw-r--r--sys/i386/isa/sound/README80
-rw-r--r--sys/i386/isa/sound/ad1848.c953
-rw-r--r--sys/i386/isa/sound/adlib_card.c51
-rw-r--r--sys/i386/isa/sound/audio.c424
-rw-r--r--sys/i386/isa/sound/configure.c830
-rw-r--r--sys/i386/isa/sound/dev_table.c269
-rw-r--r--sys/i386/isa/sound/dev_table.h345
-rw-r--r--sys/i386/isa/sound/dmabuf.c964
-rw-r--r--sys/i386/isa/sound/finetune.h49
-rw-r--r--sys/i386/isa/sound/gus_card.c193
-rw-r--r--sys/i386/isa/sound/gus_hw.h50
-rw-r--r--sys/i386/isa/sound/gus_linearvol.h18
-rw-r--r--sys/i386/isa/sound/gus_midi.c309
-rw-r--r--sys/i386/isa/sound/gus_vol.c147
-rw-r--r--sys/i386/isa/sound/gus_wave.c3246
-rw-r--r--sys/i386/isa/sound/ics2101.c263
-rw-r--r--sys/i386/isa/sound/local.h27
-rw-r--r--sys/i386/isa/sound/midi_ctrl.h22
-rw-r--r--sys/i386/isa/sound/midi_synth.c474
-rw-r--r--sys/i386/isa/sound/midi_synth.h45
-rw-r--r--sys/i386/isa/sound/midibuf.c443
-rw-r--r--sys/i386/isa/sound/mpu401.c1722
-rw-r--r--sys/i386/isa/sound/opl3.c1225
-rw-r--r--sys/i386/isa/sound/opl3.h260
-rw-r--r--sys/i386/isa/sound/os.h306
-rw-r--r--sys/i386/isa/sound/pas.h250
-rw-r--r--sys/i386/isa/sound/pas2_card.c407
-rw-r--r--sys/i386/isa/sound/pas2_midi.c338
-rw-r--r--sys/i386/isa/sound/pas2_mixer.c398
-rw-r--r--sys/i386/isa/sound/pas2_pcm.c453
-rw-r--r--sys/i386/isa/sound/patmgr.c264
-rw-r--r--sys/i386/isa/sound/pss.c924
-rw-r--r--sys/i386/isa/sound/pss.h371
-rw-r--r--sys/i386/isa/sound/sb.h28
-rw-r--r--sys/i386/isa/sound/sb16_dsp.c568
-rw-r--r--sys/i386/isa/sound/sb16_midi.c304
-rw-r--r--sys/i386/isa/sound/sb_card.c52
-rw-r--r--sys/i386/isa/sound/sb_dsp.c863
-rw-r--r--sys/i386/isa/sound/sb_midi.c252
-rw-r--r--sys/i386/isa/sound/sb_mixer.c453
-rw-r--r--sys/i386/isa/sound/sb_mixer.h221
-rw-r--r--sys/i386/isa/sound/sequencer.c1861
-rw-r--r--sys/i386/isa/sound/sound_calls.h243
-rw-r--r--sys/i386/isa/sound/sound_config.h258
-rw-r--r--sys/i386/isa/sound/sound_switch.c528
-rw-r--r--sys/i386/isa/sound/sound_timer.c406
-rw-r--r--sys/i386/isa/sound/soundcard.c401
-rw-r--r--sys/i386/isa/sound/sys_timer.c304
-rw-r--r--sys/i386/isa/sound/tuning.h29
-rw-r--r--sys/i386/isa/sound/uart6850.c323
-rw-r--r--sys/i386/isa/sound/ulaw.h69
53 files changed, 23418 insertions, 0 deletions
diff --git a/sys/i386/isa/sound/CHANGELOG b/sys/i386/isa/sound/CHANGELOG
new file mode 100644
index 000000000000..fcdbb8e3eae6
--- /dev/null
+++ b/sys/i386/isa/sound/CHANGELOG
@@ -0,0 +1,110 @@
+Changelog for version 2.90
+------------------------------------
+
+This is an intermediate release (v3.0 prototype with some experimental
+features disabled). See experimental.txt for more info.
+
+Since pre-3.0-949712
+- GUS MAX support
+- Partially working MSS/WSS support (could work with some cards).
+- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
+ (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
+ GUS MAX, but it doesn't work yet.
+Since pre-3.0-940426
+- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
+This codec chip is used in various soundcards. This version is developed
+for the 16 bit daughtercard of GUS. It should work with other cards also
+if the following requirements are met:
+ - The I/O, IRQ and DMA settings are jumper selectable or
+ the card is initialized by booting DOS before booting Linux (etc.).
+ - You add the IO, IRQ and DMA settings manually to the local.h.
+ (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
+ the base address bust be the base address of the codec chip not the
+ card itself. For the GUS16 these are the same but most MSS compatible
+ cards have the codec located at card_base+4.
+- Some minor changes
+
+Since 2.5 (******* MAJOR REWRITE ***********)
+
+This version is based on v2.3. I have tried to maintain two versions
+together so that this one should have the same features than v2.5.
+Something may still be missing. If you notice such things, please let me
+know.
+
+The Readme.v30 contains more details.
+
+- /dev/midi## devices.
+- /dev/sequencer2
+
+Since 2.5-beta2
+- Some fine tuning to the GUS v3.7 mixer code.
+- Fixed speed limits for the plain SB (1.0 to 2.0).
+
+Since 2.5-beta
+- Fixed OPL-3 detection with SB. Caused problems with PAS16.
+- GUS v3.7 mixer support.
+
+Since 2.4
+- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
+- Fixed truncated sound on /dev/dsp when the device is closed.
+- Linear volume mode for GUS
+- Pitch bends larger than +/- 2 octaves.
+- MIDI recording for SB and SB Pro. (Untested).
+- Some other fixes.
+- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
+- Implemented better detection for OPL-3. This should be usefull if you
+ have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
+- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
+
+Since 2.3b
+- Fixed bug which made it impossible to make long recordings to disk.
+ Recording was not restarted after a buffer overflow situation.
+- Limited mixer support for GUS.
+- Numerous improvements to the GUS driver by Andrew Robinson. Including
+ some click removal etc.
+
+Since 2.3
+- Fixed some minor bugs in the SB16 driver.
+
+Since 2.2b
+- Full SB16 DSP support. 8/16 bit, mono/stereo
+- The SCO and FreeBSD versions should be in sync now. There are some
+ problems with SB16 and GUS in the freebsd versions.
+ The DMA buffer allocation of the SCO version has been polished but
+ there could still be some problems. At least it hogs memory.
+ The DMA channel
+ configuration method used in the sco/System is a hack.
+- Support for the MPU emulation of the SB16.
+- Some big arrays are now allocated boot time. This makes the bss segment
+ smaller which makes it possible to use the full driver with
+ NetBSD. These arrays are not allocated if no suitable soundcard is available.
+- Fixed a bug in the compute_and_set_volume in gus_wave.c
+- Fixed the too fast mono playback problem of SB Pro and PAS16.
+
+Since 2.2
+- Stereo recording for SB Pro. Somehow it was missing and nobody
+ had noticed it earlier.
+- Minor polishing.
+- Interpreting of boot time arguments (sound=) for Linux.
+- Breakup of sb_dsp.c. Parts of the code has been moved to
+ sb_mixer.c and sb_midi.c
+
+Since 2.1
+- Preliminary support for SB16.
+ - The SB16 mixer is supported in it's native mode.
+ - Digitized voice capability up to 44.1 kHz/8 bit/mono
+ (16 bit and stereo support coming in the next release).
+- Fixed some bugs in the digitized voice driver for PAS16.
+- Proper initialization of the SB emulation of latest PAS16 models.
+
+- Significantly improved /dev/dsp and /dev/audio support.
+ - Now supports half duplex mode. It's now possible to record and
+ playback without closing and reopening the device.
+ - It's possible to use smaller buffers than earlier. There is a new
+ ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
+ This call instructs the driver to use smaller buffers. The default
+ buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
+ immediately after opening the device.
+
+Since 2.0
+Just cosmetic changes.
diff --git a/sys/i386/isa/sound/COPYING b/sys/i386/isa/sound/COPYING
new file mode 100644
index 000000000000..d1509c500253
--- /dev/null
+++ b/sys/i386/isa/sound/COPYING
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ *
+ */
diff --git a/sys/i386/isa/sound/README b/sys/i386/isa/sound/README
new file mode 100644
index 000000000000..a4a084706e5b
--- /dev/null
+++ b/sys/i386/isa/sound/README
@@ -0,0 +1,80 @@
+VoxWare v2.90 release notes
+--------------------------
+
+
+ This version includes some hidden features which
+ are described in the file experimental.txt
+ Some of these features are not enabled by default. Look at
+ experimental.txt for more info.
+
+ I just decided to release this version with some
+ incompletely implemented features disabled since
+ there are some new features required by a popular
+ application. In addition there is also support
+ for the GUS MAX and the 16 bit sampling option of GUS.
+
+ The MSS/WSS support works now. At least with SG NX Pro 16.
+
+********* IMPORTANT *****************************************
+Linux 1.0 or later is required to by this driver version.
+
+Don't distribute binaries which use /dev/sequencer and are
+compiled with the soundcard.h of this version. They will
+not work with version 2.x of the driver.
+*************************************************************
+
+
+You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z
+packages to use this driver. They should be in the same
+ftp site or BBS from where you got this driver. For
+example at nic.funet.fi:pub/OS/Linux/*.
+
+If you are looking for the installation instructions, please
+look at linux/Readme.
+
+Compatibility with the earlier versions
+---------------------------------------
+
+This version is backward compatible with the version 2.X. All programs
+compiled with sys/soundcard.h of v2.X should work without problems.
+PROGRAMS COMPILED WITH THE sys/soundcard.h OF THIS VERSION WILL NOT
+WORK WITH v2.X DRIVER. BE CAREFULL WHEN DISTRIBUTING BINARIES COMPILED
+FOR THIS VERSION.
+
+Contributors
+------------
+
+This driver contains code by several contributors. In addition several other
+persons have given usefull suggestions. The following is a list of major
+contributors. (I could have forgotten some names.)
+
+ Craig Metz 1/2 of the PAS16 Mixer and PCM support
+ Rob Hooft Volume computation algorithm for the FM synth.
+ Mika Liljeberg uLaw encoding and decoding routines
+ Greg Lee Volume computation algorithm for the GUS and
+ lot's of valuable suggestions.
+ Andy Warner ISC port
+ Jim Lowe FreeBSD port
+ Anders Baekgaard Bughunting and valuable suggestions.
+ Joerg Schubert SB16 DSP support.
+ Andrew Robinson Improvements to the GUS driver
+ Megens SA MIDI recording for SB and SB Pro.
+ Mikael Nordqvist Linear volume support for GUS.
+ Mikael Nordqvist Linear volume support for GUS.
+ Ian Hartas SVR4.2 port
+ Markus Aroharju and
+ Risto Kankkunen Major contributions to the mixer support
+ of GUS v3.7.
+ Hunyue Yau Mixer support for SG NX Pro.
+ Marc Hoffman PSS support.
+
+Regards,
+
+Hannu Savolainen
+hannu@voxware.pp.fi
+
+Snail mail: Hannu Savolainen
+ Pallaksentie 4 A 2
+ 00970 Helsinki
+ Finland
+FAX: +358 0 395 1968 (usually not connected)
diff --git a/sys/i386/isa/sound/ad1848.c b/sys/i386/isa/sound/ad1848.c
new file mode 100644
index 000000000000..b08f5f76cc68
--- /dev/null
+++ b/sys/i386/isa/sound/ad1848.c
@@ -0,0 +1,953 @@
+/*
+ * sound/ad1848.c
+ *
+ * The low level driver for the AD1848/CS4248 codec chip which
+ * is used for example in the MS Sound System.
+ *
+ * The CS4231 which is used in the GUS MAX and some other cards is
+ * upwards compatible with AD1848 and this driver is able to drive it.
+ *
+ * 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.
+ *
+ */
+
+#define DEB(x)
+#define DEB1(x)
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848)
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT 1
+#define IMODE_INPUT 2
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+typedef struct
+{
+ int base;
+ int irq;
+ int dma_capture, dma_playback;
+ unsigned char MCE_bit;
+
+ int speed;
+ unsigned char speed_bits;
+ int channels;
+ int audio_format;
+ unsigned char format_bits;
+
+ int xfer_count;
+ int irq_mode;
+ int intr_active;
+ int opened;
+ char *chip_name;
+ int mode;
+}
+
+ad1848_info;
+
+static int nr_ad1848_devs = 0;
+static char irq2dev[16] =
+{-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1};
+
+static int ad_format_mask[2 /*devc->mode*/ ] =
+{
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM
+};
+
+static ad1848_info dev_info[MAX_AUDIO_DEV];
+
+#define io_Index_Addr(d) ((d)->base)
+#define io_Indexed_Data(d) ((d)->base+1)
+#define io_Status(d) ((d)->base+2)
+#define io_Polled_IO(d) ((d)->base+3)
+
+static int ad1848_open (int dev, int mode);
+static void ad1848_close (int dev);
+static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
+static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int ad1848_prepare_for_IO (int dev, int bsize, int bcount);
+static void ad1848_reset (int dev);
+static void ad1848_halt (int dev);
+void ad1848_interrupt (int dev);
+
+static int
+ad_read (ad1848_info * devc, int reg)
+{
+ unsigned long flags;
+ int x;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
+ x = INB (io_Indexed_Data (devc));
+ /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
+ RESTORE_INTR (flags);
+
+ return x;
+}
+
+static void
+ad_write (ad1848_info * devc, int reg, int data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
+ OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc));
+ /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */
+ RESTORE_INTR (flags);
+}
+
+static void
+ad_set_MCE (ad1848_info * devc, int state)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (state)
+ devc->MCE_bit = 0x40;
+ else
+ devc->MCE_bit = 0x00;
+ OUTB (devc->MCE_bit, io_Index_Addr (devc));
+ RESTORE_INTR (flags);
+}
+
+static void
+wait_for_calibration (ad1848_info * devc)
+{
+ int timeout = 0;
+
+ /*
+ * 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 and then off.
+ */
+
+ timeout = 100000;
+ while (timeout > 0 && INB (devc->base) == 0x80)
+ timeout--;
+ if (INB (devc->base) == 0x80)
+ printk ("ad1848: Auto calibration timed out(1).\n");
+
+ timeout = 100000;
+ while (timeout > 0 && !(ad_read (devc, 11) & 0x20))
+ timeout--;
+ if (!(ad_read (devc, 11) & 0x20))
+ printk ("ad1848: Auto calibration timed out(2).\n");
+
+ timeout = 100000;
+ while (timeout > 0 && ad_read (devc, 11) & 0x20)
+ timeout--;
+ if (ad_read (devc, 11) & 0x20)
+ printk ("ad1848: Auto calibration timed out(3).\n");
+}
+
+static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] =
+{
+ {
+ "Generic AD1848 codec",
+ DMA_AUTOMODE,
+ AFMT_U8, /* Will be set later */
+ NULL,
+ ad1848_open,
+ ad1848_close,
+ ad1848_output_block,
+ ad1848_start_input,
+ ad1848_ioctl,
+ ad1848_prepare_for_IO,
+ ad1848_prepare_for_IO,
+ ad1848_reset,
+ ad1848_halt,
+ NULL,
+ NULL
+ }};
+
+static int
+ad1848_open (int dev, int mode)
+{
+ int err;
+ ad1848_info *devc = NULL;
+ unsigned long flags;
+
+ DEB (printk ("ad1848_open(int mode = %X)\n", mode));
+
+ if (dev < 0 || dev >= num_audiodevs)
+ return RET_ERROR (ENXIO);
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ if (devc->opened)
+ {
+ RESTORE_INTR (flags);
+ printk ("ad1848: Already opened\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (devc->irq) /* Not managed by another driver */
+ if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt)) < 0)
+ {
+ printk ("ad1848: IRQ in use\n");
+ RESTORE_INTR (flags);
+ return err;
+ }
+
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ RESTORE_INTR (flags);
+ printk ("ad1848: DMA in use\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ devc->intr_active = 0;
+ devc->opened = 1;
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+static void
+ad1848_close (int dev)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DEB (printk ("ad1848_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ devc->intr_active = 0;
+ if (devc->irq) /* Not managed by another driver */
+ snd_release_irq (devc->irq);
+ ad1848_reset (dev);
+ DMAbuf_close_dma (dev);
+ devc->opened = 0;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+set_speed (ad1848_info * devc, int arg)
+{
+ /*
+ * The sampling speed is encoded in the least significant nible of I8. The
+ * LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other
+ * three bits select the divisor (indirectly):
+ *
+ * The available speeds are in the following table. Keep the speeds in
+ * the increasing order.
+ */
+ typedef struct
+ {
+ int speed;
+ unsigned char bits;
+ }
+ speed_struct;
+
+ static speed_struct speed_table[] =
+ {
+ {5510, (0 << 1) | 1},
+ {5510, (0 << 1) | 1},
+ {6620, (7 << 1) | 1},
+ {8000, (0 << 1) | 0},
+ {9600, (7 << 1) | 0},
+ {11025, (1 << 1) | 1},
+ {16000, (1 << 1) | 0},
+ {18900, (2 << 1) | 1},
+ {22050, (3 << 1) | 1},
+ {27420, (2 << 1) | 0},
+ {32000, (3 << 1) | 0},
+ {33075, (6 << 1) | 1},
+ {37800, (4 << 1) | 1},
+ {44100, (5 << 1) | 1},
+ {48000, (6 << 1) | 0}
+ };
+
+ int i, n, selected = -1;
+
+ n = sizeof (speed_table) / sizeof (speed_struct);
+
+ if (arg < speed_table[0].speed)
+ selected = 0;
+ if (arg > speed_table[n - 1].speed)
+ selected = n - 1;
+
+ for (i = 1 /*really*/ ; selected == -1 && i < n; i++)
+ if (speed_table[i].speed == arg)
+ selected = i;
+ else if (speed_table[i].speed > arg)
+ {
+ int diff1, diff2;
+
+ diff1 = arg - speed_table[i - 1].speed;
+ diff2 = speed_table[i].speed - arg;
+
+ if (diff1 < diff2)
+ selected = i - 1;
+ else
+ selected = i;
+ }
+
+ if (selected == -1)
+ {
+ printk ("ad1848: Can't find speed???\n");
+ selected = 3;
+ }
+
+ devc->speed = speed_table[selected].speed;
+ devc->speed_bits = speed_table[selected].bits;
+ return devc->speed;
+}
+
+static int
+set_channels (ad1848_info * devc, int arg)
+{
+ if (arg != 1 && arg != 2)
+ return devc->channels;
+
+ devc->channels = arg;
+ return arg;
+}
+
+static int
+set_format (ad1848_info * devc, int arg)
+{
+
+ static struct format_tbl
+ {
+ int format;
+ unsigned char bits;
+ }
+ format2bits [] =
+ {
+ {
+ 0, 0
+ }
+ ,
+ {
+ AFMT_MU_LAW, 1
+ }
+ ,
+ {
+ AFMT_A_LAW, 3
+ }
+ ,
+ {
+ AFMT_IMA_ADPCM, 5
+ }
+ ,
+ {
+ AFMT_U8, 0
+ }
+ ,
+ {
+ AFMT_S16_LE, 2
+ }
+ ,
+ {
+ AFMT_S16_BE, 6
+ }
+ ,
+ {
+ AFMT_S8, 0
+ }
+ ,
+ {
+ AFMT_U16_LE, 0
+ }
+ ,
+ {
+ AFMT_U16_BE, 0
+ }
+ };
+ int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
+
+ if (!(arg & ad_format_mask[devc->mode]))
+ arg = AFMT_U8;
+
+ devc->audio_format = arg;
+
+ for (i = 0; i < n; i++)
+ if (format2bits[i].format == arg)
+ {
+ if ((devc->format_bits = format2bits[i].bits) == 0)
+ return devc->audio_format = AFMT_U8; /* Was not supported */
+
+ return arg;
+ }
+
+ /* Still hanging here. Something must be terribly wrong */
+ devc->format_bits = 0;
+ return devc->audio_format = AFMT_U8;
+}
+
+static int
+ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return set_speed (devc, arg);
+ return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return devc->speed;
+ return IOCTL_OUT (arg, devc->speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return set_channels (devc, arg + 1) - 1;
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1);
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return set_channels (devc, arg);
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return devc->channels;
+ return IOCTL_OUT (arg, devc->channels);
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return set_format (devc, arg);
+ return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return devc->audio_format;
+ return IOCTL_OUT (arg, devc->audio_format);
+
+ default:;
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ cnt = count;
+
+ if (devc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (devc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ ad1848_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+
+ ad_set_MCE (devc, 1);
+
+ ad_write (devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+
+
+ ad_write (devc, 9, 0x0d); /*
+ * Playback enable, single DMA channel mode,
+ * auto calibration on.
+ */
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+
+ devc->xfer_count = cnt;
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static void
+ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ /* int count_reg = (devc->mode == 1) ? 14 : 30; */
+
+ cnt = count;
+ if (devc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (devc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ ad1848_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ ad_set_MCE (devc, 1);
+#if 0
+ ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff));
+ ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff));
+#else
+ ad_write (devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+ if (devc->mode == 2)
+ {
+ ad_write (devc, 31, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 32, (unsigned char) ((cnt >> 8) & 0xff));
+ }
+#endif
+
+ ad_write (devc, 9, 0x0e); /*
+ * Capture enable, single DMA channel mode,
+ * auto calibration on.
+ */
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+
+ devc->xfer_count = cnt;
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static int
+ad1848_prepare_for_IO (int dev, int bsize, int bcount)
+{
+ int timeout;
+ unsigned char fs;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ ad_set_MCE (devc, 1); /* Enables changes to the format select reg */
+ fs = devc->speed_bits | (devc->format_bits << 5);
+
+ if (devc->channels > 1)
+ fs |= 0x10;
+
+ ad_write (devc, 8, fs);
+ /*
+ * Write to I8 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+ while (timeout > 0 && INB (devc->base) == 0x80)
+ timeout--;
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+ RESTORE_INTR (flags);
+
+ /*
+ * If mode == 2 (CS4231), set I28 also. It's the capture format register.
+ */
+ if (devc->mode == 2)
+ {
+ ad_set_MCE (devc, 1);
+ ad_write (devc, 28, fs);
+
+ /*
+ * Write to I28 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+ while (timeout > 0 && INB (devc->base) == 0x80)
+ timeout--;
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+ RESTORE_INTR (flags);
+ }
+ devc->xfer_count = 0;
+ return 0;
+}
+
+static void
+ad1848_reset (int dev)
+{
+ ad1848_halt (dev);
+}
+
+static void
+ad1848_halt (int dev)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ ad_write (devc, 9, 0); /* Clear the PEN and CEN bits (among others) */
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+}
+
+int
+ad1848_detect (int io_base)
+{
+
+#define DDB(x) x
+
+ unsigned char tmp;
+ int i;
+ ad1848_info *devc = &dev_info[nr_ad1848_devs];
+ unsigned char tmp1 = 0xff, tmp2 = 0xff;
+
+ if (nr_ad1848_devs >= MAX_AUDIO_DEV)
+ return 0;
+
+ devc->base = io_base;
+ devc->MCE_bit = 0x40;
+ devc->irq = 0;
+ devc->dma_capture = 0;
+ devc->dma_playback = 0;
+ devc->opened = 0;
+ devc->chip_name = "AD1848";
+ devc->mode = 1; /* MODE1 = original AD1848 */
+
+ /*
+ * Check that the I/O address is in use.
+ *
+ * The bit 0x80 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.
+ */
+
+ if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */
+ {
+ DDB (printk ("ad_detect_A\n"));
+ 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 (devc, 0, 0xaa);
+ ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
+
+ if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45)
+ {
+ DDB (printk ("ad_detect_B (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ ad_write (devc, 0, 0x45);
+ ad_write (devc, 1, 0xaa);
+
+ if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa)
+ {
+ DDB (printk ("ad_detect_C (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ /*
+ * The indirect register I12 has some read only bits. Lets
+ * try to change them.
+ */
+
+ tmp = ad_read (devc, 12);
+ ad_write (devc, 12, (~tmp) & 0x0f);
+
+ if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f))
+ {
+ DDB (printk ("ad_detect_D (%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+ * 0x01=RevB and 0x0A=RevC.
+ */
+
+ /*
+ * The original AD1848/CS4248 has just 15 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 CS4231.
+ */
+
+ ad_write (devc, 12, 0); /* Mode2=disabled */
+
+ for (i = 0; i < 16; i++)
+ if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16)))
+ {
+ DDB (printk ("ad_detect_F(%d/%x/%x)\n", i, tmp1, tmp2));
+ 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.
+ */
+
+ ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */
+
+ tmp1 = ad_read (devc, 12);
+ if (tmp1 & 0x80)
+ devc->chip_name = "CS4248";
+
+ if ((tmp1 & 0xc0) == (0x80 | 0x40))
+ {
+ /*
+ * CS4231 detected - is it?
+ *
+ * Verify that setting I0 doesn't change I16.
+ */
+ ad_write (devc, 16, 0); /* Set I16 to known value */
+
+ ad_write (devc, 0, 0x45);
+ if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */
+ {
+
+ ad_write (devc, 0, 0xaa);
+ if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */
+ {
+ DDB (printk ("ad_detect_H(%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * It's a CS4231 - So what!
+ * (Mode2 will be supported later)
+ */
+ devc->chip_name = "CS4231";
+ devc->mode = 2;
+ }
+ }
+
+ return 1;
+}
+
+void
+ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture)
+{
+ /*
+ * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+ * so that this driver doesn't need to allocate/deallocate it.
+ * The actually used IRQ is ABS(irq).
+ */
+
+ /*
+ * Initial values for the indirect registers of CS4248/AD1848.
+ */
+ static int init_values[] =
+ {
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x08, 0x02, 0x00, 0xca, 0x00, 0x00, 0x00
+ };
+ int i, my_dev;
+ ad1848_info *devc = &dev_info[nr_ad1848_devs];
+
+ if (!ad1848_detect (io_base))
+ return;
+
+ devc->irq = (irq > 0) ? irq : 0;
+ devc->dma_capture = dma_playback;
+ devc->dma_playback = dma_capture;
+ devc->opened = 0;
+
+ if (nr_ad1848_devs != 0)
+ {
+ memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs],
+ (char *) &ad1848_pcm_operations[0],
+ sizeof (struct audio_operations));
+ }
+
+ for (i = 0; i < 16; i++)
+ ad_write (devc, i, init_values[i]);
+
+ OUTB (0, io_Status (devc)); /* Clear pending interrupts */
+
+#ifndef SCO
+ sprintf (ad1848_pcm_operations[nr_ad1848_devs].name,
+ "%s (%s)", name, devc->chip_name);
+#endif
+
+ if (irq > 0)
+ printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name);
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs];
+ if (irq > 0)
+ irq2dev[irq] = my_dev;
+ else if (irq < 0)
+ irq2dev[-irq] = my_dev;
+
+ audio_devs[my_dev]->dmachan = dma_playback;
+ audio_devs[my_dev]->buffcount = 1;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE * 2;
+ audio_devs[my_dev]->devc = devc;
+ audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode];
+ nr_ad1848_devs++;
+ }
+ else
+ printk ("AD1848: Too many PCM devices available\n");
+}
+
+void
+ad1848_interrupt (int irq)
+{
+ unsigned char status;
+ ad1848_info *devc;
+ int dev;
+
+ if (irq < 0 || irq > 15)
+ return; /* Bogus irq */
+ dev = irq2dev[irq];
+ if (dev < 0 || dev >= num_audiodevs)
+ return; /* Bogus dev */
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+ status = INB (io_Status (devc));
+
+ if (status & 0x01)
+ {
+ if (devc->opened && devc->irq_mode == IMODE_OUTPUT)
+ {
+ DMAbuf_outputintr (dev, 1);
+ }
+
+ if (devc->opened && devc->irq_mode == IMODE_INPUT)
+ DMAbuf_inputintr (dev);
+ }
+
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+}
+
+#endif
+/*
+ * Some extra code for the MS Sound System
+ */
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MSS)
+
+int
+probe_ms_sound (struct address_info *hw_config)
+{
+ if ((INB (hw_config->io_base + 3) & 0x04) == 0)
+ return 0; /* WSS ID test failed */
+
+ if (hw_config->irq > 11)
+ return 0;
+
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ return 0;
+
+ return ad1848_detect (hw_config->io_base + 4);
+}
+
+long
+attach_ms_sound (long mem_start, struct address_info *hw_config)
+{
+ static unsigned char interrupt_bits[12] =
+ {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
+ char bits;
+
+ static unsigned char dma_bits[4] = {1, 2, 0, 3};
+
+ int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
+
+ if (!ad1848_detect (hw_config->io_base + 4))
+ return mem_start;
+
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ return mem_start;
+
+ OUTB (bits | 0x40, config_port); /* Verify IRQ (I guess) */
+ if ((INB (version_port) & 0x40) == 0)
+ printk ("[IRQ?]");
+
+ OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
+
+ ad1848_init ("MS Sound System", hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c
new file mode 100644
index 000000000000..6365069384a5
--- /dev/null
+++ b/sys/i386/isa/sound/adlib_card.c
@@ -0,0 +1,51 @@
+/*
+ * sound/adlib_card.c
+ *
+ * Detection routine for the AdLib card.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+long
+attach_adlib_card (long mem_start, struct address_info *hw_config)
+{
+
+ if (opl3_detect (FM_MONO))
+ {
+ mem_start = opl3_init (mem_start);
+ }
+ return mem_start;
+}
+
+int
+probe_adlib (struct address_info *hw_config)
+{
+ return opl3_detect (FM_MONO);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c
new file mode 100644
index 000000000000..d5a4acce44e3
--- /dev/null
+++ b/sys/i386/isa/sound/audio.c
@@ -0,0 +1,424 @@
+/*
+ * sound/audio.c
+ *
+ * Device file manager for /dev/audio
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+#ifndef EXCLUDE_AUDIO
+
+#include "ulaw.h"
+
+#define ON 1
+#define OFF 0
+
+static int wr_buff_no[MAX_AUDIO_DEV]; /*
+ * != -1, if there is
+ * a incomplete output
+ * block in the queue.
+ */
+static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV];
+
+static int audio_mode[MAX_AUDIO_DEV];
+
+#define AM_NONE 0
+#define AM_WRITE 1
+#define AM_READ 2
+
+static char *wr_dma_buf[MAX_AUDIO_DEV];
+static int audio_format[MAX_AUDIO_DEV];
+static int local_conversion[MAX_AUDIO_DEV];
+
+static int
+set_format (int dev, int fmt)
+{
+ if (fmt != AFMT_QUERY)
+ {
+
+ local_conversion[dev] = 0;
+
+ if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
+ if (fmt == AFMT_MU_LAW)
+ {
+ fmt = AFMT_U8;
+ local_conversion[dev] = AFMT_MU_LAW;
+ }
+ else
+ fmt = AFMT_U8; /* This is always supported */
+
+ audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, fmt, 1);
+ }
+
+ if (local_conversion[dev]) /* This shadows the HW format */
+ return local_conversion[dev];
+
+ return audio_format[dev];
+}
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ int ret;
+ int bits;
+ int dev_type = dev & 0x0f;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ if (dev_type == SND_DEV_DSP16)
+ bits = 16;
+ else
+ bits = 8;
+
+ if ((ret = DMAbuf_open (dev, mode)) < 0)
+ return ret;
+
+ local_conversion[dev] = 0;
+
+ if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, bits, 1) != bits)
+ {
+ audio_release (dev, file);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev_type == SND_DEV_AUDIO)
+ {
+ set_format (dev, AFMT_MU_LAW);
+ }
+ else
+ set_format (dev, bits);
+
+ wr_buff_no[dev] = -1;
+ audio_mode[dev] = AM_NONE;
+
+ return ret;
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+
+ DMAbuf_release (dev, mode);
+}
+
+#ifdef NO_INLINE_ASM
+static void
+translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n)
+{
+ unsigned long i;
+
+ for (i = 0; i < n; ++i)
+ buff[i] = table[buff[i]];
+}
+
+#else
+extern inline void
+translate_bytes (const void *table, void *buff, unsigned long n)
+{
+ __asm__ ("cld\n"
+ "1:\tlodsb\n\t"
+ "xlatb\n\t"
+ "stosb\n\t"
+ "loop 1b\n\t":
+ :"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
+ :"bx", "cx", "di", "si", "ax");
+}
+
+#endif
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p, l;
+ int err;
+
+ dev = dev >> 4;
+
+ p = 0;
+ c = count;
+
+ if (audio_mode[dev] == AM_READ) /*
+ * Direction changed
+ */
+ {
+ wr_buff_no[dev] = -1;
+ }
+
+ audio_mode[dev] = AM_WRITE;
+
+ if (!count) /*
+ * Flush output
+ */
+ {
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return 0;
+ }
+
+ while (c)
+ { /*
+ * Perform output blocking
+ */
+ if (wr_buff_no[dev] < 0) /*
+ * There is no incomplete buffers
+ */
+ {
+ if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0)
+ {
+ return wr_buff_no[dev];
+ }
+ wr_buff_ptr[dev] = 0;
+ }
+
+ l = c;
+ if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
+ l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
+
+ if (!audio_devs[dev]->copy_from_user)
+ { /*
+ * No device specific copy routine
+ */
+ COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
+ }
+ else
+ audio_devs[dev]->copy_from_user (dev,
+ wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l);
+
+
+ /*
+ * Insert local processing here
+ */
+
+ if (local_conversion[dev] == AFMT_MU_LAW)
+ {
+#ifdef linux
+ /*
+ * This just allows interrupts while the conversion is running
+ */
+ __asm__ ("sti");
+#endif
+ translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
+ }
+
+ c -= l;
+ p += l;
+ wr_buff_ptr[dev] += l;
+
+ if (wr_buff_ptr[dev] >= wr_buff_size[dev])
+ {
+ if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
+ {
+ return err;
+ }
+
+ wr_buff_no[dev] = -1;
+ }
+
+ }
+
+ return count;
+}
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p, l;
+ char *dmabuf;
+ int buff_no;
+
+ dev = dev >> 4;
+ p = 0;
+ c = count;
+
+ if (audio_mode[dev] == AM_WRITE)
+ {
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ }
+
+ audio_mode[dev] = AM_READ;
+
+ while (c)
+ {
+ if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
+ return buff_no;
+
+ if (l > c)
+ l = c;
+
+ /*
+ * Insert any local processing here.
+ */
+
+ if (local_conversion[dev] == AFMT_MU_LAW)
+ {
+#ifdef linux
+ /*
+ * This just allows interrupts while the conversion is running
+ */
+ __asm__ ("sti");
+#endif
+
+ translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
+ }
+
+ COPY_TO_USER (buf, p, dmabuf, l);
+
+ DMAbuf_rmchars (dev, buff_no, l);
+
+ p += l;
+ c -= l;
+ }
+
+ return count - c;
+}
+
+int
+audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_SYNC:
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+
+ case SNDCTL_DSP_POST:
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return 0;
+ break;
+
+ case SNDCTL_DSP_RESET:
+ wr_buff_no[dev] = -1;
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+
+ case SNDCTL_DSP_GETFMTS:
+ return IOCTL_OUT (arg, audio_devs[dev]->format_mask);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ return IOCTL_OUT (arg, set_format (dev, IOCTL_IN (arg)));
+
+ default:
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+ }
+}
+
+long
+audio_init (long mem_start)
+{
+ /*
+ * NOTE! This routine could be called several times during boot.
+ */
+ return mem_start;
+}
+
+#else
+/*
+ * Stub versions
+ */
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+};
+int
+audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+audio_init (long mem_start)
+{
+ return mem_start;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/configure.c b/sys/i386/isa/sound/configure.c
new file mode 100644
index 000000000000..5b7217bccb46
--- /dev/null
+++ b/sys/i386/isa/sound/configure.c
@@ -0,0 +1,830 @@
+/*
+ * sound/configure.c - Configuration program for the Linux Sound Driver
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+
+#define B(x) (1 << (x))
+
+/*
+ * Option numbers
+ */
+
+#define OPT_PAS 0
+#define OPT_SB 1
+#define OPT_ADLIB 2
+#define OPT_LAST_MUTUAL 2
+
+#define OPT_GUS 3
+#define OPT_MPU401 4
+#define OPT_UART6850 5
+#define OPT_PSS 6
+#define OPT_GUS16 7
+#define OPT_GUSMAX 8
+#define OPT_MSS 9
+
+#define OPT_HIGHLEVEL 10 /* This must be same than the next one */
+#define OPT_SBPRO 10
+#define OPT_SB16 11
+#define OPT_AUDIO 12
+#define OPT_MIDI_AUTO 13
+#define OPT_MIDI 14
+#define OPT_YM3812_AUTO 15
+#define OPT_YM3812 16
+#define OPT_SEQUENCER 17
+#define OPT_LAST 17 /* Must be the same than the defined OPT */
+
+#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)| \
+ B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)|B(OPT_MSS))
+/*
+ * Options that have been disabled for some reason (incompletely implemented
+ * and/or tested). Don't remove from this list before looking at file
+ * experimental.txt for further info.
+ */
+#define DISABLED_OPTIONS (B(OPT_PSS))
+
+typedef struct
+ {
+ unsigned long conditions;
+ unsigned long exclusive_options;
+ char macro[20];
+ int verify;
+ int alias;
+ int default_answ;
+ }
+
+hw_entry;
+
+
+/*
+ * The rule table for the driver options. The first field defines a set of
+ * options which must be selected before this entry can be selected. The
+ * second field is a set of options which are not allowed with this one. If
+ * the fourth field is zero, the option is selected without asking
+ * confirmation from the user.
+ *
+ * With this version of the rule table it is possible to select just one type of
+ * hardware.
+ *
+ * NOTE! Keep the following table and the questions array in sync with the
+ * option numbering!
+ */
+
+hw_entry hw_table[] =
+{
+/*
+ * 0
+ */
+ {0, 0, "PAS", 1, 0, 0},
+ {0, 0, "SB", 1, 0, 0},
+ {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0},
+
+ {0, 0, "GUS", 1, 0, 0},
+ {0, 0, "MPU401", 1, 0, 0},
+ {0, 0, "UART6850", 1, 0, 0},
+ {0, 0, "PSS", 1, 0, 0},
+ {B (OPT_GUS), 0, "GUS16", 1, 0, 0},
+ {B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0},
+ {0, 0, "MSS", 1, 0, 0},
+
+ {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1},
+ {B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1},
+{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0, 1},
+ {B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI, 0},
+ {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0, 1},
+ {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
+ {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1},
+ {B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0, 1}
+};
+
+char *questions[] =
+{
+ "ProAudioSpectrum 16 support",
+ "SoundBlaster support",
+ "AdLib support",
+ "Gravis Ultrasound support",
+ "MPU-401 support (NOT for SB16)",
+ "6850 UART Midi support",
+ "PSS (ECHO-ADI2111) support",
+ "16 bit sampling option of GUS (_NOT_ GUS MAX)",
+ "GUS MAX support",
+ "Microsoft Sound System support",
+
+ "SoundBlaster Pro support",
+ "SoundBlaster 16 support",
+ "digitized voice support",
+ "This should not be asked",
+ "MIDI interface support",
+ "This should not be asked",
+ "FM synthesizer (YM3812/OPL-3) support",
+ "/dev/sequencer support",
+ "Should I die"
+};
+
+unsigned long selected_options = 0;
+int sb_dma = 0;
+
+int
+can_select_option (int nr)
+{
+ switch (nr)
+ {
+ case 0:
+ fprintf (stderr, "The SoundBlaster, AdLib and ProAudioSpectrum\n"
+ "CARDS cannot be installed at the same time.\n\n"
+ "However the PAS16 has a SB emulator so you could select"
+ "the SoundBlaster DRIVER with it.\n");
+ fprintf (stderr, " - ProAudioSpectrum 16\n");
+ fprintf (stderr, " - SoundBlaster / SB Pro\n");
+ fprintf (stderr, " (Could be selected with a PAS16 also)\n");
+ fprintf (stderr, " - AdLib\n");
+ fprintf (stderr, "\nDon't enable SoundBlaster if you have GUS at 0x220!\n\n");
+ break;
+
+ case OPT_LAST_MUTUAL + 1:
+ fprintf (stderr, "\nThe following cards should work with any other cards.\n"
+ "CAUTION! Don't enable MPU-401 if you don't have it.\n");
+ break;
+
+ case OPT_HIGHLEVEL:
+ fprintf (stderr, "\nSelect one or more of the following options\n");
+ break;
+
+
+ }
+
+ if (hw_table[nr].conditions)
+ if (!(hw_table[nr].conditions & selected_options))
+ return 0;
+
+ if (hw_table[nr].exclusive_options)
+ if (hw_table[nr].exclusive_options & selected_options)
+ return 0;
+
+ if (DISABLED_OPTIONS & B (nr))
+ return 0;
+
+ return 1;
+}
+
+int
+think_positively (int def_answ)
+{
+ char answ[512];
+ int len;
+
+ if ((len = read (0, &answ, sizeof (answ))) < 1)
+ {
+ fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
+
+ perror ("stdin");
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (-1);
+ }
+
+ if (len < 2) /*
+ * There is an additional LF at the end
+ */
+ return def_answ;
+
+ answ[len - 1] = 0;
+
+ if (!strcmp (answ, "y") || !strcmp (answ, "Y"))
+ return 1;
+
+ return 0;
+}
+
+int
+ask_value (char *format, int default_answer)
+{
+ char answ[512];
+ int len, num;
+
+play_it_again_Sam:
+
+ if ((len = read (0, &answ, sizeof (answ))) < 1)
+ {
+ fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
+
+ perror ("stdin");
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (-1);
+ }
+
+ if (len < 2) /*
+ * There is an additional LF at the end
+ */
+ return default_answer;
+
+ answ[len - 1] = 0;
+
+ if (sscanf (answ, format, &num) != 1)
+ {
+ fprintf (stderr, "Illegal format. Try again: ");
+ goto play_it_again_Sam;
+ }
+
+ return num;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, num, def_size, full_driver = 1;
+ char answ[10];
+
+ printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n");
+
+ fprintf (stderr, "\nConfiguring the sound support\n\n");
+
+ fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? ");
+
+ if (think_positively (0))
+ {
+ /*
+ * Select all but some most dangerous cards. These cards are difficult to
+ * detect reliably or conflict with some other cards (SCSI, Mitsumi)
+ */
+ selected_options = 0xffffffff &
+ ~(B (OPT_MPU401) | B (OPT_UART6850) | B (OPT_PSS)) &
+ ~DISABLED_OPTIONS;
+
+ fprintf (stderr, "Note! MPU-401, PSS and 6850 UART drivers not enabled\n");
+ full_driver = 1;
+ }
+ else
+ {
+ fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?");
+ if (think_positively (0))
+ {
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (0);
+ }
+ /*
+ * Partial driver
+ */
+
+ full_driver = 0;
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (can_select_option (i))
+ {
+ if (!(selected_options & B (i))) /*
+ * Not selected yet
+ */
+ if (!hw_table[i].verify)
+ {
+ if (hw_table[i].alias)
+ selected_options |= B (hw_table[i].alias);
+ else
+ selected_options |= B (i);
+ }
+ else
+ {
+ int def_answ = hw_table[i].default_answ;
+
+ fprintf (stderr,
+ def_answ ? " %s (y/n) ? " : " %s (n/y) ? ",
+ questions[i]);
+ if (think_positively (def_answ))
+ if (hw_table[i].alias)
+ selected_options |= B (hw_table[i].alias);
+ else
+ selected_options |= B (i);
+ }
+ }
+ }
+
+ if (selected_options & B (OPT_SBPRO))
+ {
+ fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
+ if (think_positively (0))
+ printf("#define __SGNXPRO__\n");
+ }
+
+ if (selected_options & B (OPT_SB16))
+ selected_options |= B (OPT_SBPRO);
+
+ if (selected_options & B (OPT_PSS))
+ {
+ genld_again:
+ fprintf
+ (stderr,
+ "if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
+ "then you must include the LD in the kernel.\n"
+ "(do you wish to include a LD) ? ");
+ if (think_positively (0))
+ {
+ char path[512];
+
+ fprintf (stderr,
+ "Enter the path to your LD file (pwd is sound): ");
+ scanf ("%s", path);
+ fprintf (stderr, "including LD file %s\n", path);
+ selected_options |= B (OPT_SB) | B (OPT_MPU401) | B (OPT_ADLIB);
+
+ /* Gen LD header */
+ {
+ int fd;
+ int count;
+ char c;
+ int i = 0;
+
+ if ((fd = open (path, 0)) > 0)
+ {
+ FILE *sf = fopen ("synth-ld.h", "w");
+
+ fprintf (sf, "/* automaticaly generated by configure */\n");
+ fprintf (sf, "unsigned char pss_synth[] = {\n");
+ while (1)
+ {
+ count = read (fd, &c, 1);
+ if (count == 0)
+ break;
+ if (i != 0 && (i % 10) == 0)
+ fprintf (sf, "\n");
+ fprintf (sf, "0x%02x,", c & 0xFFL);
+ i++;
+ }
+ fprintf (sf, "};\n"
+ "#define pss_synthLen %d\n", i);
+ fclose (sf);
+ close (fd);
+ }
+ else
+ {
+ fprintf (stderr, "couldn't open %s as the ld file\n",
+ path);
+ fprintf (stderr, "try again with correct path? ");
+ if (think_positively (1))
+ goto genld_again;
+ }
+ }
+ }
+ else
+ {
+ FILE *sf = fopen ("synth-ld.h", "w");
+
+ fprintf (sf, "/* automaticaly generated by configure */\n");
+ fprintf (sf, "unsigned char pss_synth[1];\n"
+ "#define pss_synthLen 0\n");
+ fclose (sf);
+ }
+ }
+
+ if (!(selected_options & ANY_DEVS))
+ {
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n\n");
+ exit (0);
+ }
+ else
+ printf ("#define KERNEL_SOUNDCARD\n");
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (!hw_table[i].alias)
+ if (selected_options & B (i))
+ printf ("#undef EXCLUDE_%s\n", hw_table[i].macro);
+ else
+ printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
+
+
+ /*
+ * IRQ and DMA settings
+ */
+ printf ("\n");
+
+#if defined(linux)
+ if ((selected_options & B (OPT_SB)) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
+ {
+ fprintf (stderr, "\nI/O base for SB?\n"
+ "The factory default is 220\n"
+ "Enter the SB I/O base: ");
+
+ num = ask_value ("%x", 0x220);
+ fprintf (stderr, "SB I/O base set to %03x\n", num);
+ printf ("#define SBC_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for SoundBlaster?\n"
+ "The IRQ address is defined by the jumpers on your card.\n"
+ "The factory default is either 5 or 7 (depending on the model).\n"
+ "Valid values are 9(=2), 5, 7 and 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 7);
+ if (num != 9 && num != 5 && num != 7 && num != 10)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "SoundBlaster IRQ set to %d\n", num);
+
+ printf ("#define SBC_IRQ %d\n", num);
+
+ if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_PSS)))
+ {
+ fprintf (stderr, "\nDMA channel for SoundBlaster?\n"
+ "For SB 1.0, 1.5 and 2.0 this MUST be 1\n"
+ "SB Pro supports DMA channels 0, 1 and 3 (jumper)\n"
+ "For SB16 give the 8 bit DMA# here\n"
+ "The default value is 1\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 1);
+ if (num < 0 || num > 3)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 1;
+ }
+ fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
+ printf ("#define SBC_DMA %d\n", num);
+ sb_dma = num;
+ }
+
+ if (selected_options & B (OPT_SB16))
+ {
+
+ fprintf (stderr, "\n16 bit DMA channel for SoundBlaster 16?\n"
+ "Possible values are 5, 6 or 7\n"
+ "The default value is 6\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 6);
+ if ((num < 5 || num > 7) && (num != sb_dma))
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 6;
+ }
+ fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
+ printf ("#define SB16_DMA %d\n", num);
+
+ fprintf (stderr, "\nI/O base for SB16 Midi?\n"
+ "Possible values are 300 and 330\n"
+ "The factory default is 330\n"
+ "Enter the SB16 Midi I/O base: ");
+
+ num = ask_value ("%x", 0x330);
+ fprintf (stderr, "SB16 Midi I/O base set to %03x\n", num);
+ printf ("#define SB16MIDI_BASE 0x%03x\n", num);
+ }
+ }
+
+ if (selected_options & B (OPT_PAS))
+ {
+ if (selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
+ {
+ fprintf (stderr, "\nIRQ number for ProAudioSpectrum?\n"
+ "The recommended value is the IRQ used under DOS.\n"
+ "Please refer to the ProAudioSpectrum User's Guide.\n"
+ "The default value is 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 10);
+ if (num == 6 || num < 3 || num > 15 || num == 2) /*
+ * Illegal
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 10;
+ }
+ fprintf (stderr, "ProAudioSpectrum IRQ set to %d\n", num);
+ printf ("#define PAS_IRQ %d\n", num);
+ }
+
+ if (selected_options & B (OPT_AUDIO))
+ {
+ fprintf (stderr, "\nDMA number for ProAudioSpectrum?\n"
+ "The recommended value is the DMA channel under DOS.\n"
+ "Please refer to the ProAudioSpectrum User's Guide.\n"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num == 4 || num < 0 || num > 7)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nProAudioSpectrum DMA set to %d\n", num);
+ printf ("#define PAS_DMA %d\n", num);
+ }
+ }
+
+ if (selected_options & B (OPT_GUS))
+ {
+ fprintf (stderr, "\nI/O base for Gravis Ultrasound?\n"
+ "Valid choices are 210, 220, 230, 240, 250 or 260\n"
+ "The factory default is 220\n"
+ "Enter the GUS I/O base: ");
+
+ num = ask_value ("%x", 0x220);
+ if ((num > 0x260) || ((num & 0xf0f) != 0x200) || ((num & 0x0f0) > 0x060))
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 0x220;
+ }
+
+ if ((selected_options & B (OPT_SB)) && (num == 0x220))
+ {
+ fprintf (stderr, "FATAL ERROR!!!!!!!!!!!!!!\n"
+ "\t0x220 cannot be used if SoundBlaster is enabled.\n"
+ "\tRun the config again.\n");
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (-1);
+ }
+ fprintf (stderr, "GUS I/O base set to %03x\n", num);
+ printf ("#define GUS_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for Gravis UltraSound?\n"
+ "The recommended value is the IRQ used under DOS.\n"
+ "Please refer to the Gravis Ultrasound User's Guide.\n"
+ "The default value is 15.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 15);
+ if (num == 6 || num < 3 || num > 15 || num == 2) /*
+ * Invalid
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 15;
+ }
+ fprintf (stderr, "Gravis UltraSound IRQ set to %d\n", num);
+ printf ("#define GUS_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for Gravis UltraSound?\n"
+ "The recommended value is the DMA channel under DOS.\n"
+ "Please refer to the Gravis Ultrasound User's Guide.\n"
+ "The default value is 6\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 6);
+ if (num == 4 || num < 0 || num > 7)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 6;
+ }
+ fprintf (stderr, "\nGravis UltraSound DMA set to %d\n", num);
+ printf ("#define GUS_DMA %d\n", num);
+ }
+
+ if (selected_options & B (OPT_MPU401))
+ {
+ fprintf (stderr, "\nI/O base for MPU-401?\n"
+ "The factory default is 330\n"
+ "Enter the MPU-401 I/O base: ");
+
+ num = ask_value ("%x", 0x330);
+ fprintf (stderr, "MPU-401 I/O base set to %03x\n", num);
+ printf ("#define MPU_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for MPU-401?\n"
+ "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
+ "The default value is 9.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 9);
+ if (num == 6 || num < 3 || num > 15) /*
+ * Used for floppy
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 5;
+ }
+ fprintf (stderr, "MPU-401 IRQ set to %d\n", num);
+ printf ("#define MPU_IRQ %d\n", num);
+ }
+
+ if (selected_options & B (OPT_UART6850))
+ {
+ fprintf (stderr, "\nI/O base for 6850 UART Midi?\n"
+ "Be carefull. No defaults.\n"
+ "Enter the 6850 UART I/O base: ");
+
+ num = ask_value ("%x", 0);
+ if (num == 0)
+ {
+ /*
+ * Invalid value entered
+ */
+ printf ("#define EXCLUDE_UART6850\n");
+ }
+ else
+ {
+ fprintf (stderr, "6850 UART I/O base set to %03x\n", num);
+ printf ("#define U6850_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for 6850 UART?\n"
+ "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
+ "The default value is 5.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 5);
+ if (num == 6 || num < 3 || num > 15) /*
+ * Used for floppy
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 5;
+ }
+ fprintf (stderr, "6850 UART IRQ set to %d\n", num);
+ printf ("#define U6850_IRQ %d\n", num);
+ }
+ }
+
+ if (selected_options & B (OPT_PSS))
+ {
+ fprintf (stderr, "\nI/O base for PSS?\n"
+ "The factory default is 220\n"
+ "Enter the PSS I/O base: ");
+
+ num = ask_value ("%x", 0x220);
+ fprintf (stderr, "PSS I/O base set to %03x\n", num);
+ printf ("#define PSS_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for PSS?\n"
+ "Valid numbers are: 3, 4, 5, 7, 9(=2) or 10.\n"
+ "The default value is 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 10);
+ if (num == 6 || num < 3 || num > 15) /* Used for floppy */
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "PSS IRQ set to %d\n", num);
+ printf ("#define PSS_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for ECHO-PSS?\n"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num == 4 || num < 0 || num > 7)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nECHO-PSS DMA set to %d\n", num);
+ printf ("#define PSS_DMA %d\n", num);
+ }
+
+ if (selected_options & B (OPT_MSS))
+ {
+ fprintf (stderr, "\nI/O base for MSS (MS Sound System)?\n"
+ "The factory default is 530\n"
+ "Other possible values are 604, E80 or F40\n"
+ "Enter the MSS I/O base: ");
+
+ num = ask_value ("%x", 0x530);
+ fprintf (stderr, "MSS I/O base set to %03x\n", num);
+ printf ("#define MSS_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for MSS?\n"
+ "Valid numbers are: 7, 9(=2), 10 and 11.\n"
+ "The default value is 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 10);
+ if (num == 6 || num < 3 || num > 15) /* Used for floppy */
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "MSS IRQ set to %d\n", num);
+ printf ("#define MSS_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for MSS?\n"
+ "Valid values are 1 and 3 (sometimes 0)"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num == 4 || num < 0 || num > 7)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nMSS DMA set to %d\n", num);
+ printf ("#define MSS_DMA %d\n", num);
+ }
+
+ if (selected_options & B (OPT_GUS16))
+ {
+ fprintf (stderr, "\nI/O base for GUS16 (GUS 16 bit sampling option)?\n"
+ "The factory default is 530\n"
+ "Other possible values are 604, E80 or F40\n"
+ "Enter the GUS16 I/O base: ");
+
+ num = ask_value ("%x", 0x530);
+ fprintf (stderr, "GUS16 I/O base set to %03x\n", num);
+ printf ("#define GUS16_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for GUS16?\n"
+ "Valid numbers are: 3, 4, 5, 7, or 9(=2).\n"
+ "The default value is 7.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 7);
+ if (num == 6 || num < 3 || num > 15) /* Used for floppy */
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "GUS16 IRQ set to %d\n", num);
+ printf ("#define GUS16_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for GUS16?\n"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num < 0 || num > 3)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nGUS16 DMA set to %d\n", num);
+ printf ("#define GUS16_DMA %d\n", num);
+ }
+#endif
+
+ if (selected_options & B (OPT_AUDIO))
+ {
+ def_size = 16384;
+
+ if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_SB16)))
+ def_size = 32768;
+
+#ifndef __386BSD__
+ if ((selected_options & (B (OPT_PAS) | B (OPT_PAS) | B (OPT_GUS16) | B (OPT_GUSMAX) |
+ B (OPT_MSS) | B (OPT_PSS))) &&
+ !full_driver)
+ def_size = 65536; /*
+ * PAS16 or SB16
+ */
+#endif
+
+ fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n"
+ "%d is recommended value for this configuration.\n"
+ "Enter the value: ", def_size);
+
+ num = ask_value ("%d", def_size);
+ if (num != 4096 && num != 16384 && num != 32768 && num != 65536)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = def_size;
+ }
+ fprintf (stderr, "The DMA buffer size set to %d\n", num);
+ printf ("#define DSP_BUFFSIZE %d\n", num);
+ }
+
+ printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
+ fprintf (stderr, "The sound driver is now configured.\n");
+
+#if defined(SCO) || defined(ISC) || defined(SYSV)
+ fprintf (stderr, "Remember to update the System file\n");
+#endif
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c
new file mode 100644
index 000000000000..1eb57bbb08c1
--- /dev/null
+++ b/sys/i386/isa/sound/dev_table.c
@@ -0,0 +1,269 @@
+/*
+ * sound/dev_table.c
+ *
+ * Device call tables.
+ *
+ * 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.
+ *
+ */
+
+#define _DEV_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+int
+snd_find_driver (int type)
+{
+ int i, n = sizeof (sound_drivers) / sizeof (struct driver_info);
+
+ for (i = 0; i < (n - 1); i++)
+ if (sound_drivers[i].card_type == type)
+ return i;
+
+ return -1; /*
+ * Not found
+ */
+}
+
+long
+sndtable_init (long mem_start)
+{
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+ int drv;
+
+ for (i = 0; i < (n - 1); i++)
+ if (snd_installed_cards[i].enabled)
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ else if (sound_drivers[drv].probe (&snd_installed_cards[i].config))
+ {
+#ifndef SHORT_BANNERS
+ printk ("snd%d",
+ snd_installed_cards[i].card_type);
+#endif
+
+ mem_start = sound_drivers[drv].attach (mem_start, &snd_installed_cards[i].config);
+#ifndef SHORT_BANNERS
+ printk (" at 0x%x irq %d drq %d\n",
+ snd_installed_cards[i].config.io_base,
+ snd_installed_cards[i].config.irq,
+ snd_installed_cards[i].config.dma);
+#endif
+ }
+ else
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ return mem_start;
+}
+
+int
+sndtable_probe (int unit, struct address_info *hw_config)
+ {
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ if (!unit)
+ return TRUE;
+
+ for (i = 0; i < (n - 1); i++)
+ if (snd_installed_cards[i].enabled)
+ if (snd_installed_cards[i].card_type == unit)
+ {
+ int drv;
+
+ snd_installed_cards[i].config.io_base = hw_config->io_base;
+ snd_installed_cards[i].config.irq = hw_config->irq;
+ snd_installed_cards[i].config.dma = hw_config->dma;
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not
+ * detected
+ */
+ else if (sound_drivers[drv].probe (hw_config))
+ return 1;
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ return 0;
+ }
+
+ return FALSE;
+ }
+
+int
+sndtable_init_card (int unit, struct address_info *hw_config)
+ {
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ if (!unit)
+ {
+ if (sndtable_init (0) != 0)
+ panic ("snd: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ for (i = 0; i < (n - 1); i++)
+ if (snd_installed_cards[i].card_type == unit)
+ {
+ int drv;
+
+ snd_installed_cards[i].config.io_base = hw_config->io_base;
+ snd_installed_cards[i].config.irq = hw_config->irq;
+ snd_installed_cards[i].config.dma = hw_config->dma;
+
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ else if (sound_drivers[drv].attach (0, hw_config) != 0)
+ panic ("snd#: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+int
+sndtable_get_cardcount (void)
+{
+ return num_audiodevs + num_mixers + num_synths + num_midis;
+}
+
+#ifdef linux
+void
+sound_setup (char *str, int *ints)
+{
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ /*
+ * First disable all drivers
+ */
+
+ for (i = 0; i < n; i++)
+ snd_installed_cards[i].enabled = 0;
+
+ if (ints[0] == 0 || ints[1] == 0)
+ return;
+ /*
+ * Then enable them one by time
+ */
+
+ for (i = 1; i <= ints[0]; i++)
+ {
+ int card_type, ioaddr, irq, dma, ptr, j;
+ unsigned int val;
+
+ val = (unsigned int) ints[i];
+
+ card_type = (val & 0x0ff00000) >> 20;
+
+ if (card_type > 127)
+ {
+ /*
+ * Add any future extensions here
+ */
+ return;
+ }
+
+ ioaddr = (val & 0x000fff00) >> 8;
+ irq = (val & 0x000000f0) >> 4;
+ dma = (val & 0x0000000f);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (snd_installed_cards[j].card_type == card_type &&
+ !snd_installed_cards[j].enabled) /*
+ * Not already found
+ */
+ ptr = j;
+
+ if (ptr == -1)
+ printk ("Sound: Invalid setup parameter 0x%08x\n", val);
+ else
+ {
+ snd_installed_cards[ptr].enabled = 1;
+ snd_installed_cards[ptr].config.io_base = ioaddr;
+ snd_installed_cards[ptr].config.irq = irq;
+ snd_installed_cards[ptr].config.dma = dma;
+ }
+ }
+}
+
+#else
+void
+sound_chconf (int card_type, int ioaddr, int irq, int dma)
+{
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ int ptr, j;
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (snd_installed_cards[j].card_type == card_type &&
+ !snd_installed_cards[j].enabled) /*
+ * Not already found
+ */
+ ptr = j;
+
+ if (ptr != -1)
+ {
+ snd_installed_cards[ptr].enabled = 1;
+ if (ioaddr)
+ snd_installed_cards[ptr].config.io_base = ioaddr;
+ if (irq)
+ snd_installed_cards[ptr].config.irq = irq;
+ if (dma)
+ snd_installed_cards[ptr].config.dma = dma;
+ }
+}
+
+#endif
+
+struct address_info *
+sound_getconf (int card_type)
+{
+ int j, ptr;
+ int n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (snd_installed_cards[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr == -1)
+ return (struct address_info *) NULL;
+
+ return &snd_installed_cards[ptr].config;
+}
+
+#else
+
+void
+sound_setup (char *str, int *ints)
+{
+}
+
+#endif
diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h
new file mode 100644
index 000000000000..12b20baf7d82
--- /dev/null
+++ b/sys/i386/isa/sound/dev_table.h
@@ -0,0 +1,345 @@
+/*
+ * dev_table.h
+ *
+ * Global definitions for device call tables
+ *
+ * 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.
+ *
+
+*/
+
+#ifndef _DEV_TABLE_H_
+#define _DEV_TABLE_H_
+
+/*
+ * NOTE! NOTE! NOTE! NOTE!
+ *
+ * If you modify this file, please check the dev_table.c also.
+ *
+ * NOTE! NOTE! NOTE! NOTE!
+ */
+
+struct driver_info {
+ int card_type; /* From soundcard.h */
+ char *name;
+ long (*attach) (long mem_start, struct address_info *hw_config);
+ int (*probe) (struct address_info *hw_config);
+};
+
+struct card_info {
+ int card_type; /* Link (search key) to the driver list */
+ struct address_info config;
+ int enabled;
+};
+
+/*
+ * Device specific parameters (used only by dmabuf.c)
+ */
+#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
+
+#define DMODE_NONE 0
+#define DMODE_OUTPUT 1
+#define DMODE_INPUT 2
+
+struct dma_buffparms {
+ int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+
+ /*
+ * Pointers to raw buffers
+ */
+
+ char *raw_buf[DSP_BUFFCOUNT];
+ unsigned long raw_buf_phys[DSP_BUFFCOUNT];
+ int raw_count;
+
+ /*
+ * Device state tables
+ */
+
+ unsigned long flags;
+#define DMA_BUSY 0x00000001
+#define DMA_RESTART 0x00000002
+#define DMA_ACTIVE 0x00000004
+#define DMA_STARTED 0x00000008
+#define DMA_ALLOC_DONE 0x00000020
+
+ int open_mode;
+
+ /*
+ * Queue parameters.
+ */
+ int qlen;
+ int qhead;
+ int qtail;
+
+ int nbufs;
+ int counts[MAX_SUB_BUFFERS];
+ int subdivision;
+ char *buf[MAX_SUB_BUFFERS];
+ unsigned long buf_phys[MAX_SUB_BUFFERS];
+
+ int fragment_size;
+ int max_fragments;
+
+ int bytes_in_use;
+
+ int underrun_count;
+};
+
+struct audio_operations {
+ char name[32];
+ int flags;
+#define NOTHING_SPECIAL 0
+#define NEEDS_RESTART 1
+#define DMA_AUTOMODE 2
+ int format_mask; /* Bitmask for supported audio formats */
+ void *devc; /* Driver specific info */
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ void (*output_block) (int dev, unsigned long buf,
+ int count, int intrflag, int dma_restart);
+ void (*start_input) (int dev, unsigned long buf,
+ int count, int intrflag, int dma_restart);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local);
+ int (*prepare_for_input) (int dev, int bufsize, int nbufs);
+ int (*prepare_for_output) (int dev, int bufsize, int nbufs);
+ void (*reset) (int dev);
+ void (*halt_xfer) (int dev);
+ int (*local_qlen)(int dev);
+ void (*copy_from_user)(int dev, char *localbuf, int localoffs,
+ snd_rw_buf *userbuf, int useroffs, int len);
+ int buffcount;
+ long buffsize;
+ int dmachan;
+ struct dma_buffparms *dmap;
+};
+
+struct mixer_operations {
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+};
+
+struct synth_operations {
+ struct synth_info *info;
+ int midi_dev;
+ int synth_type;
+ int synth_subtype;
+
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ int (*kill_note) (int dev, int voice, int note, int velocity);
+ int (*start_note) (int dev, int voice, int note, int velocity);
+ int (*set_instr) (int dev, int voice, int instr);
+ void (*reset) (int dev);
+ void (*hw_control) (int dev, unsigned char *event);
+ int (*load_patch) (int dev, int format, snd_rw_buf *addr,
+ int offs, int count, int pmgr_flag);
+ void (*aftertouch) (int dev, int voice, int pressure);
+ void (*controller) (int dev, int voice, int ctrl_num, int value);
+ void (*panning) (int dev, int voice, int value);
+ void (*volume_method) (int dev, int mode);
+ int (*pmgr_interface) (int dev, struct patmgr_info *info);
+ void (*bender) (int dev, int chn, int value);
+ int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
+
+ struct voice_alloc_info alloc;
+ struct channel_info chn_info[16];
+};
+
+struct midi_operations {
+ struct midi_info info;
+ struct synth_operations *converter;
+ int (*open) (int dev, int mode,
+ void (*inputintr)(int dev, unsigned char data),
+ void (*outputintr)(int dev)
+ );
+ void (*close) (int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ int (*putc) (int dev, unsigned char data);
+ int (*start_read) (int dev);
+ int (*end_read) (int dev);
+ void (*kick)(int dev);
+ int (*command) (int dev, unsigned char *data);
+ int (*buffer_status) (int dev);
+ int (*prefix_cmd) (int dev, unsigned char status);
+};
+
+struct sound_timer_operations {
+ struct sound_timer_info info;
+ int priority;
+ int devlink;
+ int (*open)(int dev, int mode);
+ void (*close)(int dev);
+ int (*event)(int dev, unsigned char *ev);
+ unsigned long (*get_time)(int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ void (*arm_timer)(int dev, long time);
+};
+
+#ifdef _DEV_TABLE_C_
+ struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0;
+ struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
+ struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
+ struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
+
+#ifndef EXCLUDE_SEQUENCER
+ extern struct sound_timer_operations default_sound_timer;
+ struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] =
+ {&default_sound_timer, NULL};
+ int num_sound_timers = 1;
+#else
+ struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] =
+ {NULL};
+ int num_sound_timers = 0;
+#endif
+
+/*
+ * List of low level drivers compiled into the kernel.
+ */
+
+ struct driver_info sound_drivers[] = {
+#ifndef EXCLUDE_PSS
+ {SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss},
+#endif
+#ifndef EXCLUDE_YM3812
+ {SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib},
+#endif
+#ifndef EXCLUDE_PAS
+ {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas},
+#endif
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401},
+#endif
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850},
+#endif
+#ifndef EXCLUDE_SB
+ {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb},
+#endif
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16)
+#ifndef EXCLUDE_AUDIO
+ {SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect},
+#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,"SB16 MIDI", attach_sb16midi, probe_sb16midi},
+#endif
+#endif
+#ifndef EXCLUDE_GUS16
+ {SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16},
+#endif
+#ifndef EXCLUDE_MSS
+ {SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound},
+#endif
+#ifndef EXCLUDE_GUS
+ {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus},
+#endif
+ {0, "*?*", NULL, NULL}
+ };
+
+/*
+ * List of devices actually configured in the system.
+ *
+ * Note! The detection order is significant. Don't change it.
+ */
+
+ struct card_info snd_installed_cards[] = {
+#ifndef EXCLUDE_PSS
+ {SNDCARD_PSS, {PSS_BASE, PSS_IRQ, PSS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_MPU401, {MPU_BASE, MPU_IRQ, 0}, SND_DEFAULT_ENABLE},
+#ifdef MPU2_BASE
+ {SNDCARD_MPU401, {MPU2_BASE, MPU2_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#ifdef MPU3_BASE
+ {SNDCARD_MPU401, {MPU3_BASE, MPU2_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+#ifndef EXCLUDE_MSS
+ {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA}, SND_DEFAULT_ENABLE},
+# ifdef MSS2_BASE
+ {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA}, SND_DEFAULT_ENABLE},
+# endif
+#endif
+
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_UART6850, {U6850_BASE, U6850_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_PAS
+ {SNDCARD_PAS, {PAS_BASE, PAS_IRQ, PAS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_SB
+ {SNDCARD_SB, {SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16)
+#ifndef EXCLUDE_AUDIO
+ {SNDCARD_SB16, {SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE},
+#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,{SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+
+#ifndef EXCLUDE_GUS
+#ifndef EXCLUDE_GUS16
+ {SNDCARD_GUS16, {GUS16_BASE, GUS16_IRQ, GUS16_DMA}, SND_DEFAULT_ENABLE},
+#endif
+ {SNDCARD_GUS, {GUS_BASE, GUS_IRQ, GUS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_YM3812
+ {SNDCARD_ADLIB, {FM_MONO, 0, 0}, SND_DEFAULT_ENABLE},
+#endif
+ {0, {0}, 0}
+ };
+
+ int num_sound_drivers =
+ sizeof(sound_drivers) / sizeof (struct driver_info);
+ int num_sound_cards =
+ sizeof(snd_installed_cards) / sizeof (struct card_info);
+
+
+#else
+ extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; int num_audiodevs;
+ extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+ extern struct synth_operations * synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_synths;
+ extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+ extern struct sound_timer_operations * sound_timer_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_sound_timers;
+
+ extern struct driver_info sound_drivers[];
+ extern int num_sound_drivers;
+ extern struct card_info snd_installed_cards[];
+ extern int num_sound_cards;
+
+long sndtable_init(long mem_start);
+int sndtable_get_cardcount (void);
+struct address_info *sound_getconf(int card_type);
+void sound_chconf(int card_type, int ioaddr, int irq, int dma);
+int snd_find_driver(int type);
+
+#endif /* _DEV_TABLE_C_ */
+#endif /* _DEV_TABLE_H_ */
diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c
new file mode 100644
index 000000000000..0e275b6a3aea
--- /dev/null
+++ b/sys/i386/isa/sound/dmabuf.c
@@ -0,0 +1,964 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ *
+ * Copyright by Hannu Savolainen 1993, 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "sound_calls.h"
+
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
+
+DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]);
+
+static struct dma_buffparms dmaps[MAX_AUDIO_DEV] =
+{0}; /*
+ * Primitive way to allocate
+ * such a large array.
+ * Needs dynamic run-time alloction.
+ */
+
+static void
+reorganize_buffers (int dev)
+{
+ /*
+ * This routine breaks the physical device buffers to logical ones.
+ */
+
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ struct audio_operations *dsp_dev = audio_devs[dev];
+
+ unsigned i, p, n;
+ unsigned sr, nc, sz, bsz;
+
+ if (dmap->fragment_size == 0)
+ { /* Compute the fragment size using the default algorithm */
+
+ sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
+ nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
+ sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
+
+ if (sr < 1 || nc < 1 || sz < 1)
+ {
+ printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
+ dev, sr, nc, sz);
+ sr = DSP_DEFAULT_SPEED;
+ nc = 1;
+ sz = 8;
+ }
+
+ sz /= 8; /* #bits -> #bytes */
+
+ sz = sr * nc * sz;
+
+ /*
+ * Compute a buffer size for time not exeeding 1 second.
+ * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+ * of sound (using the current speed, sample size and #channels).
+ */
+
+ bsz = dsp_dev->buffsize;
+ while (bsz > sz)
+ bsz /= 2;
+
+ if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize)
+ bsz /= 2; /* Needs at least 2 buffers */
+
+ if (dmap->subdivision == 0) /* Not already set */
+ dmap->subdivision = 1; /* Init to default value */
+
+ bsz /= dmap->subdivision;
+
+ if (bsz < 64)
+ bsz = 4096; /* Just a sanity check */
+
+ while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS)
+ bsz *= 2;
+
+ dmap->fragment_size = bsz;
+ }
+ else
+ {
+ /*
+ * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or
+ * the buffer sice computation has already been done.
+ */
+ if (dmap->fragment_size > audio_devs[dev]->buffsize)
+ dmap->fragment_size = audio_devs[dev]->buffsize;
+ bsz = dmap->fragment_size;
+ }
+
+ /*
+ * Now computing addresses for the logical buffers
+ */
+
+ n = 0;
+ for (i = 0; i < dmap->raw_count &&
+ n < dmap->max_fragments &&
+ n < MAX_SUB_BUFFERS; i++)
+ {
+ p = 0;
+
+ while ((p + bsz) <= dsp_dev->buffsize &&
+ n < dmap->max_fragments &&
+ n < MAX_SUB_BUFFERS)
+ {
+ dmap->buf[n] = dmap->raw_buf[i] + p;
+ dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p;
+ p += bsz;
+ n++;
+ }
+ }
+
+ dmap->nbufs = n;
+ dmap->bytes_in_use = n * bsz;
+
+ for (i = 0; i < dmap->nbufs; i++)
+ {
+ dmap->counts[i] = 0;
+ }
+
+ dmap->flags |= DMA_ALLOC_DONE;
+}
+
+static void
+dma_init_buffers (int dev)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev];
+
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+
+ dmap->flags = DMA_BUSY; /* Other flags off */
+ dmap->qlen = dmap->qhead = dmap->qtail = 0;
+
+ dmap->qlen = dmap->qtail = dmap->qhead = 0;
+ dmap->dma_mode = DMODE_NONE;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ int retval;
+ struct dma_buffparms *dmap = NULL;
+
+ if (dev >= num_audiodevs)
+ {
+ printk ("PCM device %d not installed.\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!audio_devs[dev])
+ {
+ printk ("PCM device %d not initialized\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ dmap = audio_devs[dev]->dmap = &dmaps[dev];
+
+ if (dmap->flags & DMA_BUSY)
+ return RET_ERROR (EBUSY);
+
+#ifdef USE_RUNTIME_DMAMEM
+ dmap->raw_buf[0] = NULL;
+ sound_dma_malloc (dev);
+#endif
+
+ if (dmap->raw_buf[0] == NULL)
+ return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
+
+ if ((retval = audio_devs[dev]->open (dev, mode)) < 0)
+ return retval;
+
+ dmap->open_mode = mode;
+ dmap->subdivision = dmap->underrun_count = 0;
+ dmap->fragment_size = 0;
+ dmap->max_fragments = 65536; /* Just a large value */
+
+ dma_init_buffers (dev);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);
+
+ return 0;
+}
+
+static void
+dma_reset (int dev)
+{
+ int retval;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ audio_devs[dev]->reset (dev);
+ audio_devs[dev]->close (dev);
+
+ if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0)
+ printk ("Sound: Reset failed - Can't reopen device\n");
+ RESTORE_INTR (flags);
+
+ dma_init_buffers (dev);
+ reorganize_buffers (dev);
+}
+
+static int
+dma_sync (int dev)
+{
+ unsigned long flags;
+
+ if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)
+ {
+ DISABLE_INTR (flags);
+
+ while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])
+ && audio_devs[dev]->dmap->qlen)
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ RESTORE_INTR (flags);
+ return audio_devs[dev]->dmap->qlen;
+ }
+ }
+ RESTORE_INTR (flags);
+
+ /*
+ * Some devices such as GUS have huge amount of on board RAM for the
+ * audio data. We have to wait util the device has finished playing.
+ */
+
+ DISABLE_INTR (flags);
+ if (audio_devs[dev]->local_qlen) /* Device has hidden buffers */
+ {
+ while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ && audio_devs[dev]->local_qlen (dev))
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ);
+ }
+ }
+ RESTORE_INTR (flags);
+ }
+ return audio_devs[dev]->dmap->qlen;
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ unsigned long flags;
+
+ if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT))
+ {
+ dma_sync (dev);
+ }
+
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_free (dev);
+#endif
+
+ DISABLE_INTR (flags);
+ audio_devs[dev]->reset (dev);
+
+ audio_devs[dev]->close (dev);
+
+ audio_devs[dev]->dmap->dma_mode = DMODE_NONE;
+ audio_devs[dev]->dmap->flags &= ~DMA_BUSY;
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ unsigned long flags;
+ int err = EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ DISABLE_INTR (flags);
+ if (!dmap->qlen)
+ {
+ if (dmap->flags & DMA_RESTART)
+ {
+ dma_reset (dev);
+ dmap->flags &= ~DMA_RESTART;
+ }
+
+ if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ if (dmap->dma_mode)
+ {
+ int err;
+
+ if ((err = audio_devs[dev]->prepare_for_input (dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+ dmap->dma_mode = DMODE_INPUT;
+ }
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ {
+ audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
+ dmap->fragment_size, 0,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+ !(dmap->flags & DMA_STARTED));
+ dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+ }
+
+ /* Wait for the next block */
+
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else
+ err = EINTR;
+ }
+ RESTORE_INTR (flags);
+
+ if (!dmap->qlen)
+ return RET_ERROR (err);
+
+ *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]];
+ *len = dmap->fragment_size - dmap->counts[dmap->qhead];
+
+ return dmap->qhead;
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ int p = dmap->counts[dmap->qhead] + c;
+
+ if (p >= dmap->fragment_size)
+ { /* This buffer is completely empty */
+ dmap->counts[dmap->qhead] = 0;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+ dmap->qlen--;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ }
+ else
+ dmap->counts[dmap->qhead] = p;
+
+ return 0;
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_RESET:
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ dma_sync (dev);
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ return IOCTL_OUT (arg, dmap->fragment_size);
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ {
+ int fact = IOCTL_IN (arg);
+
+ if (fact == 0)
+ {
+ fact = dmap->subdivision;
+ if (fact == 0)
+ fact = 1;
+ return IOCTL_OUT (arg, fact);
+ }
+
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size)/* Loo late to change */
+ return RET_ERROR (EINVAL);
+
+ if (fact > MAX_REALTIME_FACTOR)
+ return RET_ERROR (EINVAL);
+
+ if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+ return RET_ERROR (EINVAL);
+
+ dmap->subdivision = fact;
+ return IOCTL_OUT (arg, fact);
+ }
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ {
+ int fact = IOCTL_IN (arg);
+ int bytes, count;
+
+ if (fact == 0)
+ return RET_ERROR (EIO);
+
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size)/* Loo late to change */
+ return RET_ERROR (EINVAL);
+
+ bytes = fact & 0xffff;
+ count = (fact >> 16) & 0xffff;
+
+ if (count == 0)
+ count = MAX_SUB_BUFFERS;
+
+ if (bytes < 7 || bytes > 17) /* <64 || > 128k */
+ return RET_ERROR (EINVAL);
+
+ if (count < 2)
+ return RET_ERROR (EINVAL);
+
+ dmap->fragment_size = (1 << bytes);
+ dmap->max_fragments = count;
+
+ if (dmap->fragment_size > audio_devs[dev]->buffsize)
+ dmap->fragment_size = audio_devs[dev]->buffsize;
+
+ if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+ audio_devs[dev]->flags & DMA_AUTOMODE)
+ dmap->fragment_size /= 2; /* Needs at least 2 buffers */
+
+ dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
+ return IOCTL_OUT (arg, bytes | (count << 16));
+ }
+ break;
+
+ default:
+ return audio_devs[dev]->ioctl (dev, cmd, arg, local);
+ }
+
+ return RET_ERROR (EIO);
+}
+
+static int
+space_in_queue (int dev)
+{
+ int len, max, tmp;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->qlen == dmap->nbufs)/* No space at all */
+ return 0;
+
+ /*
+ * Verify that there are no more pending buffers than the limit
+ * defined by the process.
+ */
+
+ max = dmap->max_fragments;
+ len = dmap->qlen;
+
+ if (audio_devs[dev]->local_qlen)
+ {
+ tmp = audio_devs[dev]->local_qlen (dev);
+ if (tmp & len)
+ tmp--; /*
+ * This buffer has been counted twice
+ */
+ len += tmp;
+ }
+
+ if (len >= max)
+ return 0;
+ return 1;
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+ unsigned long flags;
+ int abort, err = EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->dma_mode == DMODE_INPUT) /* Direction change */
+ {
+ dma_reset (dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+ else if (dmap->flags & DMA_RESTART) /* Restart buffering */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ }
+
+ dmap->flags &= ~DMA_RESTART;
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ if (!dmap->dma_mode)
+ {
+ int err;
+
+ dmap->dma_mode = DMODE_OUTPUT;
+ if ((err = audio_devs[dev]->prepare_for_output (dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ return err;
+ }
+
+
+ DISABLE_INTR (flags);
+
+ abort = 0;
+ while (!space_in_queue (dev) &&
+ !abort)
+ {
+ /*
+ * Wait for free space
+ */
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ err = EIO;
+ abort = 1;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ err = EINTR;
+ abort = 1;
+ }
+ }
+ RESTORE_INTR (flags);
+
+ if (!space_in_queue (dev))
+ {
+ return RET_ERROR (err); /* Caught a signal ? */
+ }
+
+ *buf = dmap->buf[dmap->qtail];
+ *size = dmap->fragment_size;
+ dmap->counts[dmap->qtail] = 0;
+
+ return dmap->qtail;
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (buff_no != dmap->qtail)
+ printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail);
+
+ dmap->qlen++;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+
+ dmap->counts[dmap->qtail] = l;
+
+ if ((l != dmap->fragment_size) &&
+ ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ audio_devs[dev]->flags & NEEDS_RESTART))
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ {
+ dmap->flags |= DMA_ACTIVE;
+ audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+ dmap->counts[dmap->qhead], 0,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+ !(dmap->flags & DMA_STARTED));
+ dmap->flags |= DMA_STARTED;
+ }
+
+ return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ int chan = audio_devs[dev]->dmachan;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ unsigned long flags;
+
+ /*
+ * This function is not as portable as it should be.
+ */
+
+ /*
+ * The count must be one less than the actual size. This is handled by
+ * set_dma_addr()
+ */
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE)
+ { /*
+ * Auto restart mode. Transfer the whole *
+ * buffer
+ */
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
+ set_dma_addr (chan, dmap->raw_buf_phys[0]);
+ set_dma_count (chan, dmap->bytes_in_use);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else
+
+#ifdef __386BSD__
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+ dmap->raw_buf_phys[0],
+ dmap->bytes_in_use,
+ chan);
+#else
+#if defined(GENERIC_SYSV)
+#ifndef DMAMODE_AUTO
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+#endif
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
+#ifdef DMAMODE_AUTO
+ | DMAMODE_AUTO
+#endif
+ ,
+ dmap->raw_buf_phys[0], dmap->bytes_in_use);
+ dma_enable (chan);
+#else
+#error This routine is not valid for this OS.
+#endif
+#endif
+
+#endif
+ }
+ else
+ {
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode);
+ set_dma_addr (chan, physaddr);
+ set_dma_count (chan, count);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else
+#ifdef __386BSD__
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+ physaddr,
+ count,
+ chan);
+#else
+
+#if defined(GENERIC_SYSV)
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
+ physaddr, count);
+ dma_enable (chan);
+#else
+#error This routine is not valid for this OS.
+#endif /* GENERIC_SYSV */
+#endif
+
+#endif
+ }
+
+ return count;
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ int dev;
+
+ /*
+ * NOTE! This routine could be called several times.
+ */
+
+ for (dev = 0; dev < num_audiodevs; dev++)
+ audio_devs[dev]->dmap = &dmaps[dev];
+ return mem_start;
+}
+
+void
+DMAbuf_outputintr (int dev, int event_type)
+{
+ /*
+ * Event types:
+ * 0 = DMA transfer done. Device still has more data in the local
+ * buffer.
+ * 1 = DMA transfer done. Device doesn't have local buffer or it's
+ * empty now.
+ * 2 = No DMA transfer but the device has now more space in it's local
+ * buffer.
+ */
+
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (event_type != 2)
+ {
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ {
+ printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+ return;
+ }
+
+ dmap->qlen--;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ dmap->flags &= ~DMA_ACTIVE;
+
+ if (dmap->qlen)
+ {
+ audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+ dmap->counts[dmap->qhead], 1,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->flags |= DMA_ACTIVE;
+ }
+ else if (event_type == 1)
+ {
+ dmap->underrun_count++;
+ audio_devs[dev]->halt_xfer (dev);
+ if ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ audio_devs[dev]->flags & NEEDS_RESTART)
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+ }
+ } /* event_type != 2 */
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->qlen == (dmap->nbufs - 1))
+ {
+ printk ("Sound: Recording overrun\n");
+ dmap->underrun_count++;
+ audio_devs[dev]->halt_xfer (dev);
+ dmap->flags &= ~DMA_ACTIVE;
+ if (audio_devs[dev]->flags & DMA_AUTOMODE)
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+ }
+ else
+ {
+ dmap->qlen++;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+
+ audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
+ dmap->fragment_size, 1,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->flags |= DMA_ACTIVE;
+ }
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+ unsigned long flags;
+ int chan = audio_devs[dev]->dmachan;
+
+ if (ALLOC_DMA_CHN (chan))
+ {
+ printk ("Unable to grab DMA%d for the audio driver\n", chan);
+ return RET_ERROR (EBUSY);
+ }
+
+ DISABLE_INTR (flags);
+#ifdef linux
+ disable_dma (chan);
+ clear_dma_ff (chan);
+#endif
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+ int chan = audio_devs[dev]->dmachan;
+
+ DMAbuf_reset_dma (chan);
+ RELEASE_DMA_CHN (chan);
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+}
+
+/*
+ * The sound_mem_init() is called by mem_init() immediately after mem_map is
+ * initialized and before free_page_list is created.
+ *
+ * This routine allocates DMA buffers at the end of available physical memory (
+ * <16M) and marks pages reserved at mem_map.
+ */
+
+#else
+/*
+ * Stub versions if audio services not included
+ */
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ return RET_ERROR (ENXIO);
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ return 0;
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_open_dma (int chan)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+DMAbuf_close_dma (int chan)
+{
+ return;
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+ return;
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ return;
+}
+
+void
+DMAbuf_outputintr (int dev, int underrun_flag)
+{
+ return;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/finetune.h b/sys/i386/isa/sound/finetune.h
new file mode 100644
index 000000000000..b86a0eb15a1f
--- /dev/null
+++ b/sys/i386/isa/sound/finetune.h
@@ -0,0 +1,49 @@
+#ifdef SEQUENCER_C
+/*
+ * 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.
+ *
+ */
+
+ unsigned short finetune_table[128] =
+ {
+/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499,
+/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567,
+/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637,
+/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707,
+/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777,
+/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848,
+/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919,
+/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991,
+/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063,
+/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136,
+/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210,
+/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284,
+/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358,
+/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433,
+/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509,
+/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585
+ };
+#else
+ extern unsigned short finetune_table[128];
+#endif
diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c
new file mode 100644
index 000000000000..614532c7d114
--- /dev/null
+++ b/sys/i386/isa/sound/gus_card.c
@@ -0,0 +1,193 @@
+/*
+ * sound/gus_card.c
+ *
+ * Detection routine for the Gravis Ultrasound.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#include "gus_hw.h"
+
+void gusintr (int);
+
+int gus_base, gus_irq, gus_dma;
+extern int gus_wave_volume;
+extern int gus_pcm_volume;
+extern int have_gus_max;
+
+long
+attach_gus_card (long mem_start, struct address_info *hw_config)
+{
+ int io_addr;
+
+ snd_set_irq_handler (hw_config->irq, gusintr);
+
+ if (gus_wave_detect (hw_config->io_base)) /*
+ * Try first the default
+ */
+ {
+ mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
+#ifndef EXCLUDE_MIDI
+ mem_start = gus_midi_init (mem_start);
+#endif
+#ifndef EXCLUDE_SEQUENCER
+ sound_timer_init (hw_config->io_base + 8);
+#endif
+ return mem_start;
+ }
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /*
+ * Already tested
+ */
+ if (gus_wave_detect (io_addr))
+ {
+ printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base);
+ mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
+#ifndef EXCLUDE_MIDI
+ mem_start = gus_midi_init (mem_start);
+#endif
+#ifndef EXCLUDE_SEQUENCER
+ sound_timer_init (io_addr + 8);
+#endif
+ return mem_start;
+ }
+
+#endif
+
+ return mem_start; /*
+ * Not detected
+ */
+}
+
+int
+probe_gus (struct address_info *hw_config)
+{
+ int io_addr;
+
+ if (gus_wave_detect (hw_config->io_base))
+ return 1;
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /*
+ * Already tested
+ */
+ if (gus_wave_detect (io_addr))
+ return 1;
+
+#endif
+
+ return 0;
+}
+
+void
+gusintr (int irq)
+{
+ unsigned char src;
+
+#ifdef linux
+ sti ();
+#endif
+
+#ifndef EXCLUDE_GUSMAX
+ if (have_gus_max)
+ ad1848_interrupt (irq);
+#endif
+
+ while (1)
+ {
+ if (!(src = INB (u_IrqStatus)))
+ return;
+
+ if (src & DMA_TC_IRQ)
+ {
+ guswave_dma_irq ();
+ }
+
+ if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
+ {
+#ifndef EXCLUDE_MIDI
+ gus_midi_interrupt (0);
+#endif
+ }
+
+ if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
+ {
+#ifndef EXCLUDE_SEQUENCER
+ sound_timer_interrupt ();
+#else
+ gus_write8 (0x45, 0); /* Stop timers */
+#endif
+ }
+
+ if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+ {
+ gus_voice_irq ();
+ }
+ }
+}
+
+#endif
+
+/*
+ * Some extra code for the 16 bit sampling option
+ */
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS16)
+
+int
+probe_gus_db16 (struct address_info *hw_config)
+{
+ return ad1848_detect (hw_config->io_base);
+}
+
+long
+attach_gus_db16 (long mem_start, struct address_info *hw_config)
+{
+ gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+
+ ad1848_init ("GUS 16 bit sampling", hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h
new file mode 100644
index 000000000000..f97a0b8670e3
--- /dev/null
+++ b/sys/i386/isa/sound/gus_hw.h
@@ -0,0 +1,50 @@
+
+/*
+ * I/O addresses
+ */
+
+#define u_Base (gus_base + 0x000)
+#define u_Mixer u_Base
+#define u_Status (gus_base + 0x006)
+#define u_TimerControl (gus_base + 0x008)
+#define u_TimerData (gus_base + 0x009)
+#define u_IRQDMAControl (gus_base + 0x00b)
+#define u_MidiControl (gus_base + 0x100)
+#define MIDI_RESET 0x03
+#define MIDI_ENABLE_XMIT 0x20
+#define MIDI_ENABLE_RCV 0x80
+#define u_MidiStatus u_MidiControl
+#define MIDI_RCV_FULL 0x01
+#define MIDI_XMIT_EMPTY 0x02
+#define MIDI_FRAME_ERR 0x10
+#define MIDI_OVERRUN 0x20
+#define MIDI_IRQ_PEND 0x80
+#define u_MidiData (gus_base + 0x101)
+#define u_Voice (gus_base + 0x102)
+#define u_Command (gus_base + 0x103)
+#define u_DataLo (gus_base + 0x104)
+#define u_DataHi (gus_base + 0x105)
+#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */
+#define u_MixSelect (gus_base + 0x506) /* registers. */
+#define u_IrqStatus u_Status
+# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */
+# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */
+# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */
+# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */
+# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */
+# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */
+# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */
+
+#define ICS2101 1
+# define ICS_MIXDEVS 6
+# define DEV_MIC 0
+# define DEV_LINE 1
+# define DEV_CD 2
+# define DEV_GF1 3
+# define DEV_UNUSED 4
+# define DEV_VOL 5
+
+# define CHN_LEFT 0
+# define CHN_RIGHT 1
+#define CS4231 2
+#define u_DRAMIO (gus_base + 0x107)
diff --git a/sys/i386/isa/sound/gus_linearvol.h b/sys/i386/isa/sound/gus_linearvol.h
new file mode 100644
index 000000000000..7ad0c30d4fd9
--- /dev/null
+++ b/sys/i386/isa/sound/gus_linearvol.h
@@ -0,0 +1,18 @@
+static unsigned short gus_linearvol[128] = {
+ 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0,
+ 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0,
+ 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70,
+ 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0,
+ 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38,
+ 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78,
+ 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8,
+ 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8,
+ 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c,
+ 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c,
+ 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c,
+ 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c,
+ 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c,
+ 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc,
+ 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc,
+ 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc
+};
diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c
new file mode 100644
index 000000000000..87aea6251daf
--- /dev/null
+++ b/sys/i386/isa/sound/gus_midi.c
@@ -0,0 +1,309 @@
+/*
+ * sound/gus2_midi.c
+ *
+ * The low level driver for the GUS Midi Interface.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "gus_hw.h"
+
+#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI)
+
+static int midi_busy = 0, input_opened = 0;
+static int my_dev;
+static int output_used = 0;
+static volatile unsigned char gus_midi_control;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+extern int gus_base, gus_irq, gus_dma;
+
+#define GUS_MIDI_STATUS() INB(u_MidiStatus)
+
+static int
+gus_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+
+ if (midi_busy)
+ {
+ printk ("GUS: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ OUTB (MIDI_RESET, u_MidiControl);
+ gus_delay ();
+
+ gus_midi_control = 0;
+ input_opened = 0;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ gus_midi_control |= MIDI_ENABLE_RCV;
+ input_opened = 1;
+ }
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ }
+
+ OUTB (gus_midi_control, u_MidiControl); /*
+ * Enable
+ */
+
+ midi_busy = 1;
+ qlen = qhead = qtail = output_used = 0;
+ midi_input_intr = input;
+
+ return 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+ unsigned long flags;
+ int ok = 0;
+
+ output_used = 1;
+
+ DISABLE_INTR (flags);
+
+ if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY)
+ {
+ ok = 1;
+ OUTB (midi_byte, u_MidiData);
+ }
+ else
+ {
+ /*
+ * Enable Midi xmit interrupts (again)
+ */
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ OUTB (gus_midi_control, u_MidiControl);
+ }
+
+ RESTORE_INTR (flags);
+ return ok;
+}
+
+static void
+gus_midi_close (int dev)
+{
+ /*
+ * Reset FIFO pointers, disable intrs
+ */
+
+ OUTB (MIDI_RESET, u_MidiControl);
+ midi_busy = 0;
+}
+
+static int
+gus_midi_out (int dev, unsigned char midi_byte)
+{
+
+ unsigned long flags;
+
+ /*
+ * Drain the local queue first
+ */
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Output the byte if the local queue is empty.
+ */
+
+ if (!qlen)
+ if (dump_to_midi (midi_byte))
+ return 1; /*
+ * OK
+ */
+
+ /*
+ * Put to the local queue
+ */
+
+ if (qlen >= 256)
+ return 0; /*
+ * Local queue full
+ */
+
+ DISABLE_INTR (flags);
+
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+gus_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+gus_midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+gus_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+gus_midi_kick (int dev)
+{
+}
+
+static int
+gus_midi_buffer_status (int dev)
+{
+ unsigned long flags;
+
+ if (!output_used)
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ if (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
+}
+
+#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations gus_midi_operations =
+{
+ {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
+ &std_midi_synth,
+ gus_midi_open,
+ gus_midi_close,
+ gus_midi_ioctl,
+ gus_midi_out,
+ gus_midi_start_read,
+ gus_midi_end_read,
+ gus_midi_kick,
+ NULL, /*
+ * command
+ */
+ gus_midi_buffer_status,
+ NULL
+};
+
+long
+gus_midi_init (long mem_start)
+{
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ OUTB (MIDI_RESET, u_MidiControl);
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &gus_midi_operations;
+ return mem_start;
+}
+
+void
+gus_midi_interrupt (int dummy)
+{
+ unsigned char stat, data;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ stat = GUS_MIDI_STATUS ();
+
+ if (stat & MIDI_RCV_FULL)
+ {
+ data = INB (u_MidiData);
+ if (input_opened)
+ midi_input_intr (my_dev, data);
+ }
+
+ if (stat & MIDI_XMIT_EMPTY)
+ {
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ if (!qlen)
+ {
+ /*
+ * Disable Midi output interrupts, since no data in the buffer
+ */
+ gus_midi_control &= ~MIDI_ENABLE_XMIT;
+ OUTB (gus_midi_control, u_MidiControl);
+ }
+ }
+
+ if (stat & MIDI_FRAME_ERR)
+ printk ("Midi framing error\n");
+ if (stat & MIDI_OVERRUN && input_opened)
+ printk ("GUS: Midi input overrun\n");
+
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c
new file mode 100644
index 000000000000..055a1170e9fb
--- /dev/null
+++ b/sys/i386/isa/sound/gus_vol.c
@@ -0,0 +1,147 @@
+/*
+ * gus_vol.c - Compute volume for GUS.
+ *
+ * Greg Lee 1993.
+ */
+#include "sound_config.h"
+#ifndef EXCLUDE_GUS
+#include "gus_linearvol.h"
+
+#define GUS_VOLUME gus_wave_volume
+
+
+extern int gus_wave_volume;
+
+/*
+ * Calculate gus volume from note velocity, main volume, expression, and
+ * intrinsic patch volume given in patch library. Expression is multiplied
+ * in, so it emphasizes differences in note velocity, while main volume is
+ * added in -- I don't know whether this is right, but it seems reasonable to
+ * me. (In the previous stage, main volume controller messages were changed
+ * to expression controller messages, if they were found to be used for
+ * dynamic volume adjustments, so here, main volume can be assumed to be
+ * constant throughout a song.)
+ *
+ * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so
+ * we can give a big boost to very weak voices like nylon guitar and the
+ * basses. The normal value is 64. Strings are assigned lower values.
+ */
+unsigned short
+gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
+{
+ int i, m, n, x;
+
+
+ /*
+ * A voice volume of 64 is considered neutral, so adjust the main volume if
+ * something other than this neutral value was assigned in the patch
+ * library.
+ */
+ x = 256 + 6 * (voicev - 64);
+
+ /*
+ * Boost expression by voice volume above neutral.
+ */
+ if (voicev > 65)
+ xpn += voicev - 64;
+ xpn += (voicev - 64) / 2;
+
+ /*
+ * Combine multiplicative and level components.
+ */
+ x = vel * xpn * 6 + (voicev / 4) * x;
+
+#ifdef GUS_VOLUME
+ /*
+ * Further adjustment by installation-specific master volume control
+ * (default 60).
+ */
+ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+ /*
+ * Experimental support for the channel main volume
+ */
+
+ mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */
+ x = (x * mainv * mainv) / 16384;
+#endif
+
+ if (x < 2)
+ return (0);
+ else if (x >= 65535)
+ return ((15 << 8) | 255);
+
+ /*
+ * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit
+ * mantissa m.
+ */
+ n = x;
+ i = 7;
+ if (n < 128)
+ {
+ while (i > 0 && n < (1 << i))
+ i--;
+ }
+ else
+ while (n > 255)
+ {
+ n >>= 1;
+ i++;
+ }
+ /*
+ * Mantissa is part of linear volume not expressed in exponent. (This is
+ * not quite like real logs -- I wonder if it's right.)
+ */
+ m = x - (1 << i);
+
+ /*
+ * Adjust mantissa to 8 bits.
+ */
+ if (m > 0)
+ {
+ if (i > 8)
+ m >>= i - 8;
+ else if (i < 8)
+ m <<= 8 - i;
+ }
+
+ return ((i << 8) + m);
+}
+
+/*
+ * Volume-values are interpreted as linear values. Volume is based on the
+ * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in)
+ * and the volume set by the mixer-device (default 60%).
+ */
+
+unsigned short
+gus_linear_vol (int vol, int mainvol)
+{
+ int mixer_mainvol;
+
+ if (vol <= 0)
+ vol = 0;
+ else if (vol >= 127)
+ vol = 127;
+
+#ifdef GUS_VOLUME
+ mixer_mainvol = GUS_VOLUME;
+#else
+ mixer_mainvol = 100;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+ if (mainvol <= 0)
+ mainvol = 0;
+ else if (mainvol >= 127)
+ mainvol = 127;
+#else
+ mainvol = 128;
+#endif
+
+ return gus_linearvol[(((vol * mainvol) / 128) * mixer_mainvol) / 100];
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c
new file mode 100644
index 000000000000..5f8e39c8ae5b
--- /dev/null
+++ b/sys/i386/isa/sound/gus_wave.c
@@ -0,0 +1,3246 @@
+/*
+ * sound/gus_wave.c
+ *
+ * Driver for the Gravis UltraSound wave table synth.
+ *
+ * Copyright by Hannu Savolainen 1993, 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.
+ *
+ */
+
+#include "sound_config.h"
+#include "ultrasound.h"
+#include "gus_hw.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#define MAX_SAMPLE 128
+#define MAX_PATCH 256
+
+struct voice_info
+ {
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ unsigned long mode;
+ int bender;
+ int bender_range;
+ int panning;
+ int midi_volume;
+ unsigned int initial_volume;
+ unsigned int current_volume;
+ int loop_irq_mode, loop_irq_parm;
+#define LMODE_FINISH 1
+#define LMODE_PCM 2
+#define LMODE_PCM_STOP 3
+ int volume_irq_mode, volume_irq_parm;
+#define VMODE_HALT 1
+#define VMODE_ENVELOPE 2
+#define VMODE_START_NOTE 3
+
+ int env_phase;
+ unsigned char env_rate[6];
+ unsigned char env_offset[6];
+
+ /*
+ * Volume computation parameters for gus_adagio_vol()
+ */
+ int main_vol, expression_vol, patch_vol;
+
+ /* Variables for "Ultraclick" removal */
+ int dev_pending, note_pending, volume_pending, sample_pending;
+ char kill_pending;
+ long offset_pending;
+
+ };
+
+static struct voice_alloc_info *voice_alloc;
+
+extern int gus_base;
+extern int gus_irq, gus_dma;
+static long gus_mem_size = 0;
+static long free_mem_ptr = 0;
+static int gus_busy = 0;
+static int nr_voices = 0;
+static int gus_devnum = 0;
+static int volume_base, volume_scale, volume_method;
+static int gus_recmask = SOUND_MASK_MIC;
+static int recording_active = 0;
+
+int gus_wave_volume = 60;
+int gus_pcm_volume = 80;
+int have_gus_max = 0;
+static int gus_line_vol = 100, gus_mic_vol = 0;
+static unsigned char mix_image = 0x00;
+
+/*
+ * Current version of this driver doesn't allow synth and PCM functions
+ * at the same time. The active_device specifies the active driver
+ */
+static int active_device = 0;
+
+#define GUS_DEV_WAVE 1 /* Wave table synth */
+#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */
+#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */
+
+static int gus_sampling_speed;
+static int gus_sampling_channels;
+static int gus_sampling_bits;
+
+DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+
+/*
+ * Variables and buffers for PCM output
+ */
+#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */
+
+static int pcm_bsize, pcm_nblk, pcm_banksize;
+static int pcm_datasize[MAX_PCM_BUFFERS];
+static volatile int pcm_head, pcm_tail, pcm_qlen;
+static volatile int pcm_active;
+static volatile int dma_active;
+static int pcm_opened = 0;
+static int pcm_current_dev;
+static int pcm_current_block;
+static unsigned long pcm_current_buf;
+static int pcm_current_count;
+static int pcm_current_intrflag;
+
+struct voice_info voices[32];
+
+static int freq_div_table[] =
+{
+ 44100, /* 14 */
+ 41160, /* 15 */
+ 38587, /* 16 */
+ 36317, /* 17 */
+ 34300, /* 18 */
+ 32494, /* 19 */
+ 30870, /* 20 */
+ 29400, /* 21 */
+ 28063, /* 22 */
+ 26843, /* 23 */
+ 25725, /* 24 */
+ 24696, /* 25 */
+ 23746, /* 26 */
+ 22866, /* 27 */
+ 22050, /* 28 */
+ 21289, /* 29 */
+ 20580, /* 30 */
+ 19916, /* 31 */
+ 19293 /* 32 */
+};
+
+static struct patch_info *samples;
+static long sample_ptrs[MAX_SAMPLE + 1];
+static int sample_map[32];
+static int free_sample;
+
+
+static int patch_table[MAX_PATCH];
+static int patch_map[32];
+
+static struct synth_info gus_info =
+{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
+
+static void gus_poke (long addr, unsigned char data);
+static void compute_and_set_volume (int voice, int volume, int ramp_time);
+extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
+extern unsigned short gus_linear_vol (int vol, int mainvol);
+static void compute_volume (int voice, int volume);
+static void do_volume_irq (int voice);
+static void set_input_volumes (void);
+
+#define INSTANT_RAMP -1 /* Instant change. No ramping */
+#define FAST_RAMP 0 /* Fastest possible ramp */
+
+static void
+reset_sample_memory (void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_SAMPLE; i++)
+ sample_ptrs[i] = -1;
+ for (i = 0; i < 32; i++)
+ sample_map[i] = -1;
+ for (i = 0; i < 32; i++)
+ patch_map[i] = -1;
+
+ gus_poke (0, 0); /* Put a silent sample to the beginning */
+ gus_poke (1, 0);
+ free_mem_ptr = 2;
+
+ free_sample = 0;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ patch_table[i] = -1;
+}
+
+void
+gus_delay (void)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ INB (u_DRAMIO);
+}
+
+static void
+gus_poke (long addr, unsigned char data)
+{ /* Writes a byte to the DRAM */
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB (0x43, u_Command);
+ OUTB (addr & 0xff, u_DataLo);
+ OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+ OUTB (0x44, u_Command);
+ OUTB ((addr >> 16) & 0xff, u_DataHi);
+ OUTB (data, u_DRAMIO);
+ RESTORE_INTR (flags);
+}
+
+static unsigned char
+gus_peek (long addr)
+{ /* Reads a byte from the DRAM */
+ unsigned long flags;
+ unsigned char tmp;
+
+ DISABLE_INTR (flags);
+ OUTB (0x43, u_Command);
+ OUTB (addr & 0xff, u_DataLo);
+ OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+ OUTB (0x44, u_Command);
+ OUTB ((addr >> 16) & 0xff, u_DataHi);
+ tmp = INB (u_DRAMIO);
+ RESTORE_INTR (flags);
+
+ return tmp;
+}
+
+void
+gus_write8 (int reg, unsigned int data)
+{ /* Writes to an indirect register (8 bit) */
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+ OUTB ((unsigned char) (data & 0xff), u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+unsigned char
+gus_read8 (int reg)
+{ /* Reads from an indirect register (8 bit). Offset 0x80. */
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg | 0x80, u_Command);
+ val = INB (u_DataHi);
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+unsigned char
+gus_look8 (int reg)
+{ /* Reads from an indirect register (8 bit). No additional offset. */
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg, u_Command);
+ val = INB (u_DataHi);
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+void
+gus_write16 (int reg, unsigned int data)
+{ /* Writes to an indirect register (16 bit) */
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+
+ OUTB ((unsigned char) (data & 0xff), u_DataLo);
+ OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+unsigned short
+gus_read16 (int reg)
+{ /* Reads from an indirect register (16 bit). Offset 0x80. */
+ unsigned long flags;
+ unsigned char hi, lo;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg | 0x80, u_Command);
+
+ lo = INB (u_DataLo);
+ hi = INB (u_DataHi);
+
+ RESTORE_INTR (flags);
+
+ return ((hi << 8) & 0xff00) | lo;
+}
+
+void
+gus_write_addr (int reg, unsigned long address, int is16bit)
+{ /* Writes an 24 bit memory address */
+ unsigned long hold_address;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (is16bit)
+ {
+ /*
+ * Special processing required for 16 bit patches
+ */
+
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+ /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
+ gus_delay ();
+ gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_select_voice (int voice)
+{
+ if (voice < 0 || voice > 31)
+ return;
+
+ OUTB (voice, u_Voice);
+}
+
+static void
+gus_select_max_voices (int nvoices)
+{
+ if (nvoices < 14)
+ nvoices = 14;
+ if (nvoices > 32)
+ nvoices = 32;
+
+ voice_alloc->max_voice = nr_voices = nvoices;
+
+ gus_write8 (0x0e, (nvoices - 1) | 0xc0);
+}
+
+static void
+gus_voice_on (unsigned int mode)
+{
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+ gus_delay ();
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+}
+
+static void
+gus_voice_off (void)
+{
+ gus_write8 (0x00, gus_read8 (0x00) | 0x03);
+}
+
+static void
+gus_voice_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) |
+ (mode & 0xfc)); /* Don't touch last two bits */
+ gus_delay ();
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_voice_freq (unsigned long freq)
+{
+ unsigned long divisor = freq_div_table[nr_voices - 14];
+ unsigned short fc;
+
+ fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+ fc = fc << 1;
+
+ gus_write16 (0x01, fc);
+}
+
+static void
+gus_voice_volume (unsigned int vol)
+{
+ gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */
+ gus_write16 (0x09, (unsigned short) (vol << 4));
+}
+
+static void
+gus_voice_balance (unsigned int balance)
+{
+ gus_write8 (0x0c, (unsigned char) (balance & 0xff));
+}
+
+static void
+gus_ramp_range (unsigned int low, unsigned int high)
+{
+ gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff));
+ gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff));
+}
+
+static void
+gus_ramp_rate (unsigned int scale, unsigned int rate)
+{
+ gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
+}
+
+static void
+gus_rampon (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, mode & 0xfc);
+ gus_delay ();
+ gus_write8 (0x0d, mode & 0xfc);
+}
+
+static void
+gus_ramp_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) |
+ (mode & 0xfc)); /* Leave the last 2 bits alone */
+ gus_delay ();
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_rampoff (void)
+{
+ gus_write8 (0x0d, 0x03);
+}
+
+static void
+gus_set_voice_pos (int voice, long position)
+{
+ int sample_no;
+
+ if ((sample_no = sample_map[voice]) != -1)
+ if (position < samples[sample_no].len)
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].offset_pending = position;
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample_no] + position,
+ samples[sample_no].mode & WAVE_16_BITS);
+}
+
+static void
+gus_voice_init (int voice)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_volume (0);
+ gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */
+ gus_write8 (0x00, 0x03); /* Voice off */
+ gus_write8 (0x0d, 0x03); /* Ramping off */
+ voice_alloc->map[voice] = 0;
+ RESTORE_INTR (flags);
+
+}
+
+static void
+gus_voice_init2 (int voice)
+{
+ voices[voice].panning = 0;
+ voices[voice].mode = 0;
+ voices[voice].orig_freq = 20000;
+ voices[voice].current_freq = 20000;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200;
+ voices[voice].initial_volume = 0;
+ voices[voice].current_volume = 0;
+ voices[voice].loop_irq_mode = 0;
+ voices[voice].loop_irq_parm = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].volume_irq_parm = 0;
+ voices[voice].env_phase = 0;
+ voices[voice].main_vol = 127;
+ voices[voice].patch_vol = 127;
+ voices[voice].expression_vol = 127;
+ voices[voice].sample_pending = -1;
+}
+
+static void
+step_envelope (int voice)
+{
+ unsigned vol, prev_vol, phase;
+ unsigned char rate;
+ long int flags;
+
+ if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ return;
+ /*
+ * Sustain phase begins. Continue envelope after receiving note off.
+ */
+ }
+
+ if (voices[voice].env_phase >= 5)
+ { /* Envelope finished. Shoot the voice down */
+ gus_voice_init (voice);
+ return;
+ }
+
+ prev_vol = voices[voice].current_volume;
+ phase = ++voices[voice].env_phase;
+ compute_volume (voice, voices[voice].midi_volume);
+ vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
+ rate = voices[voice].env_rate[phase];
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ gus_voice_volume (prev_vol);
+
+
+ gus_write8 (0x06, rate); /* Ramping rate */
+
+ voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+ if (((vol - prev_vol) / 64) == 0) /* No significant volume change */
+ {
+ RESTORE_INTR (flags);
+ step_envelope (voice); /* Continue the envelope on the next step */
+ return;
+ }
+
+ if (vol > prev_vol)
+ {
+ if (vol >= (4096 - 64))
+ vol = 4096 - 65;
+ gus_ramp_range (0, vol);
+ gus_rampon (0x20); /* Increasing volume, with IRQ */
+ }
+ else
+ {
+ if (vol <= 64)
+ vol = 65;
+ gus_ramp_range (vol, 4030);
+ gus_rampon (0x60); /* Decreasing volume, with IRQ */
+ }
+ voices[voice].current_volume = vol;
+ RESTORE_INTR (flags);
+}
+
+static void
+init_envelope (int voice)
+{
+ voices[voice].env_phase = -1;
+ voices[voice].current_volume = 64;
+
+ step_envelope (voice);
+}
+
+static void
+start_release (int voice, long int flags)
+{
+ if (gus_read8 (0x00) & 0x03)
+ return; /* Voice already stopped */
+
+ voices[voice].env_phase = 2; /* Will be incremented by step_envelope */
+
+ voices[voice].current_volume =
+ voices[voice].initial_volume =
+ gus_read16 (0x09) >> 4; /* Get current volume */
+
+ voices[voice].mode &= ~WAVE_SUSTAIN_ON;
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ step_envelope (voice);
+}
+
+static void
+gus_voice_fade (int voice)
+{
+ int instr_no = sample_map[voice], is16bits;
+ long int flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ if (instr_no < 0 || instr_no > MAX_SAMPLE)
+ {
+ gus_write8 (0x00, 0x03); /* Hard stop */
+ voice_alloc->map[voice] = 0;
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ start_release (voice, flags);
+ return;
+ }
+
+ /*
+ * Ramp the volume down but not too quickly.
+ */
+ if ((int) (gus_read16 (0x09) >> 4) < 100) /* Get current volume */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ return;
+ }
+
+ gus_ramp_range (65, 4030);
+ gus_ramp_rate (2, 4);
+ gus_rampon (0x40 | 0x20); /* Down, once, with IRQ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_reset (void)
+{
+ int i;
+
+ gus_select_max_voices (24);
+ volume_base = 3071;
+ volume_scale = 4;
+ volume_method = VOL_METHOD_ADAGIO;
+
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init (i); /* Turn voice off */
+ gus_voice_init2 (i);
+ }
+
+ INB (u_Status); /* Touch the status register */
+
+ gus_look8 (0x41); /* Clear any pending DMA IRQs */
+ gus_look8 (0x49); /* Clear any pending sample IRQs */
+
+ gus_read8 (0x0f); /* Clear pending IRQs */
+
+}
+
+static void
+gus_initialize (void)
+{
+ unsigned long flags;
+ unsigned char dma_image, irq_image, tmp;
+
+ static unsigned char gus_irq_map[16] =
+ {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7};
+
+ static unsigned char gus_dma_map[8] =
+ {0, 1, 0, 2, 0, 3, 4, 5};
+
+ DISABLE_INTR (flags);
+ gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /* Release Reset */
+ gus_delay ();
+ gus_delay ();
+
+ /*
+ * Clear all interrupts
+ */
+
+ gus_write8 (0x41, 0); /* DMA control */
+ gus_write8 (0x45, 0); /* Timer control */
+ gus_write8 (0x49, 0); /* Sample control */
+
+ gus_select_max_voices (24);
+
+ INB (u_Status); /* Touch the status register */
+
+ gus_look8 (0x41); /* Clear any pending DMA IRQs */
+ gus_look8 (0x49); /* Clear any pending sample IRQs */
+ gus_read8 (0x0f); /* Clear pending IRQs */
+
+ gus_reset (); /* Resets all voices */
+
+ gus_look8 (0x41); /* Clear any pending DMA IRQs */
+ gus_look8 (0x49); /* Clear any pending sample IRQs */
+ gus_read8 (0x0f); /* Clear pending IRQs */
+
+ gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */
+
+ /*
+ * Set up for Digital ASIC
+ */
+
+ OUTB (0x05, gus_base + 0x0f);
+
+ mix_image |= 0x02; /* Disable line out */
+ OUTB (mix_image, u_Mixer);
+
+ OUTB (0x00, u_IRQDMAControl);
+
+ OUTB (0x00, gus_base + 0x0f);
+
+ /*
+ * Now set up the DMA and IRQ interface
+ *
+ * The GUS supports two IRQs and two DMAs.
+ *
+ * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
+ * Adding this support requires significant changes to the dmabuf.c, dsp.c
+ * and audio.c also.
+ */
+
+ irq_image = 0;
+ tmp = gus_irq_map[gus_irq];
+ if (!tmp)
+ printk ("Warning! GUS IRQ not selected\n");
+ irq_image |= tmp;
+ irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
+
+ dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */
+ tmp = gus_dma_map[gus_dma];
+ if (!tmp)
+ printk ("Warning! GUS DMA not selected\n");
+ dma_image |= tmp;
+
+ /*
+ * For some reason the IRQ and DMA addresses must be written twice
+ */
+
+ /*
+ * Doing it first time
+ */
+
+ OUTB (mix_image, u_Mixer); /* Select DMA control */
+ OUTB (dma_image | 0x80, u_IRQDMAControl); /* Set DMA address */
+
+ OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */
+ OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */
+
+ /*
+ * Doing it second time
+ */
+
+ OUTB (mix_image, u_Mixer); /* Select DMA control */
+ OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */
+
+ OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */
+ OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */
+
+ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+
+ mix_image &= ~0x02; /* Enable line out */
+ mix_image |= 0x08; /* Enable IRQ */
+ OUTB (mix_image, u_Mixer); /*
+ * Turn mixer channels on
+ * Note! Mic in is left off.
+ */
+
+ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+
+ gusintr (0); /* Serve pending interrupts */
+ RESTORE_INTR (flags);
+}
+
+int
+gus_wave_detect (int baseaddr)
+{
+ unsigned long i;
+ unsigned long loc;
+
+ gus_base = baseaddr;
+
+ gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /* Release Reset */
+ gus_delay ();
+ gus_delay ();
+
+ /* See if there is first block there.... */
+ gus_poke (0L, 0xaa);
+ if (gus_peek (0L) != 0xaa)
+ return (0);
+
+ /* Now zero it out so that I can check for mirroring .. */
+ gus_poke (0L, 0x00);
+ for (i = 1L; i < 1024L; i++)
+ {
+ int n, failed;
+
+ /* check for mirroring ... */
+ if (gus_peek (0L) != 0)
+ break;
+ loc = i << 10;
+
+ for (n = loc - 1, failed = 0; n <= loc; n++)
+ {
+ gus_poke (loc, 0xaa);
+ if (gus_peek (loc) != 0xaa)
+ failed = 1;
+
+ gus_poke (loc, 0x55);
+ if (gus_peek (loc) != 0x55)
+ failed = 1;
+ }
+
+ if (failed)
+ break;
+ }
+ gus_mem_size = i << 10;
+ return 1;
+}
+
+static int
+guswave_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+
+ switch (cmd)
+ {
+ case SNDCTL_SYNTH_INFO:
+ gus_info.nr_voices = nr_voices;
+ IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info));
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ reset_sample_memory ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_PERCMODE:
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return gus_mem_size - free_mem_ptr - 32;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+guswave_set_instr (int dev, int voice, int instr_no)
+{
+ int sample_no;
+
+ if (instr_no < 0 || instr_no > MAX_PATCH)
+ return RET_ERROR (EINVAL);
+
+ if (voice < 0 || voice > 31)
+ return RET_ERROR (EINVAL);
+
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].sample_pending = instr_no;
+ return 0;
+ }
+
+ sample_no = patch_table[instr_no];
+ patch_map[voice] = -1;
+
+ if (sample_no < 0)
+ {
+ printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
+ return RET_ERROR (EINVAL);/* Patch not defined */
+ }
+
+ if (sample_ptrs[sample_no] == -1) /* Sample not loaded */
+ {
+ printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n",
+ sample_no, instr_no, voice);
+ return RET_ERROR (EINVAL);
+ }
+
+ sample_map[voice] = sample_no;
+ patch_map[voice] = instr_no;
+ return 0;
+}
+
+static int
+guswave_kill_note (int dev, int voice, int note, int velocity)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ voice_alloc->map[voice] = 0xffff;
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].kill_pending = 1;
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ RESTORE_INTR (flags);
+ gus_voice_fade (voice);
+ }
+
+ return 0;
+}
+
+static void
+guswave_aftertouch (int dev, int voice, int pressure)
+{
+ short lo_limit, hi_limit;
+ unsigned long flags;
+
+ return; /* Procedure currently disabled */
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2)
+ return; /* Don't mix with envelopes */
+
+ if (pressure < 32)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ compute_and_set_volume (voice, 255, 0); /* Back to original volume */
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ hi_limit = voices[voice].current_volume;
+ lo_limit = hi_limit * 99 / 100;
+ if (lo_limit < 65)
+ lo_limit = 65;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ if (hi_limit > (4095 - 65))
+ {
+ hi_limit = 4095 - 65;
+ gus_voice_volume (hi_limit);
+ }
+ gus_ramp_range (lo_limit, hi_limit);
+ gus_ramp_rate (3, 8);
+ gus_rampon (0x58); /* Bidirectional, dow, loop */
+ RESTORE_INTR (flags);
+}
+
+static void
+guswave_panning (int dev, int voice, int value)
+{
+ if (voice >= 0 || voice < 32)
+ voices[voice].panning = value;
+}
+
+static void
+guswave_volume_method (int dev, int mode)
+{
+ if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
+ volume_method = mode;
+}
+
+static void
+compute_volume (int voice, int volume)
+{
+ if (volume < 128)
+ voices[voice].midi_volume = volume;
+
+ switch (volume_method)
+ {
+ case VOL_METHOD_ADAGIO:
+ voices[voice].initial_volume =
+ gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol,
+ voices[voice].expression_vol,
+ voices[voice].patch_vol);
+ break;
+
+ case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */
+ voices[voice].initial_volume =
+ gus_linear_vol (volume, voices[voice].main_vol);
+ break;
+
+ default:
+ voices[voice].initial_volume = volume_base +
+ (voices[voice].midi_volume * volume_scale);
+ }
+
+ if (voices[voice].initial_volume > 4030)
+ voices[voice].initial_volume = 4030;
+}
+
+static void
+compute_and_set_volume (int voice, int volume, int ramp_time)
+{
+ int current, target, rate;
+ unsigned long flags;
+
+ compute_volume (voice, volume);
+ voices[voice].current_volume = voices[voice].initial_volume;
+
+ DISABLE_INTR (flags);
+ /*
+ * CAUTION! Interrupts disabled. Enable them before returning
+ */
+
+ gus_select_voice (voice);
+
+ current = gus_read16 (0x09) >> 4;
+ target = voices[voice].initial_volume;
+
+ if (ramp_time == INSTANT_RAMP)
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ if (ramp_time == FAST_RAMP)
+ rate = 63;
+ else
+ rate = 16;
+ gus_ramp_rate (0, rate);
+
+ if ((target - current) / 64 == 0) /* Close enough to target. */
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ if (target > current)
+ {
+ if (target > (4095 - 65))
+ target = 4095 - 65;
+ gus_ramp_range (current, target);
+ gus_rampon (0x00); /* Ramp up, once, no IRQ */
+ }
+ else
+ {
+ if (target < 65)
+ target = 65;
+
+ gus_ramp_range (target, current);
+ gus_rampon (0x40); /* Ramp down, once, no irq */
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+dynamic_volume_change (int voice)
+{
+ unsigned char status;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x00); /* Get voice status */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03)
+ return; /* Voice was not running */
+
+ if (!(voices[voice].mode & WAVE_ENVELOPES))
+ {
+ compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+ return;
+ }
+
+ /*
+ * Voice is running and has envelopes.
+ */
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x0d); /* Ramping status */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03) /* Sustain phase? */
+ {
+ compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+ return;
+ }
+
+ if (voices[voice].env_phase < 0)
+ return;
+
+ compute_volume (voice, voices[voice].midi_volume);
+
+}
+
+static void
+guswave_controller (int dev, int voice, int ctrl_num, int value)
+{
+ unsigned long flags;
+ unsigned long freq;
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ voices[voice].bender = value;
+
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ {
+ freq = compute_finetune (voices[voice].orig_freq, value,
+ voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (freq);
+ RESTORE_INTR (flags);
+ }
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+ case CTL_EXPRESSION:
+ value /= 128;
+ case CTRL_EXPRESSION:
+ if (volume_method == VOL_METHOD_ADAGIO)
+ {
+ voices[voice].expression_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
+ }
+ break;
+
+ case CTL_PAN:
+ voices[voice].panning = (value * 2) - 128;
+ break;
+
+ case CTL_MAIN_VOLUME:
+ value = (value * 100) / 16383;
+
+ case CTRL_MAIN_VOLUME:
+ voices[voice].main_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int
+guswave_start_note2 (int dev, int voice, int note_num, int volume)
+{
+ int sample, best_sample, best_delta, delta_freq;
+ int is16bits, samplep, patch, pan;
+ unsigned long note_freq, base_note, freq, flags;
+ unsigned char mode = 0;
+
+ if (voice < 0 || voice > 31)
+ {
+ printk ("GUS: Invalid voice\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ if (note_num == 255)
+ {
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ voices[voice].midi_volume = volume;
+ dynamic_volume_change (voice);
+ return 0;
+ }
+
+ compute_and_set_volume (voice, volume, 1);
+ return 0;
+ }
+
+ if ((patch = patch_map[voice]) == -1)
+ {
+ return RET_ERROR (EINVAL);
+ }
+
+ if ((samplep = patch_table[patch]) == -1)
+ {
+ return RET_ERROR (EINVAL);
+ }
+
+ note_freq = note_to_freq (note_num);
+
+ /*
+ * Find a sample within a patch so that the note_freq is between low_note
+ * and high_note.
+ */
+ sample = -1;
+
+ best_sample = samplep;
+ best_delta = 1000000;
+ while (samplep >= 0 && sample == -1)
+ {
+ delta_freq = note_freq - samples[samplep].base_note;
+ if (delta_freq < 0)
+ delta_freq = -delta_freq;
+ if (delta_freq < best_delta)
+ {
+ best_sample = samplep;
+ best_delta = delta_freq;
+ }
+ if (samples[samplep].low_note <= note_freq &&
+ note_freq <= samples[samplep].high_note)
+ sample = samplep;
+ else
+ samplep = samples[samplep].key; /*
+ * Follow link
+ */
+ }
+ if (sample == -1)
+ sample = best_sample;
+
+ if (sample == -1)
+ {
+ printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
+ return 0; /* Should play default patch ??? */
+ }
+
+ is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;
+ voices[voice].mode = samples[sample].mode;
+ voices[voice].patch_vol = samples[sample].volume;
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ {
+ voices[voice].env_rate[i] = samples[sample].env_rate[i];
+ voices[voice].env_offset[i] = samples[sample].env_offset[i];
+ }
+ }
+
+ sample_map[voice] = sample;
+
+ base_note = samples[sample].base_note / 100; /* Try to avoid overflows */
+ note_freq /= 100;
+
+ freq = samples[sample].base_freq * note_freq / base_note;
+
+ voices[voice].orig_freq = freq;
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender,
+ voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ pan = (samples[sample].panning + voices[voice].panning) / 32;
+ pan += 7;
+ if (pan < 0)
+ pan = 0;
+ if (pan > 15)
+ pan = 15;
+
+ if (samples[sample].mode & WAVE_16_BITS)
+ {
+ mode |= 0x04; /* 16 bits */
+ if ((sample_ptrs[sample] >> 18) !=
+ ((sample_ptrs[sample] + samples[sample].len) >> 18))
+ printk ("GUS: Sample address error\n");
+ }
+
+ /*************************************************************************
+ * CAUTION! Interrupts disabled. Don't return before enabling
+ *************************************************************************/
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_off ();
+ gus_rampoff ();
+
+ RESTORE_INTR (flags);
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ compute_volume (voice, volume);
+ init_envelope (voice);
+ }
+ else
+ compute_and_set_volume (voice, volume, 0);
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len -
+ voices[voice].offset_pending, is16bits); /* start=end */
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
+ is16bits); /* Sample start=begin */
+
+ if (samples[sample].mode & WAVE_LOOPING)
+ {
+ mode |= 0x08;
+
+ if (samples[sample].mode & WAVE_BIDIR_LOOP)
+ mode |= 0x10;
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ {
+ gus_write_addr (0x0a,
+ sample_ptrs[sample] + samples[sample].loop_end -
+ voices[voice].offset_pending, is16bits);
+ mode |= 0x40;
+ }
+
+ gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start,
+ is16bits);/* Loop start location */
+ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end,
+ is16bits);/* Loop end location */
+ }
+ else
+ {
+ mode |= 0x20; /* Loop IRQ at the end */
+ voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */
+ voices[voice].loop_irq_parm = 1;
+ gus_write_addr (0x02, sample_ptrs[sample],
+ is16bits);/* Loop start location */
+ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1,
+ is16bits);/* Loop end location */
+ }
+ gus_voice_freq (freq);
+ gus_voice_balance (pan);
+ gus_voice_on (mode);
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+/*
+ * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking
+ * when the note playing on the voice is changed. It uses volume
+ * ramping.
+ */
+
+static int
+guswave_start_note (int dev, int voice, int note_num, int volume)
+{
+ long int flags;
+ int mode;
+ int ret_val = 0;
+
+ DISABLE_INTR (flags);
+ if (note_num == 255)
+ {
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].volume_pending = volume;
+ else
+ {
+ RESTORE_INTR (flags);
+ ret_val = guswave_start_note2 (dev, voice, note_num, volume);
+ }
+ }
+ else
+ {
+ gus_select_voice (voice);
+ mode = gus_read8 (0x00);
+ if (mode & 0x20)
+ gus_write8 (0x00, mode & 0xdf); /* No interrupt! */
+
+ voices[voice].offset_pending = 0;
+ voices[voice].kill_pending = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].loop_irq_mode = 0;
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ RESTORE_INTR (flags);
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ DISABLE_INTR (flags);
+ }
+
+ if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < 2065))
+ {
+ ret_val = guswave_start_note2 (dev, voice, note_num, volume);
+ }
+ else
+ {
+ voices[voice].dev_pending = dev;
+ voices[voice].note_pending = note_num;
+ voices[voice].volume_pending = volume;
+ voices[voice].volume_irq_mode = VMODE_START_NOTE;
+
+ gus_rampoff ();
+ gus_ramp_range (2000, 4065);
+ gus_ramp_rate (0, 63);/* Fastest possible rate */
+ gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */
+ RESTORE_INTR (flags);
+ }
+ }
+ return ret_val;
+}
+
+static void
+guswave_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init (i);
+ gus_voice_init2 (i);
+ }
+}
+
+static int
+guswave_open (int dev, int mode)
+{
+ int err;
+
+ if (gus_busy)
+ return RET_ERROR (EBUSY);
+
+ gus_initialize ();
+
+ if ((err = DMAbuf_open_dma (gus_devnum)) < 0)
+ return err;
+
+ RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+ gus_busy = 1;
+ active_device = GUS_DEV_WAVE;
+
+ gus_reset ();
+
+ return 0;
+}
+
+static void
+guswave_close (int dev)
+{
+ gus_busy = 0;
+ active_device = 0;
+ gus_reset ();
+
+ DMAbuf_close_dma (gus_devnum);
+}
+
+static int
+guswave_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct patch_info patch;
+ int instr;
+ long sizeof_patch;
+
+ unsigned long blk_size, blk_end, left, src_offs, target;
+
+ sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
+
+ if (format != GUS_PATCH)
+ {
+ printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < sizeof_patch)
+ {
+ printk ("GUS Error: Patch header too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ count -= sizeof_patch;
+
+ if (free_sample >= MAX_SAMPLE)
+ {
+ printk ("GUS: Sample table full\n");
+ return RET_ERROR (ENOSPC);
+ }
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs);
+
+ instr = patch.instr_no;
+
+ if (instr < 0 || instr > MAX_PATCH)
+ {
+ printk ("GUS: Invalid patch number %d\n", instr);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < patch.len)
+ {
+ printk ("GUS Warning: Patch record too short (%d<%d)\n",
+ count, (int) patch.len);
+ patch.len = count;
+ }
+
+ if (patch.len <= 0 || patch.len > gus_mem_size)
+ {
+ printk ("GUS: Invalid sample length %d\n", (int) patch.len);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (patch.mode & WAVE_LOOPING)
+ {
+ if (patch.loop_start < 0 || patch.loop_start >= patch.len)
+ {
+ printk ("GUS: Invalid loop start\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
+ {
+ printk ("GUS: Invalid loop end\n");
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */
+
+#define GUS_BANK_SIZE (256*1024)
+
+ if (patch.mode & WAVE_16_BITS)
+ {
+ /*
+ * 16 bit samples must fit one 256k bank.
+ */
+ if (patch.len >= GUS_BANK_SIZE)
+ {
+ printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len);
+ return RET_ERROR (ENOSPC);
+ }
+
+ if ((free_mem_ptr / GUS_BANK_SIZE) !=
+ ((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
+ {
+ unsigned long tmp_mem = /* Aling to 256K */
+ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
+
+ if ((tmp_mem + patch.len) > gus_mem_size)
+ return RET_ERROR (ENOSPC);
+
+ free_mem_ptr = tmp_mem; /* This leaves unusable memory */
+ }
+ }
+
+ if ((free_mem_ptr + patch.len) > gus_mem_size)
+ return RET_ERROR (ENOSPC);
+
+ sample_ptrs[free_sample] = free_mem_ptr;
+
+ /*
+ * Tremolo is not possible with envelopes
+ */
+
+ if (patch.mode & WAVE_ENVELOPES)
+ patch.mode &= ~WAVE_TREMOLO;
+
+ memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
+
+ /*
+ * Link this_one sample to the list of samples for patch 'instr'.
+ */
+
+ samples[free_sample].key = patch_table[instr];
+ patch_table[instr] = free_sample;
+
+ /*
+ * Use DMA to transfer the wave data to the DRAM
+ */
+
+ left = patch.len;
+ src_offs = 0;
+ target = free_mem_ptr;
+
+ while (left) /* Not completely transferred yet */
+ {
+ blk_size = audio_devs[gus_devnum]->buffsize;
+ if (blk_size > left)
+ blk_size = left;
+
+ /*
+ * DMA cannot cross 256k bank boundaries. Check for that.
+ */
+ blk_end = target + blk_size;
+
+ if ((target >> 18) != (blk_end >> 18))
+ { /* Split the block */
+
+ blk_end &= ~(256 * 1024 - 1);
+ blk_size = blk_end - target;
+ }
+
+#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA)
+ /*
+ * For some reason the DMA is not possible. We have to use PIO.
+ */
+ {
+ long i;
+ unsigned char data;
+
+ for (i = 0; i < blk_size; i++)
+ {
+ GET_BYTE_FROM_USER (data, addr, sizeof_patch + i);
+ if (patch.mode & WAVE_UNSIGNED)
+
+ if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
+ data ^= 0x80; /* Convert to signed */
+ gus_poke (target + i, data);
+ }
+ }
+#else /* GUS_NO_DMA */
+ {
+ unsigned long address, hold_address;
+ unsigned char dma_command;
+ unsigned long flags;
+
+ /*
+ * OK, move now. First in and then out.
+ */
+
+ COPY_FROM_USER (audio_devs[gus_devnum]->dmap->raw_buf[0],
+ addr, sizeof_patch + src_offs,
+ blk_size);
+
+ DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/
+ gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma (gus_devnum,
+ audio_devs[gus_devnum]->dmap->raw_buf_phys[0],
+ blk_size, DMA_MODE_WRITE);
+
+ /*
+ * Set the DRAM address for the wave data
+ */
+
+ address = target;
+
+ if (audio_devs[gus_devnum]->dmachan > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+
+ /*
+ * Start the DMA transfer
+ */
+
+ dma_command = 0x21; /* IRQ enable, DMA start */
+ if (patch.mode & WAVE_UNSIGNED)
+ dma_command |= 0x80; /* Invert MSB */
+ if (patch.mode & WAVE_16_BITS)
+ dma_command |= 0x40; /* 16 bit _DATA_ */
+ if (audio_devs[gus_devnum]->dmachan > 3)
+ dma_command |= 0x04; /* 16 bit DMA _channel_ */
+
+ gus_write8 (0x41, dma_command); /* Lets bo luteet (=bugs) */
+
+ /*
+ * Sleep here until the DRAM DMA done interrupt is served
+ */
+ active_device = GUS_DEV_WAVE;
+
+ DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ);
+ if (TIMED_OUT (dram_sleeper, dram_sleep_flag))
+ printk ("GUS: DMA Transfer timed out\n");
+ RESTORE_INTR (flags);
+ }
+#endif /* GUS_NO_DMA */
+
+ /*
+ * Now the next part
+ */
+
+ left -= blk_size;
+ src_offs += blk_size;
+ target += blk_size;
+
+ gus_write8 (0x41, 0); /* Stop DMA */
+ }
+
+ free_mem_ptr += patch.len;
+
+ if (!pmgr_flag)
+ pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
+ free_sample++;
+ return 0;
+}
+
+static void
+guswave_hw_control (int dev, unsigned char *event)
+{
+ int voice, cmd;
+ unsigned short p1, p2;
+ unsigned long plong, flags;
+
+ cmd = event[2];
+ voice = event[3];
+ p1 = *(unsigned short *) &event[4];
+ p2 = *(unsigned short *) &event[6];
+ plong = *(unsigned long *) &event[4];
+
+ if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
+ (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
+ do_volume_irq (voice);
+
+ switch (cmd)
+ {
+
+ case _GUS_NUMVOICES:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_select_max_voices (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICESAMPLE:
+ guswave_set_instr (dev, voice, p1);
+ break;
+
+ case _GUS_VOICEON:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_voice_on (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEOFF:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_off ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEFADE:
+ gus_voice_fade (voice);
+ break;
+
+ case _GUS_VOICEMODE:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_voice_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEBALA:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_balance (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEFREQ:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (plong);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEVOL:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_volume (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEVOL2: /* Just update the software voice level */
+ voices[voice].initial_volume =
+ voices[voice].current_volume = p1;
+ break;
+
+ case _GUS_RAMPRANGE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_ramp_range (p1, p2);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPRATE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NJET-NJET */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_ramp_rate (p1, p2);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPMODE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_ramp_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPON:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* EI-EI */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_rampon (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPOFF:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NEJ-NEJ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOLUME_SCALE:
+ volume_base = p1;
+ volume_scale = p2;
+ break;
+
+ case _GUS_VOICE_POS:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_set_voice_pos (voice, plong);
+ RESTORE_INTR (flags);
+ break;
+
+ default:;
+ }
+}
+
+static int
+gus_sampling_set_speed (int speed)
+{
+ if (speed <= 0)
+ return gus_sampling_speed;
+
+ if (speed > 44100)
+ speed = 44100;
+
+ gus_sampling_speed = speed;
+ return speed;
+}
+
+static int
+gus_sampling_set_channels (int channels)
+{
+ if (!channels)
+ return gus_sampling_channels;
+ if (channels > 2)
+ channels = 2;
+ if (channels < 1)
+ channels = 1;
+ gus_sampling_channels = channels;
+ return channels;
+}
+
+static int
+gus_sampling_set_bits (int bits)
+{
+ if (!bits)
+ return gus_sampling_bits;
+
+ if (bits != 8 && bits != 16)
+ bits = 8;
+
+ gus_sampling_bits = bits;
+ return bits;
+}
+
+static int
+gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return gus_sampling_set_speed (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return gus_sampling_speed;
+ return IOCTL_OUT (arg, gus_sampling_speed);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return gus_sampling_set_channels (arg + 1) - 1;
+ return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return gus_sampling_set_channels (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return gus_sampling_channels;
+ return IOCTL_OUT (arg, gus_sampling_channels);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return gus_sampling_set_bits (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return gus_sampling_bits;
+ return IOCTL_OUT (arg, gus_sampling_bits);
+
+ case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ case SOUND_PCM_READ_FILTER:
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+gus_sampling_reset (int dev)
+{
+}
+
+static int
+gus_sampling_open (int dev, int mode)
+{
+#ifdef GUS_NO_DMA
+ printk ("GUS: DMA mode not enabled. Device not supported\n");
+ return RET_ERROR (ENXIO);
+#endif
+
+ if (gus_busy)
+ return RET_ERROR (EBUSY);
+
+ gus_initialize ();
+
+ gus_busy = 1;
+ active_device = 0;
+
+ gus_reset ();
+ reset_sample_memory ();
+ gus_select_max_voices (14);
+
+ pcm_active = 0;
+ dma_active = 0;
+ pcm_opened = 1;
+ if (mode & OPEN_READ)
+ {
+ recording_active = 1;
+ set_input_volumes ();
+ }
+
+ return 0;
+}
+
+static void
+gus_sampling_close (int dev)
+{
+ gus_reset ();
+ gus_busy = 0;
+ pcm_opened = 0;
+ active_device = 0;
+
+ if (recording_active)
+ set_input_volumes ();
+
+ recording_active = 0;
+}
+
+static void
+gus_sampling_update_volume (void)
+{
+ unsigned long flags;
+ int voice;
+
+ DISABLE_INTR (flags);
+ if (pcm_active && pcm_opened)
+ for (voice = 0; voice < gus_sampling_channels; voice++)
+ {
+ gus_select_voice (voice);
+ gus_rampoff ();
+ gus_voice_volume (1530 + (25 * gus_pcm_volume));
+ gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+play_next_pcm_block (void)
+{
+ unsigned long flags;
+ int speed = gus_sampling_speed;
+ int this_one, is16bits, chn;
+ unsigned long dram_loc;
+ unsigned char mode[2], ramp_mode[2];
+
+ if (!pcm_qlen)
+ return;
+
+ this_one = pcm_head;
+
+ for (chn = 0; chn < gus_sampling_channels; chn++)
+ {
+ mode[chn] = 0x00;
+ ramp_mode[chn] = 0x03; /* Ramping and rollover off */
+
+ if (chn == 0)
+ {
+ mode[chn] |= 0x20; /* Loop IRQ */
+ voices[chn].loop_irq_mode = LMODE_PCM;
+ }
+
+ if (gus_sampling_bits != 8)
+ {
+ is16bits = 1;
+ mode[chn] |= 0x04; /* 16 bit data */
+ }
+ else
+ is16bits = 0;
+
+ dram_loc = this_one * pcm_bsize;
+ dram_loc += chn * pcm_banksize;
+
+ if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */
+ {
+ mode[chn] |= 0x08; /* Enable loop */
+ ramp_mode[chn] = 0x03;/* Disable rollover bit */
+ }
+ else
+ {
+ if (chn == 0)
+ ramp_mode[chn] = 0x04; /* Enable rollover bit */
+ }
+
+ DISABLE_INTR (flags);
+ gus_select_voice (chn);
+ gus_voice_freq (speed);
+
+ if (gus_sampling_channels == 1)
+ gus_voice_balance (7); /* mono */
+ else if (chn == 0)
+ gus_voice_balance (0); /* left */
+ else
+ gus_voice_balance (15); /* right */
+
+ if (!pcm_active) /* Playback not already active */
+ {
+ /*
+ * The playback was not started yet (or there has been a pause).
+ * Start the voice (again) and ask for a rollover irq at the end of
+ * this_one block. If this_one one is last of the buffers, use just
+ * the normal loop with irq.
+ */
+
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_volume (1530 + (25 * gus_pcm_volume));
+ gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+
+ gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */
+ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start */
+
+ if (chn != 0)
+ gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk),
+ is16bits); /* Loop end location */
+ }
+
+ if (chn == 0)
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one],
+ is16bits); /* Loop end location */
+ else
+ mode[chn] |= 0x08; /* Enable looping */
+
+ if (pcm_datasize[this_one] != pcm_bsize)
+ {
+ /*
+ * Incompletely filled block. Possibly the last one.
+ */
+ if (chn == 0)
+ {
+ mode[chn] &= ~0x08; /* Disable looping */
+ mode[chn] |= 0x20;/* Enable IRQ at the end */
+ voices[0].loop_irq_mode = LMODE_PCM_STOP;
+ ramp_mode[chn] = 0x03; /* No rollover bit */
+ }
+ else
+ {
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one],
+ is16bits); /* Loop end location */
+ mode[chn] &= ~0x08; /* Disable looping */
+ }
+ }
+
+ RESTORE_INTR (flags);
+ }
+
+ for (chn = 0; chn < gus_sampling_channels; chn++)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (chn);
+ gus_write8 (0x0d, ramp_mode[chn]);
+ gus_voice_on (mode[chn]);
+ RESTORE_INTR (flags);
+ }
+
+ pcm_active = 1;
+}
+
+static void
+gus_transfer_output_block (int dev, unsigned long buf,
+ int total_count, int intrflag, int chn)
+{
+ /*
+ * This routine transfers one block of audio data to the DRAM. In mono mode
+ * it's called just once. When in stereo mode, this_one routine is called
+ * once for both channels.
+ *
+ * The left/mono channel data is transferred to the beginning of dram and the
+ * right data to the area pointed by gus_page_size.
+ */
+
+ int this_one, count;
+ unsigned long flags;
+ unsigned char dma_command;
+ unsigned long address, hold_address;
+
+ DISABLE_INTR (flags);
+
+ count = total_count / gus_sampling_channels;
+
+ if (chn == 0)
+ {
+ if (pcm_qlen >= pcm_nblk)
+ printk ("GUS Warning: PCM buffers out of sync\n");
+
+ this_one = pcm_current_block = pcm_tail;
+ pcm_qlen++;
+ pcm_tail = (pcm_tail + 1) % pcm_nblk;
+ pcm_datasize[this_one] = count;
+ }
+ else
+ this_one = pcm_current_block;
+
+ gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);
+
+ address = this_one * pcm_bsize;
+ address += chn * pcm_banksize;
+
+ if (audio_devs[dev]->dmachan > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+
+ dma_command = 0x21; /* IRQ enable, DMA start */
+
+ if (gus_sampling_bits != 8)
+ dma_command |= 0x40; /* 16 bit _DATA_ */
+ else
+ dma_command |= 0x80; /* Invert MSB */
+
+ if (audio_devs[dev]->dmachan > 3)
+ dma_command |= 0x04; /* 16 bit DMA channel */
+
+ gus_write8 (0x41, dma_command); /* Kickstart */
+
+ if (chn == (gus_sampling_channels - 1)) /* Last channel */
+ {
+ /*
+ * Last (right or mono) channel data
+ */
+ dma_active = 1; /* DMA started. There is a unacknowledged buffer */
+ active_device = GUS_DEV_PCM_DONE;
+ if (!pcm_active && (pcm_qlen > 0 || count < pcm_bsize))
+ {
+ play_next_pcm_block ();
+ }
+ }
+ else
+ {
+ /*
+ * Left channel data. The right channel
+ * is transferred after DMA interrupt
+ */
+ active_device = GUS_DEV_PCM_CONTINUE;
+ }
+
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_sampling_output_block (int dev, unsigned long buf, int total_count,
+ int intrflag, int restart_dma)
+{
+ pcm_current_buf = buf;
+ pcm_current_count = total_count;
+ pcm_current_intrflag = intrflag;
+ pcm_current_dev = dev;
+ gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
+}
+
+static void
+gus_sampling_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+ unsigned char mode;
+
+ DISABLE_INTR (flags);
+
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ mode = 0xa0; /* DMA IRQ enabled, invert MSB */
+
+ if (audio_devs[dev]->dmachan > 3)
+ mode |= 0x04; /* 16 bit DMA channel */
+ if (gus_sampling_channels > 1)
+ mode |= 0x02; /* Stereo */
+ mode |= 0x01; /* DMA enable */
+
+ gus_write8 (0x49, mode);
+
+ RESTORE_INTR (flags);
+}
+
+static int
+gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
+{
+ unsigned int rate;
+
+ rate = (9878400 / (gus_sampling_speed + 2)) / 16;
+
+ gus_write8 (0x48, rate & 0xff); /* Set sampling rate */
+
+ if (gus_sampling_bits != 8)
+ {
+ printk ("GUS Error: 16 bit recording not supported\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static int
+gus_sampling_prepare_for_output (int dev, int bsize, int bcount)
+{
+ int i;
+
+ long mem_ptr, mem_size;
+
+ mem_ptr = 0;
+ mem_size = gus_mem_size / gus_sampling_channels;
+
+ if (mem_size > (256 * 1024))
+ mem_size = 256 * 1024;
+
+ pcm_bsize = bsize / gus_sampling_channels;
+ pcm_head = pcm_tail = pcm_qlen = 0;
+
+ pcm_nblk = MAX_PCM_BUFFERS;
+ if ((pcm_bsize * pcm_nblk) > mem_size)
+ pcm_nblk = mem_size / pcm_bsize;
+
+ for (i = 0; i < pcm_nblk; i++)
+ pcm_datasize[i] = 0;
+
+ pcm_banksize = pcm_nblk * pcm_bsize;
+
+ if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024))
+ pcm_nblk--;
+
+ return 0;
+}
+
+static int
+gus_local_qlen (int dev)
+{
+ return pcm_qlen;
+}
+
+static void
+gus_copy_from_user (int dev, char *localbuf, int localoffs,
+ snd_rw_buf * userbuf, int useroffs, int len)
+{
+ if (gus_sampling_channels == 1)
+ {
+ COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
+ }
+ else if (gus_sampling_bits == 8)
+ {
+ int in_left = useroffs;
+ int in_right = useroffs + 1;
+ char *out_left, *out_right;
+ int i;
+
+ len /= 2;
+ localoffs /= 2;
+ out_left = &localbuf[localoffs];
+ out_right = out_left + pcm_bsize;
+
+ for (i = 0; i < len; i++)
+ {
+ GET_BYTE_FROM_USER (*out_left++, userbuf, in_left);
+ in_left += 2;
+ GET_BYTE_FROM_USER (*out_right++, userbuf, in_right);
+ in_right += 2;
+ }
+ }
+ else
+ {
+ int in_left = useroffs;
+ int in_right = useroffs + 1;
+ short *out_left, *out_right;
+ int i;
+
+ len /= 4;
+ localoffs /= 4;
+
+ out_left = (short *) &localbuf[localoffs];
+ out_right = out_left + (pcm_bsize / 2);
+
+ for (i = 0; i < len; i++)
+ {
+ GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left);
+ in_left += 2;
+ GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right);
+ in_right += 2;
+ }
+ }
+}
+
+static struct audio_operations gus_sampling_operations =
+{
+ "Gravis UltraSound",
+ NEEDS_RESTART,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ gus_sampling_open,
+ gus_sampling_close,
+ gus_sampling_output_block,
+ gus_sampling_start_input,
+ gus_sampling_ioctl,
+ gus_sampling_prepare_for_input,
+ gus_sampling_prepare_for_output,
+ gus_sampling_reset,
+ gus_sampling_reset,
+ gus_local_qlen,
+ gus_copy_from_user
+};
+
+static void
+guswave_bender (int dev, int voice, int value)
+{
+ int freq;
+ unsigned long flags;
+
+ voices[voice].bender = value - 8192;
+ freq = compute_finetune (voices[voice].orig_freq, value,
+ voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (freq);
+ RESTORE_INTR (flags);
+}
+
+static int
+guswave_patchmgr (int dev, struct patmgr_info *rec)
+{
+ int i, n;
+
+ switch (rec->command)
+ {
+ case PM_GET_DEVTYPE:
+ rec->parm1 = PMTYPE_WAVE;
+ return 0;
+ break;
+
+ case PM_GET_NRPGM:
+ rec->parm1 = MAX_PATCH;
+ return 0;
+ break;
+
+ case PM_GET_PGMMAP:
+ rec->parm1 = MAX_PATCH;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ {
+ int ptr = patch_table[i];
+
+ rec->data.data8[i] = 0;
+
+ while (ptr >= 0 && ptr < free_sample)
+ {
+ rec->data.data8[i]++;
+ ptr = samples[ptr].key; /* Follow link */
+ }
+ }
+ return 0;
+ break;
+
+ case PM_GET_PGM_PATCHES:
+ {
+ int ptr = patch_table[rec->parm1];
+
+ n = 0;
+
+ while (ptr >= 0 && ptr < free_sample)
+ {
+ rec->data.data32[n++] = ptr;
+ ptr = samples[ptr].key; /* Follow link */
+ }
+ }
+ rec->parm1 = n;
+ return 0;
+ break;
+
+ case PM_GET_PATCH:
+ {
+ int ptr = rec->parm1;
+ struct patch_info *pat;
+
+ if (ptr < 0 || ptr >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ memcpy (rec->data.data8, (char *) &samples[ptr],
+ sizeof (struct patch_info));
+
+ pat = (struct patch_info *) rec->data.data8;
+
+ pat->key = GUS_PATCH; /* Restore patch type */
+ rec->parm1 = sample_ptrs[ptr]; /* DRAM location */
+ rec->parm2 = sizeof (struct patch_info);
+ }
+ return 0;
+ break;
+
+ case PM_SET_PATCH:
+ {
+ int ptr = rec->parm1;
+ struct patch_info *pat;
+
+ if (ptr < 0 || ptr >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ pat = (struct patch_info *) rec->data.data8;
+
+ if (pat->len > samples[ptr].len) /* Cannot expand sample */
+ return RET_ERROR (EINVAL);
+
+ pat->key = samples[ptr].key; /* Ensure the link is correct */
+
+ memcpy ((char *) &samples[ptr], rec->data.data8,
+ sizeof (struct patch_info));
+
+ pat->key = GUS_PATCH;
+ }
+ return 0;
+ break;
+
+ case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */
+ {
+ int sample = rec->parm1;
+ int n;
+ long offs = rec->parm2;
+ int l = rec->parm3;
+
+ if (sample < 0 || sample >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ if (offs < 0 || offs >= samples[sample].len)
+ return RET_ERROR (EINVAL); /* Invalid offset */
+
+ n = samples[sample].len - offs; /* Num of bytes left */
+
+ if (l > n)
+ l = n;
+
+ if (l > sizeof (rec->data.data8))
+ l = sizeof (rec->data.data8);
+
+ if (l <= 0)
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
+
+ offs += sample_ptrs[sample]; /*
+ * Begin offsess + offset to DRAM
+ */
+
+ for (n = 0; n < l; n++)
+ rec->data.data8[n] = gus_peek (offs++);
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
+ }
+ return 0;
+ break;
+
+ case PM_WRITE_PATCH: /*
+ * Writes a block of wave data to the DRAM
+ */
+ {
+ int sample = rec->parm1;
+ int n;
+ long offs = rec->parm2;
+ int l = rec->parm3;
+
+ if (sample < 0 || sample >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ if (offs < 0 || offs >= samples[sample].len)
+ return RET_ERROR (EINVAL); /*
+ * Invalid offset
+ */
+
+ n = samples[sample].len - offs; /*
+ * Nr of bytes left
+ */
+
+ if (l > n)
+ l = n;
+
+ if (l > sizeof (rec->data.data8))
+ l = sizeof (rec->data.data8);
+
+ if (l <= 0)
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
+
+ offs += sample_ptrs[sample]; /*
+ * Begin offsess + offset to DRAM
+ */
+
+ for (n = 0; n < l; n++)
+ gus_poke (offs++, rec->data.data8[n]);
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
+ }
+ return 0;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p;
+
+ p = alloc->ptr;
+ /*
+ * First look for a completely stopped voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0)
+ {
+ alloc->ptr = p;
+ return p;
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ /*
+ * Then look for a releasing voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0xffff)
+ {
+ alloc->ptr = p;
+ return p;
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+ printk ("GUS: Out of free voices\n");
+
+ alloc->ptr = p;
+ return p;
+}
+
+static struct synth_operations guswave_operations =
+{
+ &gus_info,
+ 0,
+ SYNTH_TYPE_SAMPLE,
+ SAMPLE_TYPE_GUS,
+ guswave_open,
+ guswave_close,
+ guswave_ioctl,
+ guswave_kill_note,
+ guswave_start_note,
+ guswave_set_instr,
+ guswave_reset,
+ guswave_hw_control,
+ guswave_load_patch,
+ guswave_aftertouch,
+ guswave_controller,
+ guswave_panning,
+ guswave_volume_method,
+ guswave_patchmgr,
+ guswave_bender,
+ guswave_alloc
+};
+
+static void
+set_input_volumes (void)
+{
+ unsigned long flags;
+ unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Enable channels having vol > 10%
+ * Note! bit 0x01 means line in DISABLED while 0x04 means
+ * mic in ENABLED.
+ */
+ if (gus_line_vol > 10)
+ mask &= ~0x01;
+ if (gus_mic_vol > 10)
+ mask |= 0x04;
+
+ if (recording_active)
+ {
+ /*
+ * Disable channel, if not selected for recording
+ */
+ if (!(gus_recmask & SOUND_MASK_LINE))
+ mask |= 0x01;
+ if (!(gus_recmask & SOUND_MASK_MIC))
+ mask &= ~0x04;
+ }
+
+ mix_image &= ~0x07;
+ mix_image |= mask & 0x07;
+ OUTB (mix_image, u_Mixer);
+
+ RESTORE_INTR (flags);
+}
+
+int
+gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
+ SOUND_MASK_SYNTH|SOUND_MASK_PCM)
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ gus_recmask = IOCTL_IN (arg) & MIX_DEVS;
+ if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+ gus_recmask = SOUND_MASK_MIC;
+ /* Note! Input volumes are updated during next open for recording */
+ return IOCTL_OUT (arg, gus_recmask);
+ break;
+
+ case SOUND_MIXER_MIC:
+ {
+ int vol = IOCTL_IN (arg) & 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_mic_vol = vol;
+ set_input_volumes ();
+ return IOCTL_OUT (arg, vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_LINE:
+ {
+ int vol = IOCTL_IN (arg) & 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_line_vol = vol;
+ set_input_volumes ();
+ return IOCTL_OUT (arg, vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_PCM:
+ gus_pcm_volume = IOCTL_IN (arg) & 0xff;
+ if (gus_pcm_volume < 0)
+ gus_pcm_volume = 0;
+ if (gus_pcm_volume > 100)
+ gus_pcm_volume = 100;
+ gus_sampling_update_volume ();
+ return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ {
+ int voice;
+
+ gus_wave_volume = IOCTL_IN (arg) & 0xff;
+
+ if (gus_wave_volume < 0)
+ gus_wave_volume = 0;
+ if (gus_wave_volume > 100)
+ gus_wave_volume = 100;
+
+ if (active_device == GUS_DEV_WAVE)
+ for (voice = 0; voice < nr_voices; voice++)
+ dynamic_volume_change (voice); /* Apply the new vol */
+
+ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
+ }
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, gus_recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8));
+ break;
+
+ case SOUND_MIXER_PCM:
+ return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations gus_mixer_operations =
+{
+ gus_default_mixer_ioctl
+};
+
+static long
+gus_default_mixer_init (long mem_start)
+{
+ if (num_mixers < MAX_MIXER_DEV) /*
+ * Don't install if there is another
+ * mixer
+ */
+ mixer_devs[num_mixers++] = &gus_mixer_operations;
+
+ return mem_start;
+}
+
+long
+gus_wave_init (long mem_start, int irq, int dma)
+{
+ unsigned long flags;
+ unsigned char val;
+ char *model_num = "2.4";
+ int gus_type = 0x24; /* 2.4 */
+ int mixer_type = 0;
+
+ /*
+ * Try to identify the GUS model.
+ *
+ * Versions < 3.6 don't have the digital ASIC. Try to probe it first.
+ */
+
+ DISABLE_INTR (flags);
+ OUTB (0x20, gus_base + 0x0f);
+ val = INB (gus_base + 0x0f);
+ RESTORE_INTR (flags);
+
+ if (val != 0xff && (val & 0x06)) /* Should be 0x02?? */
+ {
+ /*
+ * It has the digital ASIC so the card is at least v3.4.
+ * Next try to detect the true model.
+ */
+
+ val = INB (u_MixSelect);
+
+ /*
+ * Value 255 means pre-3.7 which don't have mixer.
+ * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
+ * 10 and above is GUS MAX which has the CS4231 codec/mixer.
+ *
+ * Sorry. No GUS max support yet but it should be available
+ * soon after the SDK for GUS MAX is available.
+ */
+
+ if (val == 255 || val < 5)
+ {
+ model_num = "3.4";
+ gus_type = 0x34;
+ }
+ else if (val < 10)
+ {
+ model_num = "3.7";
+ gus_type = 0x37;
+ mixer_type = ICS2101;
+ }
+ else
+ {
+ model_num = "MAX";
+ gus_type = 0x40;
+ mixer_type = CS4231;
+#ifndef EXCLUDE_GUSMAX
+ {
+ unsigned char max_config = 0x40; /* Codec enable */
+
+ if (dma > 3)
+ max_config |= 0x30; /* 16 bit playback and capture DMAs */
+
+ max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
+
+ OUTB (max_config, gus_base + 0x106); /* UltraMax control */
+ }
+
+ if (ad1848_detect (gus_base + 0x10c))
+ {
+ gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+ have_gus_max = 1;
+ ad1848_init ("GUS MAX", gus_base + 0x10c,
+ -irq,
+ dma,
+ dma);
+ }
+ else
+ printk ("[Where's the CS4231?]");
+#endif
+ }
+ }
+ else
+ {
+ /*
+ * ASIC not detected so the card must be 2.2 or 2.4.
+ * There could still be the 16-bit/mixer daughter card.
+ * It has the same codec/mixer than MAX.
+ * At this time there is no support for it but it will appear soon.
+ */
+ }
+
+
+ printk (" <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
+
+#ifndef SCO
+ sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
+#endif
+
+ if (irq < 0 || irq > 15)
+ {
+ printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
+ return mem_start;
+ }
+
+ if (dma < 0 || dma > 7)
+ {
+ printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma);
+ return mem_start;
+ }
+
+ gus_irq = irq;
+ gus_dma = dma;
+
+ if (num_synths >= MAX_SYNTH_DEV)
+ printk ("GUS Error: Too many synthesizers\n");
+ else
+ {
+ voice_alloc = &guswave_operations.alloc;
+ synth_devs[num_synths++] = &guswave_operations;
+ }
+
+ PERMANENT_MALLOC (struct patch_info *, samples,
+ (MAX_SAMPLE + 1) * sizeof (*samples), mem_start);
+
+ reset_sample_memory ();
+
+ gus_initialize ();
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations;
+ audio_devs[gus_devnum]->dmachan = dma;
+ audio_devs[gus_devnum]->buffcount = 1;
+ audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
+ }
+ else
+ printk ("GUS: Too many PCM devices available\n");
+
+ /*
+ * Mixer dependent initialization.
+ */
+
+ switch (mixer_type)
+ {
+ case ICS2101:
+ gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+ return ics2101_mixer_init (mem_start);
+
+ case CS4231:
+ /* Initialized elsewhere (ad1848.c) */
+ default:
+ return gus_default_mixer_init (mem_start);
+ }
+
+ return mem_start;
+}
+
+static void
+do_loop_irq (int voice)
+{
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ tmp = gus_read8 (0x00);
+ tmp &= ~0x20; /*
+ * Disable wave IRQ for this_one voice
+ */
+ gus_write8 (0x00, tmp);
+
+ mode = voices[voice].loop_irq_mode;
+ voices[voice].loop_irq_mode = 0;
+ parm = voices[voice].loop_irq_parm;
+
+ switch (mode)
+ {
+
+ case LMODE_FINISH: /*
+ * Final loop finished, shoot volume down
+ */
+
+ if ((int) (gus_read16 (0x09) >> 4) < 100) /*
+ * Get current volume
+ */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ break;
+ }
+ gus_ramp_range (65, 4065);
+ gus_ramp_rate (0, 63); /*
+ * Fastest possible rate
+ */
+ gus_rampon (0x20 | 0x40); /*
+ * Ramp down, once, irq
+ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ break;
+
+ case LMODE_PCM_STOP:
+ pcm_active = 0; /* Signal to the play_next_pcm_block routine */
+ case LMODE_PCM:
+ {
+ int orig_qlen = pcm_qlen;
+ int flag; /* 0 or 2 */
+
+ pcm_qlen--;
+ pcm_head = (pcm_head + 1) % pcm_nblk;
+ if (pcm_qlen)
+ {
+ play_next_pcm_block ();
+ }
+ else
+ { /* Underrun. Just stop the voice */
+ gus_voice_off ();
+ gus_rampoff ();
+ pcm_active = 0;
+ }
+
+ /*
+ * If the queue was full before this interrupt, the DMA transfer was
+ * suspended. Let it continue now.
+ */
+ if (dma_active)
+ {
+ if (pcm_qlen == 0)
+ flag = 1; /* Underflow */
+ else
+ flag = 0;
+ dma_active = 0;
+ }
+ else
+ flag = 2; /* Just notify the dmabuf.c */
+ DMAbuf_outputintr (gus_devnum, flag);
+ }
+ break;
+
+ default:;
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+do_volume_irq (int voice)
+{
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ gus_select_voice (voice);
+
+ tmp = gus_read8 (0x0d);
+ tmp &= ~0x20; /*
+ * Disable volume ramp IRQ
+ */
+ gus_write8 (0x0d, tmp);
+
+ mode = voices[voice].volume_irq_mode;
+ voices[voice].volume_irq_mode = 0;
+ parm = voices[voice].volume_irq_parm;
+
+ switch (mode)
+ {
+ case VMODE_HALT: /*
+ * Decay phase finished
+ */
+ RESTORE_INTR (flags);
+ gus_voice_init (voice);
+ break;
+
+ case VMODE_ENVELOPE:
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ step_envelope (voice);
+ break;
+
+ case VMODE_START_NOTE:
+ RESTORE_INTR (flags);
+ guswave_start_note2 (voices[voice].dev_pending, voice,
+ voices[voice].note_pending, voices[voice].volume_pending);
+ if (voices[voice].kill_pending)
+ guswave_kill_note (voices[voice].dev_pending, voice,
+ voices[voice].note_pending, 0);
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ }
+ break;
+
+ default:;
+ }
+}
+
+void
+gus_voice_irq (void)
+{
+ unsigned long wave_ignore = 0, volume_ignore = 0;
+ unsigned long voice_bit;
+
+ unsigned char src, voice;
+
+ while (1)
+ {
+ src = gus_read8 (0x0f); /*
+ * Get source info
+ */
+ voice = src & 0x1f;
+ src &= 0xc0;
+
+ if (src == (0x80 | 0x40))
+ return; /*
+ * No interrupt
+ */
+
+ voice_bit = 1 << voice;
+
+ if (!(src & 0x80)) /*
+ * Wave IRQ pending
+ */
+ if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ wave_ignore |= voice_bit;
+ do_loop_irq (voice);
+ }
+
+ if (!(src & 0x40)) /*
+ * Volume IRQ pending
+ */
+ if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ volume_ignore |= voice_bit;
+ do_volume_irq (voice);
+ }
+ }
+}
+
+void
+guswave_dma_irq (void)
+{
+ unsigned char status;
+
+ status = gus_look8 (0x41); /* Get DMA IRQ Status */
+ if (status & 0x40) /* DMA interrupt pending */
+ switch (active_device)
+ {
+ case GUS_DEV_WAVE:
+ if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag))
+ WAKE_UP (dram_sleeper, dram_sleep_flag);
+ break;
+
+ case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */
+ gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
+ pcm_current_count,
+ pcm_current_intrflag, 1);
+ break;
+
+ case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */
+ if (pcm_qlen < pcm_nblk)
+ {
+ int flag = (1 - dma_active) * 2; /* 0 or 2 */
+
+ if (pcm_qlen == 0)
+ flag = 1; /* Underrun */
+ dma_active = 0;
+ DMAbuf_outputintr (gus_devnum, flag);
+ }
+ break;
+
+ default:;
+ }
+
+ status = gus_look8 (0x49); /*
+ * Get Sampling IRQ Status
+ */
+ if (status & 0x40) /*
+ * Sampling Irq pending
+ */
+ {
+ DMAbuf_inputintr (gus_devnum);
+ }
+
+}
+
+#endif
diff --git a/sys/i386/isa/sound/ics2101.c b/sys/i386/isa/sound/ics2101.c
new file mode 100644
index 000000000000..4147a070fd8e
--- /dev/null
+++ b/sys/i386/isa/sound/ics2101.c
@@ -0,0 +1,263 @@
+/*
+ * sound/ics2101.c
+ *
+ * Driver for the ICS2101 mixer of GUS v3.7.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#include "ultrasound.h"
+#include "gus_hw.h"
+
+#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
+ SOUND_MASK_SYNTH| \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+extern int gus_base;
+static int volumes[ICS_MIXDEVS];
+static int left_fix[ICS_MIXDEVS] =
+{1, 1, 1, 2, 1, 2};
+static int right_fix[ICS_MIXDEVS] =
+{2, 2, 2, 1, 2, 1};
+
+static int
+scale_vol (int vol)
+{
+#if 1
+ /*
+ * Experimental volume scaling by Risto Kankkunen.
+ * This should give smoother volume response than just
+ * a plain multiplication.
+ */
+ int e;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ vol = (31 * vol + 50) / 100;
+ e = 0;
+ if (vol)
+ {
+ while (vol < 16)
+ {
+ vol <<= 1;
+ e--;
+ }
+ vol -= 16;
+ e += 7;
+ }
+ return ((e << 4) + vol);
+#else
+ return ((vol * 127) + 50) / 100;
+#endif
+}
+
+static void
+write_mix (int dev, int chn, int vol)
+{
+ int *selector;
+ unsigned long flags;
+ int ctrl_addr = dev << 3;
+ int attn_addr = dev << 3;
+
+ vol = scale_vol (vol);
+
+ if (chn == CHN_LEFT)
+ {
+ selector = left_fix;
+ ctrl_addr |= 0x00;
+ attn_addr |= 0x02;
+ }
+ else
+ {
+ selector = right_fix;
+ ctrl_addr |= 0x01;
+ attn_addr |= 0x03;
+ }
+
+ DISABLE_INTR (flags);
+ OUTB (ctrl_addr, u_MixSelect);
+ OUTB (selector[dev], u_MixData);
+ OUTB (attn_addr, u_MixSelect);
+ OUTB ((unsigned char) vol, u_MixData);
+ RESTORE_INTR (flags);
+}
+
+static int
+set_volumes (int dev, int vol)
+{
+ int left = vol & 0x00ff;
+ int right = (vol >> 8) & 0x00ff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+
+ write_mix (dev, CHN_LEFT, left);
+ write_mix (dev, CHN_RIGHT, right);
+
+ vol = left + (right << 8);
+ volumes[dev] = vol;
+ return vol;
+}
+
+static int
+ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl (dev, cmd, arg);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, set_volumes (DEV_MIC, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_CD:
+ return IOCTL_OUT (arg, set_volumes (DEV_CD, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, set_volumes (DEV_LINE, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, set_volumes (DEV_GF1, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_VOLUME:
+ return IOCTL_OUT (arg, set_volumes (DEV_VOL, IOCTL_IN (arg)));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl (dev, cmd, arg);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD |
+ SOUND_MASK_SYNTH | SOUND_MASK_VOLUME |
+ SOUND_MASK_MIC);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, volumes[DEV_MIC]);
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, volumes[DEV_LINE]);
+ break;
+
+ case SOUND_MIXER_CD:
+ return IOCTL_OUT (arg, volumes[DEV_CD]);
+ break;
+
+ case SOUND_MIXER_VOLUME:
+ return IOCTL_OUT (arg, volumes[DEV_VOL]);
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, volumes[DEV_GF1]);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations ics2101_mixer_operations =
+{
+ ics2101_mixer_ioctl
+};
+
+long
+ics2101_mixer_init (long mem_start)
+{
+ int i;
+
+ if (num_mixers < MAX_MIXER_DEV)
+ {
+ mixer_devs[num_mixers++] = &ics2101_mixer_operations;
+
+ /*
+ * Some GUS v3.7 cards had some channels flipped. Disable
+ * the flipping feature if the model id is other than 5.
+ */
+
+ if (INB (u_MixSelect) != 5)
+ {
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ left_fix[i] = 1;
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ right_fix[i] = 2;
+ }
+
+ set_volumes (DEV_GF1, 0x5a5a);
+ set_volumes (DEV_CD, 0x5a5a);
+ set_volumes (DEV_MIC, 0x0000);
+ set_volumes (DEV_LINE, 0x5a5a);
+ set_volumes (DEV_VOL, 0x5a5a);
+ set_volumes (DEV_UNUSED, 0x0000);
+ }
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h
new file mode 100644
index 000000000000..533d60a22662
--- /dev/null
+++ b/sys/i386/isa/sound/local.h
@@ -0,0 +1,27 @@
+/* Generated by configure. Don't edit!!!! */
+
+#define KERNEL_SOUNDCARD
+#undef EXCLUDE_PAS
+#undef EXCLUDE_SB
+#undef EXCLUDE_ADLIB
+#undef EXCLUDE_GUS
+#define EXCLUDE_MPU401
+#define EXCLUDE_UART6850
+#define EXCLUDE_PSS
+#undef EXCLUDE_GUS16
+#undef EXCLUDE_GUSMAX
+#undef EXCLUDE_MSS
+#undef EXCLUDE_SBPRO
+#undef EXCLUDE_SB16
+#undef EXCLUDE_AUDIO
+#undef EXCLUDE_MIDI
+#undef EXCLUDE_YM3812
+#undef EXCLUDE_SEQUENCER
+
+#define DSP_BUFFSIZE 32768
+#define SELECTED_SOUND_OPTIONS 0xffffff8f
+#define SOUND_VERSION_STRING "2.90-2"
+#define SOUND_CONFIG_DATE "Thu Sep 29 15:33:39 PDT 1994"
+#define SOUND_CONFIG_BY "swallace"
+#define SOUND_CONFIG_HOST "pal-r32-a07b.slip.nts.uci.edu"
+#define SOUND_CONFIG_DOMAIN ""
diff --git a/sys/i386/isa/sound/midi_ctrl.h b/sys/i386/isa/sound/midi_ctrl.h
new file mode 100644
index 000000000000..616b5914c6e2
--- /dev/null
+++ b/sys/i386/isa/sound/midi_ctrl.h
@@ -0,0 +1,22 @@
+static unsigned char ctrl_def_values[128] =
+{
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 0 to 7 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 8 to 15 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 16 to 23 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 24 to 31 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
+};
diff --git a/sys/i386/isa/sound/midi_synth.c b/sys/i386/isa/sound/midi_synth.c
new file mode 100644
index 000000000000..fd6d8bde62cd
--- /dev/null
+++ b/sys/i386/isa/sound/midi_synth.c
@@ -0,0 +1,474 @@
+/*
+ * sound/midi_synth.c
+ *
+ * High level midi sequencer manager for dumb MIDI interfaces.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI)
+
+#define _MIDI_SYNTH_C_
+
+DEFINE_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
+
+#include "midi_synth.h"
+
+static int midi2synth[MAX_MIDI_DEV];
+static unsigned char prev_out_status[MAX_MIDI_DEV];
+
+static void
+midi_outc (int midi_dev, int data)
+{
+ int timeout;
+
+ for (timeout = 0; timeout < 32000; timeout++)
+ if (midi_devs[midi_dev]->putc (midi_dev, (unsigned char) (data & 0xff)))
+ {
+ if (data & 0x80) /*
+ * Status byte
+ */
+ prev_out_status[midi_dev] =
+ (unsigned char) (data & 0xff); /*
+ * Store for running status
+ */
+ return; /*
+ * Mission complete
+ */
+ }
+
+ /*
+ * Sorry! No space on buffers.
+ */
+ printk ("Midi send timed out\n");
+}
+
+static int
+prefix_cmd (int midi_dev, unsigned char status)
+{
+ if (midi_devs[midi_dev]->prefix_cmd == NULL)
+ return 1;
+
+ return midi_devs[midi_dev]->prefix_cmd (midi_dev, status);
+}
+
+static void
+midi_synth_input (int dev, unsigned char data)
+{
+ int orig_dev;
+
+ if (dev < 0 || dev > num_synths)
+ return;
+
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
+
+ orig_dev = midi2synth[dev];
+
+}
+
+static void
+midi_synth_output (int dev)
+{
+ /*
+ * Currently NOP
+ */
+}
+
+int
+midi_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ /*
+ * int orig_dev = synth_devs[dev]->midi_dev;
+ */
+
+ switch (cmd)
+ {
+
+ case SNDCTL_SYNTH_INFO:
+ IOCTL_TO_USER ((char *) arg, 0, synth_devs[dev]->info,
+ sizeof (struct synth_info));
+
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+int
+midi_synth_kill_note (int dev, int channel, int note, int velocity)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
+ { /*
+ * Use running status
+ */
+ if (!prefix_cmd (orig_dev, note))
+ return 0;
+
+ midi_outc (orig_dev, note);
+
+ if (msg == 0x90) /*
+ * Running status = Note on
+ */
+ midi_outc (orig_dev, 0);/*
+ * Note on with velocity 0 == note
+ * off
+ */
+ else
+ midi_outc (orig_dev, velocity);
+ }
+ else
+ {
+ if (velocity == 64)
+ {
+ if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, 0); /*
+ * Zero G
+ */
+ }
+ else
+ {
+ if (!prefix_cmd (orig_dev, 0x80 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x80 | (channel & 0x0f)); /*
+ * Note off
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ }
+
+ return 0;
+}
+
+int
+midi_synth_set_instr (int dev, int channel, int instr_no)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ if (instr_no < 0 || instr_no > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+
+ if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /*
+ * Program change
+ */
+ midi_outc (orig_dev, instr_no);
+
+ return 0;
+}
+
+int
+midi_synth_start_note (int dev, int channel, int note, int velocity)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (chn == channel && msg == 0x90)
+ { /*
+ * Use running status
+ */
+ if (!prefix_cmd (orig_dev, note))
+ return 0;
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ else
+ {
+ if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ return 0;
+}
+
+void
+midi_synth_reset (int dev)
+{
+}
+
+int
+midi_synth_open (int dev, int mode)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int err;
+
+ if (orig_dev < 0 || orig_dev > num_midis)
+ return RET_ERROR (ENXIO);
+
+ midi2synth[orig_dev] = dev;
+ prev_out_status[orig_dev] = 0;
+
+ if ((err = midi_devs[orig_dev]->open (orig_dev, mode,
+ midi_synth_input, midi_synth_output)) < 0)
+ return err;
+
+ return 1;
+}
+
+void
+midi_synth_close (int dev)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ /*
+ * Shut up the synths by sending just single active sensing message.
+ */
+ midi_devs[orig_dev]->putc (orig_dev, 0xfe);
+
+ midi_devs[orig_dev]->close (orig_dev);
+}
+
+void
+midi_synth_hw_control (int dev, unsigned char *event)
+{
+}
+
+int
+midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ struct sysex_info sysex;
+ int i;
+ unsigned long left, src_offs, eox_seen = 0;
+ int first_byte = 1;
+
+ if (!prefix_cmd (orig_dev, 0xf0))
+ return 0;
+
+ if (format != SYSEX_PATCH)
+ {
+ printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < sizeof (struct sysex_info))
+ {
+ printk ("MIDI Error: Patch header too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ count -= sizeof (struct sysex_info);
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ COPY_FROM_USER (&((char *) &sysex)[offs], addr, offs, sizeof (struct sysex_info) - offs);
+
+ if (count < sysex.len)
+ {
+ printk ("MIDI Warning: Sysex record too short (%d<%d)\n",
+ count, (int) sysex.len);
+ sysex.len = count;
+ }
+
+ left = sysex.len;
+ src_offs = 0;
+
+ RESET_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
+
+ for (i = 0; i < left && !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag); i++)
+ {
+ unsigned char data;
+
+ GET_BYTE_FROM_USER (data, addr, sizeof (struct sysex_info) + i);
+
+ if (first_byte && data != 0xf0)
+ midi_outc (orig_dev, 0xf0); /* Sysex start */
+
+ eox_seen = (data == 0xf7);/*
+ * Last byte was end of sysex
+ */
+
+ if (i == 0)
+ {
+ if (data != 0xf0) /*
+ * Sysex start
+ */
+ return RET_ERROR (EINVAL);
+ }
+
+ while (!midi_devs[orig_dev]->putc (orig_dev, (unsigned char) (data & 0xff)) &&
+ !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag))
+ DO_SLEEP (sysex_sleeper, sysex_sleep_flag, 1); /* Wait for timeout */
+
+ if (!first_byte && data & 0x80)
+ return 0;
+ first_byte = 0;
+ }
+
+ if (!eox_seen)
+ midi_outc (orig_dev, 0xf7);
+ return 0;
+}
+
+void
+midi_synth_panning (int dev, int channel, int pressure)
+{
+}
+
+void
+midi_synth_aftertouch (int dev, int channel, int pressure)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (pressure < 0 || pressure > 127)
+ return;
+ if (channel < 0 || channel > 15)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xd0 || chn != channel) /*
+ * Test for running status
+ */
+ {
+ if (!prefix_cmd (orig_dev, 0xd0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xd0 | (channel & 0x0f)); /*
+ * Channel pressure
+ */
+ }
+ else if (!prefix_cmd (orig_dev, pressure))
+ return;
+ midi_outc (orig_dev, pressure);
+}
+
+void
+midi_synth_controller (int dev, int channel, int ctrl_num, int value)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int chn, msg;
+
+ if (ctrl_num < 1 || ctrl_num > 127)
+ return; /* NOTE! Controller # 0 ignored */
+ if (channel < 0 || channel > 15)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xb0 || chn != channel)
+ {
+ if (!prefix_cmd (orig_dev, 0xb0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xb0 | (channel & 0x0f));
+ }
+ else if (!prefix_cmd (orig_dev, ctrl_num))
+ return;
+
+ midi_outc (orig_dev, ctrl_num);
+ midi_outc (orig_dev, value & 0x7f);
+}
+
+int
+midi_synth_patchmgr (int dev, struct patmgr_info *rec)
+{
+ return RET_ERROR (EINVAL);
+}
+
+void
+midi_synth_bender (int dev, int channel, int value)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, prev_chn;
+
+ if (channel < 0 || channel > 15)
+ return;
+
+ if (value < 0 || value > 16383)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ prev_chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xd0 || prev_chn != channel) /*
+ * * Test for running status */
+ {
+ if (!prefix_cmd (orig_dev, 0xe0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xe0 | (channel & 0x0f));
+ }
+ else if (!prefix_cmd (orig_dev, value & 0x7f))
+ return;
+
+ midi_outc (orig_dev, value & 0x7f);
+ midi_outc (orig_dev, (value >> 7) & 0x7f);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/midi_synth.h b/sys/i386/isa/sound/midi_synth.h
new file mode 100644
index 000000000000..04075e294ca6
--- /dev/null
+++ b/sys/i386/isa/sound/midi_synth.h
@@ -0,0 +1,45 @@
+int midi_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg);
+int midi_synth_kill_note (int dev, int channel, int note, int velocity);
+int midi_synth_set_instr (int dev, int channel, int instr_no);
+int midi_synth_start_note (int dev, int channel, int note, int volume);
+void midi_synth_reset (int dev);
+int midi_synth_open (int dev, int mode);
+void midi_synth_close (int dev);
+void midi_synth_hw_control (int dev, unsigned char *event);
+int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag);
+void midi_synth_panning (int dev, int channel, int pressure);
+void midi_synth_aftertouch (int dev, int channel, int pressure);
+void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
+int midi_synth_patchmgr (int dev, struct patmgr_info *rec);
+void midi_synth_bender (int dev, int chn, int value);
+
+
+#ifndef _MIDI_SYNTH_C_
+static struct synth_info std_synth_info =
+{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
+
+static struct synth_operations std_midi_synth =
+{
+ &std_synth_info,
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ midi_synth_open,
+ midi_synth_close,
+ midi_synth_ioctl,
+ midi_synth_kill_note,
+ midi_synth_start_note,
+ midi_synth_set_instr,
+ midi_synth_reset,
+ midi_synth_hw_control,
+ midi_synth_load_patch,
+ midi_synth_aftertouch,
+ midi_synth_controller,
+ midi_synth_panning,
+ NULL,
+ midi_synth_patchmgr,
+ midi_synth_bender
+};
+#endif
diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c
new file mode 100644
index 000000000000..ae0ddc3ea54b
--- /dev/null
+++ b/sys/i386/isa/sound/midibuf.c
@@ -0,0 +1,443 @@
+/*
+ * sound/midibuf.c
+ *
+ * Device file manager for /dev/midi#
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI)
+
+/*
+ * Don't make MAX_QUEUE_SIZE larger than 4000
+ */
+
+#define MAX_QUEUE_SIZE 4000
+
+DEFINE_WAIT_QUEUES (midi_sleeper[MAX_MIDI_DEV], midi_sleep_flag[MAX_MIDI_DEV]);
+DEFINE_WAIT_QUEUES (input_sleeper[MAX_MIDI_DEV], input_sleep_flag[MAX_MIDI_DEV]);
+
+struct midi_buf
+ {
+ int len, head, tail;
+ unsigned char queue[MAX_QUEUE_SIZE];
+ };
+
+struct midi_parms
+ {
+ int prech_timeout; /*
+ * Timeout before the first ch
+ */
+ };
+
+static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] =
+{NULL};
+static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] =
+{NULL};
+static struct midi_parms parms[MAX_MIDI_DEV];
+
+static void midi_poll (unsigned long dummy);
+
+DEFINE_TIMER (poll_timer, midi_poll);
+static volatile int open_devs = 0;
+
+#define DATA_AVAIL(q) (q->len)
+#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
+
+#define QUEUE_BYTE(q, data) \
+ if (SPACE_AVAIL(q)) \
+ { \
+ unsigned long flags; \
+ DISABLE_INTR(flags); \
+ q->queue[q->tail] = (data); \
+ q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
+ RESTORE_INTR(flags); \
+ }
+
+#define REMOVE_BYTE(q, data) \
+ if (DATA_AVAIL(q)) \
+ { \
+ unsigned long flags; \
+ DISABLE_INTR(flags); \
+ data = q->queue[q->head]; \
+ q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
+ RESTORE_INTR(flags); \
+ }
+
+void
+drain_midi_queue (int dev)
+{
+
+ /*
+ * Give the Midi driver time to drain its output queues
+ */
+
+ if (midi_devs[dev]->buffer_status != NULL)
+ while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) &&
+ midi_devs[dev]->buffer_status (dev))
+ DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], HZ / 10);
+}
+
+static void
+midi_input_intr (int dev, unsigned char data)
+{
+ if (midi_in_buf[dev] == NULL)
+ return;
+
+ if (data == 0xfe) /*
+ * Active sensing
+ */
+ return; /*
+ * Ignore
+ */
+
+ if (SPACE_AVAIL (midi_in_buf[dev]))
+ {
+ QUEUE_BYTE (midi_in_buf[dev], data);
+ if (SOMEONE_WAITING (input_sleeper[dev], input_sleep_flag[dev]))
+ WAKE_UP (input_sleeper[dev], input_sleep_flag[dev]);
+ }
+
+}
+
+static void
+midi_output_intr (int dev)
+{
+ /*
+ * Currently NOP
+ */
+}
+
+static void
+midi_poll (unsigned long dummy)
+{
+ unsigned long flags;
+ int dev;
+
+ DISABLE_INTR (flags);
+ if (open_devs)
+ {
+ for (dev = 0; dev < num_midis; dev++)
+ if (midi_out_buf[dev] != NULL)
+ {
+ while (DATA_AVAIL (midi_out_buf[dev]) &&
+ midi_devs[dev]->putc (dev,
+ midi_out_buf[dev]->queue[midi_out_buf[dev]->head]))
+ {
+ midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
+ midi_out_buf[dev]->len--;
+ }
+
+ if (DATA_AVAIL (midi_out_buf[dev]) < 100 &&
+ SOMEONE_WAITING (midi_sleeper[dev], midi_sleep_flag[dev]))
+ WAKE_UP (midi_sleeper[dev], midi_sleep_flag[dev]);
+ }
+ ACTIVATE_TIMER (poll_timer, midi_poll, 1); /*
+ * Come back later
+ */
+ }
+ RESTORE_INTR (flags);
+}
+
+int
+MIDIbuf_open (int dev, struct fileinfo *file)
+{
+ int mode, err;
+ unsigned long flags;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (num_midis > MAX_MIDI_DEV)
+ {
+ printk ("Sound: FATAL ERROR: Too many midi interfaces\n");
+ num_midis = MAX_MIDI_DEV;
+ }
+
+ if (dev < 0 || dev >= num_midis)
+ {
+ printk ("Sound: Nonexistent MIDI interface %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ /*
+ * Interrupts disabled. Be careful
+ */
+
+ DISABLE_INTR (flags);
+ if ((err = midi_devs[dev]->open (dev, mode,
+ midi_input_intr, midi_output_intr)) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+
+ parms[dev].prech_timeout = 0;
+
+ RESET_WAIT_QUEUE (midi_sleeper[dev], midi_sleep_flag[dev]);
+ RESET_WAIT_QUEUE (input_sleeper[dev], input_sleep_flag[dev]);
+
+ midi_in_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf));
+
+ if (midi_in_buf[dev] == NULL)
+ {
+ printk ("midi: Can't allocate buffer\n");
+ midi_devs[dev]->close (dev);
+ RESTORE_INTR (flags);
+ return RET_ERROR (EIO);
+ }
+ midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
+
+ midi_out_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf));
+
+ if (midi_out_buf[dev] == NULL)
+ {
+ printk ("midi: Can't allocate buffer\n");
+ midi_devs[dev]->close (dev);
+ KERNEL_FREE (midi_in_buf[dev]);
+ midi_in_buf[dev] = NULL;
+ RESTORE_INTR (flags);
+ return RET_ERROR (EIO);
+ }
+ midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
+ if (!open_devs)
+ ACTIVATE_TIMER (poll_timer, midi_poll, 1); /*
+ * Come back later
+ */
+ open_devs++;
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+void
+MIDIbuf_release (int dev, struct fileinfo *file)
+{
+ int mode;
+ unsigned long flags;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Wait until the queue is empty
+ */
+
+ if (mode != OPEN_READ)
+ {
+ midi_devs[dev]->putc (dev, 0xfe); /*
+ * Active sensing to shut the
+ * devices
+ */
+
+ while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) &&
+ DATA_AVAIL (midi_out_buf[dev]))
+ DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); /*
+ * Sync
+ */
+
+ drain_midi_queue (dev); /*
+ * Ensure the output queues are empty
+ */
+ }
+
+ midi_devs[dev]->close (dev);
+ KERNEL_FREE (midi_in_buf[dev]);
+ KERNEL_FREE (midi_out_buf[dev]);
+ midi_in_buf[dev] = NULL;
+ midi_out_buf[dev] = NULL;
+ open_devs--;
+ RESTORE_INTR (flags);
+}
+
+int
+MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+ int c, n, i;
+ unsigned char tmp_data;
+
+ dev = dev >> 4;
+
+ if (!count)
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ c = 0;
+
+ while (c < count)
+ {
+ n = SPACE_AVAIL (midi_out_buf[dev]);
+
+ if (n == 0) /*
+ * No space just now. We have to sleep
+ */
+ {
+ DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0);
+ if (PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]))
+ {
+ RESTORE_INTR (flags);
+ return RET_ERROR (EINTR);
+ }
+
+ n = SPACE_AVAIL (midi_out_buf[dev]);
+ }
+
+ if (n > (count - c))
+ n = count - c;
+
+ for (i = 0; i < n; i++)
+ {
+ COPY_FROM_USER (&tmp_data, buf, c, 1);
+ QUEUE_BYTE (midi_out_buf[dev], tmp_data);
+ c++;
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return c;
+}
+
+
+int
+MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int n, c = 0;
+ unsigned long flags;
+ unsigned char tmp_data;
+
+ dev = dev >> 4;
+
+ DISABLE_INTR (flags);
+
+ if (!DATA_AVAIL (midi_in_buf[dev])) /*
+ * No data yet, wait
+ */
+ {
+ DO_SLEEP (input_sleeper[dev], input_sleep_flag[dev],
+ parms[dev].prech_timeout);
+ if (PROCESS_ABORTING (input_sleeper[dev], input_sleep_flag[dev]))
+ c = RET_ERROR (EINTR); /*
+ * The user is getting restless
+ */
+ }
+
+ if (c == 0 && DATA_AVAIL (midi_in_buf[dev])) /*
+ * Got some bytes
+ */
+ {
+ n = DATA_AVAIL (midi_in_buf[dev]);
+ if (n > count)
+ n = count;
+ c = 0;
+
+ while (c < n)
+ {
+ REMOVE_BYTE (midi_in_buf[dev], tmp_data);
+ COPY_TO_USER (buf, c, &tmp_data, 1);
+ c++;
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return c;
+}
+
+int
+MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ int val;
+
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+
+ case SNDCTL_MIDI_PRETIME:
+ val = IOCTL_IN (arg);
+ if (val < 0)
+ val = 0;
+
+ val = (HZ * val) / 10;
+ parms[dev].prech_timeout = val;
+ return IOCTL_OUT (arg, val);
+ break;
+
+ default:
+ return midi_devs[dev]->ioctl (dev, cmd, arg);
+ }
+}
+
+#ifdef ALLOW_SELECT
+int
+MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!DATA_AVAIL (midi_in_buf[dev]))
+ {
+ input_sleep_flag[dev].mode = WK_SLEEP;
+ select_wait (&input_sleeper[dev], wait);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_OUT:
+ if (SPACE_AVAIL (midi_out_buf[dev]))
+ {
+ midi_sleep_flag[dev].mode = WK_SLEEP;
+ select_wait (&midi_sleeper[dev], wait);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif /* ALLOW_SELECT */
+
+long
+MIDIbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c
new file mode 100644
index 000000000000..40be06638ac5
--- /dev/null
+++ b/sys/i386/isa/sound/mpu401.c
@@ -0,0 +1,1722 @@
+/*
+ * sound/mpu401.c
+ *
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ *
+ * 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.
+ *
+ */
+
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+
+static int init_sequence[20]; /* NOTE! pos 0 = len, start pos 1. */
+static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
+
+struct mpu_config
+ {
+ int base; /*
+ * I/O base
+ */
+ int irq;
+ int opened; /*
+ * Open mode
+ */
+ int devno;
+ int synthno;
+ int uart_mode;
+ int initialized;
+ int mode;
+#define MODE_MIDI 1
+#define MODE_SYNTH 2
+ unsigned char version, revision;
+ unsigned int capabilities;
+#define MPU_CAP_INTLG 0x10000000
+#define MPU_CAP_SYNC 0x00000010
+#define MPU_CAP_FSK 0x00000020
+#define MPU_CAP_CLS 0x00000040
+#define MPU_CAP_SMPTE 0x00000080
+#define MPU_CAP_2PORT 0x00000001
+ int timer_flag;
+
+#define MBUF_MAX 10
+#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
+ {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
+ int m_busy;
+ unsigned char m_buf[MBUF_MAX];
+ int m_ptr;
+ int m_state;
+ int m_left;
+ unsigned char last_status;
+ void (*inputintr) (int dev, unsigned char data);
+ unsigned short controls[32];
+ };
+
+#define DATAPORT(base) (base)
+#define COMDPORT(base) (base+1)
+#define STATPORT(base) (base+1)
+
+#define mpu401_status(base) INB(STATPORT(base))
+#define input_avail(base) (!(mpu401_status(base)&INPUT_AVAIL))
+#define output_ready(base) (!(mpu401_status(base)&OUTPUT_READY))
+#define write_command(base, cmd) OUTB(cmd, COMDPORT(base))
+#define read_data(base) INB(DATAPORT(base))
+
+#define write_data(base, byte) OUTB(byte, DATAPORT(base))
+
+#define OUTPUT_READY 0x40
+#define INPUT_AVAIL 0x80
+#define MPU_ACK 0xF7
+#define MPU_RESET 0xFF
+#define UART_MODE_ON 0x3F
+
+static struct mpu_config dev_conf[MAX_MIDI_DEV] =
+{
+ {0}};
+
+static int n_mpu_devs = 0;
+static int irq2dev[16];
+
+static int reset_mpu401 (struct mpu_config *devc);
+static void set_uart_mode (int dev, struct mpu_config *devc, int arg);
+static void mpu_timer_init (int midi_dev);
+static void mpu_timer_interrupt (void);
+static void timer_ext_event (struct mpu_config *devc, int event, int parm);
+
+static struct synth_info mpu_synth_info_proto =
+{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, SYNTH_CAP_INPUT};
+
+static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
+
+/*
+ * States for the input scanner
+ */
+
+#define ST_INIT 0 /* Ready for timing byte or msg */
+#define ST_TIMED 1 /* Leading timing byte rcvd */
+#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */
+
+#define ST_SYSMSG 100 /* System message (sysx etc). */
+#define ST_SYSEX 101 /* System exclusive msg */
+#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */
+#define ST_SONGSEL 103 /* Song select */
+#define ST_SONGPOS 104 /* Song position pointer */
+
+static unsigned char len_tab[] =/* # of data bytes following a status
+ */
+{
+ 2, /* 8x */
+ 2, /* 9x */
+ 2, /* Ax */
+ 2, /* Bx */
+ 1, /* Cx */
+ 1, /* Dx */
+ 2, /* Ex */
+ 0 /* Fx */
+};
+
+#define STORE(cmd) \
+if (devc->opened & OPEN_READ) \
+{ \
+ int len; \
+ unsigned char obuf[8]; \
+ cmd; \
+ seq_input_event(obuf, len); \
+}
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+static void
+do_midi_msg (struct mpu_config *devc, unsigned char *msg, int mlen)
+{
+ switch (msg[0] & 0xf0)
+ {
+ case 0x90:
+ if (msg[2] != 0)
+ {
+ STORE (SEQ_START_NOTE (devc->synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+ }
+ msg[2] = 64;
+
+ case 0x80:
+ STORE (SEQ_STOP_NOTE (devc->synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xA0:
+ STORE (SEQ_KEY_PRESSURE (devc->synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xB0:
+ /*
+ * Fix the controller value (combine MSB and LSB)
+ */
+ if (msg[1] < 64)
+ {
+ int ctrl = msg[1];
+
+ if (ctrl < 32)
+ {
+ devc->controls[ctrl] = (msg[2] & 0x7f) << 7;
+ }
+ else
+ {
+ ctrl -= 32;
+ devc->controls[ctrl] =
+ (devc->controls[ctrl] & ~0x7f) | (msg[2] & 0x7f);
+ }
+ STORE (SEQ_CONTROL (devc->synthno, msg[0] & 0x0f,
+ msg[1], devc->controls[ctrl]));
+ }
+ else
+ STORE (SEQ_CONTROL (devc->synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xC0:
+ STORE (SEQ_SET_PATCH (devc->synthno, msg[0] & 0x0f, msg[1]));
+ break;
+
+ case 0xD0:
+ STORE (SEQ_CHN_PRESSURE (devc->synthno, msg[0] & 0x0f, msg[1]));
+ break;
+
+ case 0xE0:
+ STORE (SEQ_BENDER (devc->synthno, msg[0] & 0x0f,
+ (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7)));
+ break;
+
+ default:
+ printk ("MPU: Unknown midi channel message %02x\n", msg[0]);
+ }
+}
+
+static int
+mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
+{
+ switch (devc->m_state)
+ {
+ case ST_INIT:
+ switch (midic)
+ {
+ case 0xf8:
+ /* Timer overflow */
+ break;
+
+ case 0xfc:
+ printk ("<all end>");
+ break;
+
+ case 0xfd:
+ if (devc->timer_flag)
+ mpu_timer_interrupt ();
+ break;
+
+ case 0xfe:
+ return MPU_ACK;
+ break;
+
+ case 0xf0:
+ case 0xf1:
+ case 0xf2:
+ case 0xf3:
+ case 0xf4:
+ case 0xf5:
+ case 0xf6:
+ case 0xf7:
+ printk ("<Trk data rq #%d>", midic & 0x0f);
+ break;
+
+ case 0xf9:
+ printk ("<conductor rq>");
+ break;
+
+ case 0xff:
+ devc->m_state = ST_SYSMSG;
+ break;
+
+ default:
+ if (midic <= 0xef)
+ {
+ /* printk("mpu time: %d ", midic); */
+ devc->m_state = ST_TIMED;
+ }
+ else
+ printk ("<MPU: Unknown event %02x> ", midic);
+ }
+ break;
+
+ case ST_TIMED:
+ {
+ int msg = (midic & 0xf0) >> 4;
+
+ devc->m_state = ST_DATABYTE;
+ if (msg < 8) /* Data byte */
+ {
+ /* printk("midi msg (running status) "); */
+ msg = (devc->last_status & 0xf0) >> 4;
+ msg -= 8;
+ devc->m_left = len_tab[msg] - 1;
+
+ devc->m_ptr = 2;
+ devc->m_buf[0] = devc->last_status;
+ devc->m_buf[1] = midic;
+
+ if (devc->m_left <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg (devc, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ }
+ else if (msg == 0xf) /* MPU MARK */
+ {
+ devc->m_state = ST_INIT;
+
+ switch (midic)
+ {
+ case 0xf8:
+ /* printk("NOP "); */
+ break;
+
+ case 0xf9:
+ /* printk("meas end "); */
+ break;
+
+ case 0xfc:
+ /* printk("data end "); */
+ break;
+
+ default:
+ printk ("Unknown MPU mark %02x\n", midic);
+ }
+ }
+ else
+ {
+ devc->last_status = midic;
+ /* printk("midi msg "); */
+ msg -= 8;
+ devc->m_left = len_tab[msg];
+
+ devc->m_ptr = 1;
+ devc->m_buf[0] = midic;
+
+ if (devc->m_left <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg (devc, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ }
+ }
+ break;
+
+ case ST_SYSMSG:
+ switch (midic)
+ {
+ case 0xf0:
+ printk ("<SYX>");
+ devc->m_state = ST_SYSEX;
+ break;
+
+ case 0xf1:
+ devc->m_state = ST_MTC;
+ break;
+
+ case 0xf2:
+ devc->m_state = ST_SONGPOS;
+ devc->m_ptr = 0;
+ break;
+
+ case 0xf3:
+ devc->m_state = ST_SONGSEL;
+ break;
+
+ case 0xf6:
+ /* printk("tune_request\n"); */
+ devc->m_state = ST_INIT;
+
+ /*
+ * Real time messages
+ */
+ case 0xf8:
+ /* midi clock */
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_CLOCK, 0);
+ break;
+
+ case 0xfA:
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_START, 0);
+ break;
+
+ case 0xFB:
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_CONTINUE, 0);
+ break;
+
+ case 0xFC:
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_STOP, 0);
+ break;
+
+ case 0xFE:
+ /* active sensing */
+ devc->m_state = ST_INIT;
+ break;
+
+ case 0xff:
+ /* printk("midi hard reset"); */
+ devc->m_state = ST_INIT;
+ break;
+
+ default:
+ printk ("unknown MIDI sysmsg %0x\n", midic);
+ devc->m_state = ST_INIT;
+ }
+ break;
+
+ case ST_MTC:
+ devc->m_state = ST_INIT;
+ printk ("MTC frame %x02\n", midic);
+ break;
+
+ case ST_SYSEX:
+ if (midic == 0xf7)
+ {
+ printk ("<EOX>");
+ devc->m_state = ST_INIT;
+ }
+ else
+ printk ("%02x ", midic);
+ break;
+
+ case ST_SONGPOS:
+ BUFTEST (devc);
+ devc->m_buf[devc->m_ptr++] = midic;
+ if (devc->m_ptr == 2)
+ {
+ devc->m_state = ST_INIT;
+ devc->m_ptr = 0;
+ timer_ext_event (devc, TMR_SPP,
+ ((devc->m_buf[1] & 0x7f) << 7) |
+ (devc->m_buf[0] & 0x7f));
+ }
+ break;
+
+ case ST_DATABYTE:
+ BUFTEST (devc);
+ devc->m_buf[devc->m_ptr++] = midic;
+ if ((--devc->m_left) <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg (devc, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ break;
+
+ default:
+ printk ("Bad state %d ", devc->m_state);
+ devc->m_state = ST_INIT;
+ }
+
+ return 1;
+}
+
+static void
+mpu401_input_loop (struct mpu_config *devc)
+{
+ unsigned long flags;
+ int busy;
+
+ DISABLE_INTR (flags);
+ busy = devc->m_busy;
+ devc->m_busy = 1;
+ RESTORE_INTR (flags);
+
+ if (busy)
+ return;
+
+ while (input_avail (devc->base))
+ {
+ unsigned char c = read_data (devc->base);
+
+ if (devc->mode == MODE_SYNTH)
+ {
+ mpu_input_scanner (devc, c);
+ }
+ else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
+ devc->inputintr (devc->devno, c);
+ }
+
+ devc->m_busy = 0;
+}
+
+void
+mpuintr (int irq)
+{
+ struct mpu_config *devc;
+ int dev;
+
+#ifdef linux
+ sti ();
+#endif
+
+ if (irq < 1 || irq > 15)
+ {
+ printk ("MPU-401: Interrupt #%d?\n", irq);
+ return;
+ }
+
+ dev = irq2dev[irq];
+ if (dev == -1)
+ {
+ printk ("MPU-401: Interrupt #%d?\n", irq);
+ return;
+ }
+
+ devc = &dev_conf[dev];
+
+ if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
+ if (input_avail (devc->base))
+ mpu401_input_loop (devc);
+
+}
+
+static int
+mpu401_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int err;
+ struct mpu_config *devc;
+
+ if (dev < 0 || dev >= num_midis)
+ return RET_ERROR (ENXIO);
+
+ devc = &dev_conf[dev];
+
+ if (devc->opened)
+ {
+ printk ("MPU-401: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ irq2dev[devc->irq] = dev;
+ if ((err = snd_set_irq_handler (devc->irq, mpuintr) < 0))
+ return err;
+
+ set_uart_mode (dev, devc, 1);
+ devc->mode = MODE_MIDI;
+ devc->synthno = 0;
+
+ mpu401_input_loop (devc);
+
+ devc->inputintr = input;
+ devc->opened = mode;
+
+ return 0;
+}
+
+static void
+mpu401_close (int dev)
+{
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+ if (devc->uart_mode)
+ reset_mpu401 (devc); /*
+ * This disables the UART mode
+ */
+ devc->mode = 0;
+
+ snd_release_irq (devc->irq);
+ devc->inputintr = NULL;
+ irq2dev[devc->irq] = -1;
+ devc->opened = 0;
+}
+
+static int
+mpu401_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+#if 0
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ if (input_avail (devc->base))
+ mpu401_input_loop (devc);
+#endif
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (devc->base); timeout--); /*
+ * Wait
+ */
+
+ DISABLE_INTR (flags);
+ if (!output_ready (devc->base))
+ {
+ printk ("MPU-401: Send data timeout\n");
+ RESTORE_INTR (flags);
+ return 0;
+ }
+
+ write_data (devc->base, midi_byte);
+ RESTORE_INTR (flags);
+ return 1;
+}
+
+static int
+mpu401_command (int dev, mpu_command_rec * cmd)
+{
+ int i, timeout, ok;
+ int ret = 0;
+ unsigned long flags;
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+ if (devc->uart_mode) /*
+ * Not possible in UART mode
+ */
+ {
+ printk ("MPU-401 commands not possible in the UART mode\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+ if (input_avail (devc->base))
+ mpu401_input_loop (devc);
+
+ /*
+ * Sometimes it takes about 30000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 500000; timeout > 0 && !output_ready (devc->base); timeout--);
+
+ DISABLE_INTR (flags);
+ if (!output_ready (devc->base))
+ {
+ printk ("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd);
+ RESTORE_INTR (flags);
+ return RET_ERROR (EIO);
+ }
+
+ write_command (devc->base, cmd->cmd);
+ ok = 0;
+ for (timeout = 500000; timeout > 0 && !ok; timeout--)
+ if (input_avail (devc->base))
+ if (mpu_input_scanner (devc, read_data (devc->base)) == MPU_ACK)
+ ok = 1;
+
+ if (!ok)
+ {
+ RESTORE_INTR (flags);
+ printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd);
+ return RET_ERROR (EIO);
+ }
+
+ if (cmd->nr_args)
+ for (i = 0; i < cmd->nr_args; i++)
+ {
+ for (timeout = 30000; timeout > 0 && !output_ready (devc->base); timeout--);
+
+ if (!mpu401_out (dev, cmd->data[i]))
+ {
+ RESTORE_INTR (flags);
+ printk ("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
+ return RET_ERROR (EIO);
+ }
+ }
+
+ ret = 0;
+ cmd->data[0] = 0;
+
+ if (cmd->nr_returns)
+ for (i = 0; i < cmd->nr_returns; i++)
+ {
+ ok = 0;
+ for (timeout = 5000; timeout > 0 && !ok; timeout--)
+ if (input_avail (devc->base))
+ {
+ cmd->data[i] = read_data (devc->base);
+ ok = 1;
+ }
+
+ if (!ok)
+ {
+ RESTORE_INTR (flags);
+ printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd);
+ return RET_ERROR (EIO);
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return ret;
+}
+
+static int
+exec_cmd (int dev, int cmd, int data)
+{
+ int ret;
+
+ static mpu_command_rec rec;
+
+ rec.cmd = cmd & 0xff;
+ rec.nr_args = ((cmd & 0xf0) == 0xE0);
+ rec.nr_returns = ((cmd & 0xf0) == 0xA0);
+ rec.data[0] = data & 0xff;
+
+ if ((ret = mpu401_command (dev, &rec)) < 0)
+ return ret;
+ return (unsigned char) rec.data[0];
+}
+
+static int
+mpu401_prefix_cmd (int dev, unsigned char status)
+{
+ struct mpu_config *devc = &dev_conf[dev];
+
+ if (devc->uart_mode)
+ return 1;
+
+ if (status < 0xf0)
+ {
+ if (exec_cmd (dev, 0xD0, 0) < 0)
+ return 0;
+
+ return 1;
+ }
+
+ switch (status)
+ {
+ case 0xF0:
+ if (exec_cmd (dev, 0xDF, 0) < 0)
+ return 0;
+
+ return 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+mpu401_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+mpu401_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+mpu401_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+ switch (cmd)
+ {
+ case 1:
+ IOCTL_FROM_USER ((char *) &init_sequence, (char *) arg, 0, sizeof (init_sequence));
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_MPUMODE:
+ if (devc->version == 0)
+ {
+ printk ("MPU-401: Intelligent mode not supported by the HW\n");
+ return RET_ERROR (EINVAL);
+ }
+ set_uart_mode (dev, devc, !IOCTL_IN (arg));
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_MPUCMD:
+ {
+ int ret;
+ mpu_command_rec rec;
+
+ IOCTL_FROM_USER ((char *) &rec, (char *) arg, 0, sizeof (rec));
+
+ if ((ret = mpu401_command (dev, &rec)) < 0)
+ return ret;
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &rec, sizeof (rec));
+ return 0;
+ }
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static void
+mpu401_kick (int dev)
+{
+}
+
+static int
+mpu401_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+static int
+mpu_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ int midi_dev;
+ struct mpu_config *devc;
+
+ midi_dev = synth_devs[dev]->midi_dev;
+
+ if (midi_dev < 0 || midi_dev > num_midis)
+ return RET_ERROR (ENXIO);
+
+ devc = &dev_conf[midi_dev];
+
+ switch (cmd)
+ {
+
+ case SNDCTL_SYNTH_INFO:
+ IOCTL_TO_USER ((char *) arg, 0, &mpu_synth_info[midi_dev],
+ sizeof (struct synth_info));
+
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+mpu_synth_open (int dev, int mode)
+{
+ int midi_dev, err;
+ struct mpu_config *devc;
+
+ midi_dev = synth_devs[dev]->midi_dev;
+
+ if (midi_dev < 0 || midi_dev > num_midis)
+ return RET_ERROR (ENXIO);
+
+ devc = &dev_conf[midi_dev];
+
+ if (devc->opened)
+ {
+ printk ("MPU-401: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ devc->opened = mode;
+ devc->mode = MODE_SYNTH;
+ devc->synthno = dev;
+
+ devc->inputintr = NULL;
+ irq2dev[devc->irq] = midi_dev;
+ if ((err = snd_set_irq_handler (devc->irq, mpuintr) < 0))
+ return err;
+
+ reset_mpu401 (devc);
+
+ if (mode & OPEN_READ)
+ {
+ exec_cmd (midi_dev, 0x34, 0); /* Return timing bytes in stop mode */
+ exec_cmd (midi_dev, 0x8B, 0); /* Enable data in stop mode */
+ }
+
+ return 0;
+}
+
+static void
+mpu_synth_close (int dev)
+{
+ int midi_dev;
+ struct mpu_config *devc;
+
+ midi_dev = synth_devs[dev]->midi_dev;
+
+ devc = &dev_conf[midi_dev];
+ exec_cmd (midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */
+ exec_cmd (midi_dev, 0x8a, 0); /* Disable data in stopped mode */
+
+ devc->opened = 0;
+ devc->mode = 0;
+ snd_release_irq (devc->irq);
+ devc->inputintr = NULL;
+ irq2dev[devc->irq] = -1;
+}
+
+#define MIDI_SYNTH_NAME "MPU-401 UART Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct synth_operations mpu401_synth_proto =
+{
+ NULL,
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ mpu_synth_open,
+ mpu_synth_close,
+ mpu_synth_ioctl,
+ midi_synth_kill_note,
+ midi_synth_start_note,
+ midi_synth_set_instr,
+ midi_synth_reset,
+ midi_synth_hw_control,
+ midi_synth_load_patch,
+ midi_synth_aftertouch,
+ midi_synth_controller,
+ midi_synth_panning,
+ NULL,
+ midi_synth_patchmgr,
+ midi_synth_bender
+};
+
+static struct synth_operations mpu401_synth_operations[MAX_MIDI_DEV];
+
+static struct midi_operations mpu401_midi_proto =
+{
+ {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+ NULL,
+ mpu401_open,
+ mpu401_close,
+ mpu401_ioctl,
+ mpu401_out,
+ mpu401_start_read,
+ mpu401_end_read,
+ mpu401_kick,
+ NULL,
+ mpu401_buffer_status,
+ mpu401_prefix_cmd
+};
+
+static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
+
+static void
+mpu401_chk_version (struct mpu_config *devc)
+{
+ int tmp;
+
+ devc->version = devc->revision = 0;
+
+ if ((tmp = exec_cmd (num_midis, 0xAC, 0)) < 0)
+ return;
+ devc->version = tmp;
+
+ if ((tmp = exec_cmd (num_midis, 0xAD, 0)) < 0)
+ return;
+ devc->revision = tmp;
+}
+
+long
+attach_mpu401 (long mem_start, struct address_info *hw_config)
+{
+ int i;
+ unsigned long flags;
+ char revision_char;
+
+ struct mpu_config *devc;
+
+ for (i = 0; i < 16; i++)
+ irq2dev[i] = -1;
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("MPU-401: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ devc = &dev_conf[num_midis];
+
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->opened = 0;
+ devc->uart_mode = 0;
+ devc->initialized = 0;
+ devc->version = 0;
+ devc->revision = 0;
+ devc->capabilities = 0;
+ devc->timer_flag = 0;
+ devc->m_busy = 0;
+ devc->m_state = ST_INIT;
+
+ for (i = 0; i < 32; i++)
+ devc->controls[i] = 0x2000;
+
+ if (!reset_mpu401 (devc))
+ return mem_start;
+
+ DISABLE_INTR (flags);
+ mpu401_chk_version (devc);
+ if (devc->version == 0)
+ mpu401_chk_version (devc);
+ RESTORE_INTR (flags);
+
+ if (devc->version == 0)
+ {
+ memcpy ((char *) &mpu401_synth_operations[num_midis],
+ (char *) &std_midi_synth,
+ sizeof (struct synth_operations));
+ }
+ else
+ {
+ devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */
+ memcpy ((char *) &mpu401_synth_operations[num_midis],
+ (char *) &mpu401_synth_proto,
+ sizeof (struct synth_operations));
+ }
+
+ memcpy ((char *) &mpu401_midi_operations[num_midis],
+ (char *) &mpu401_midi_proto,
+ sizeof (struct midi_operations));
+
+ mpu401_midi_operations[num_midis].converter =
+ &mpu401_synth_operations[num_midis];
+
+ memcpy ((char *) &mpu_synth_info[num_midis],
+ (char *) &mpu_synth_info_proto,
+ sizeof (struct synth_info));
+
+ n_mpu_devs++;
+
+ if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */
+ {
+ int ports = (devc->revision & 0x08) ? 32 : 16;
+
+ devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
+ MPU_CAP_CLS | MPU_CAP_2PORT;
+
+ revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
+ printk (" <MQX-%d%c MIDI Interface>",
+ ports,
+ revision_char);
+#ifndef SCO
+ sprintf (mpu_synth_info[num_midis].name,
+ "MQX-%d%c MIDI Interface #%d",
+ ports,
+ revision_char,
+ n_mpu_devs);
+#endif
+ }
+ else
+ {
+
+ revision_char = devc->revision ? devc->revision + '@' : ' ';
+ if (devc->revision > ('Z' - '@'))
+ revision_char = '+';
+
+ devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
+
+ printk (" <MPU-401 MIDI Interface %d.%d%c>",
+ (devc->version & 0xf0) >> 4,
+ devc->version & 0x0f,
+ revision_char);
+#ifndef SCO
+ sprintf (mpu_synth_info[num_midis].name,
+ "MPU-401 %d.%d%c Midi interface #%d",
+ (devc->version & 0xf0) >> 4,
+ devc->version & 0x0f,
+ revision_char,
+ n_mpu_devs);
+#endif
+ }
+
+#ifndef SCO
+ strcpy (mpu401_midi_operations[num_midis].info.name,
+ mpu_synth_info[num_midis].name);
+#endif
+
+ mpu401_synth_operations[num_midis].midi_dev = devc->devno = num_midis;
+ mpu401_synth_operations[devc->devno].info =
+ &mpu_synth_info[devc->devno];
+
+ if (devc->capabilities & MPU_CAP_INTLG) /* Has timer */
+ mpu_timer_init (num_midis);
+
+ midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno];
+ return mem_start;
+}
+
+static int
+reset_mpu401 (struct mpu_config *devc)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+ int timeout_limit;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ * (If the device is in the UART mode, it will not ack the reset cmd).
+ */
+
+ ok = 0;
+
+ timeout_limit = devc->initialized ? 30000 : 100000;
+ devc->initialized = 1;
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
+ ok = output_ready (devc->base);
+
+ write_command (devc->base, MPU_RESET); /*
+ * Send MPU-401 RESET Command
+ */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
+ {
+ DISABLE_INTR (flags);
+ if (input_avail (devc->base))
+ if (read_data (devc->base) == MPU_ACK)
+ ok = 1;
+ RESTORE_INTR (flags);
+ }
+
+ }
+
+ devc->m_state = ST_INIT;
+ devc->m_ptr = 0;
+ devc->m_left = 0;
+ devc->last_status = 0;
+ devc->uart_mode = 0;
+
+ return ok;
+}
+
+static void
+set_uart_mode (int dev, struct mpu_config *devc, int arg)
+{
+
+ if (!arg && devc->version == 0)
+ return;
+
+ if ((devc->uart_mode == 0) == (arg == 0))
+ return; /* Already set */
+
+ reset_mpu401 (devc); /* This exits the uart mode */
+
+ if (arg)
+ {
+ if (exec_cmd (dev, UART_MODE_ON, 0) < 0)
+ {
+ printk ("MPU%d: Can't enter UART mode\n", devc->devno);
+ devc->uart_mode = 0;
+ return;
+ }
+ }
+ devc->uart_mode = arg;
+
+}
+
+int
+probe_mpu401 (struct address_info *hw_config)
+{
+ int ok = 0;
+ struct mpu_config tmp_devc;
+
+ tmp_devc.base = hw_config->io_base;
+ tmp_devc.irq = hw_config->irq;
+ tmp_devc.initialized = 0;
+
+ ok = reset_mpu401 (&tmp_devc);
+
+ return ok;
+}
+
+/*****************************************************
+ * Timer stuff
+ ****************************************************/
+
+#if !defined(EXCLUDE_SEQUENCER)
+
+static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
+static volatile int curr_tempo, curr_timebase, hw_timebase;
+static int max_timebase = 8; /* 8*24=192 ppqn */
+static volatile unsigned long next_event_time;
+static volatile unsigned long curr_ticks, curr_clocks;
+static unsigned long prev_event_time;
+static int metronome_mode;
+
+static unsigned long
+clocks2ticks (unsigned long clocks)
+{
+ /*
+ * The MPU-401 supports just a limited set of possible timebase values.
+ * Since the applications require more choices, the driver has to
+ * program the HW to do it's best and to convert between the HW and
+ * actual timebases.
+ */
+
+ return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
+}
+
+static void
+set_timebase (int midi_dev, int val)
+{
+ int hw_val;
+
+ if (val < 48)
+ val = 48;
+ if (val > 1000)
+ val = 1000;
+
+ hw_val = val;
+ hw_val = (hw_val + 23) / 24;
+ if (hw_val > max_timebase)
+ hw_val = max_timebase;
+
+ if (exec_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
+ {
+ printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24);
+ return;
+ }
+ hw_timebase = hw_val * 24;
+ curr_timebase = val;
+
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = curr_clocks = 0;
+ RESTORE_INTR (flags);
+}
+
+static void
+set_timer_mode (int midi_dev)
+{
+ if (timer_mode & TMR_MODE_CLS)
+ exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */
+ else if (timer_mode & TMR_MODE_SMPTE)
+ exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */
+
+ if (timer_mode & TMR_INTERNAL)
+ {
+ exec_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */
+ }
+ else
+ {
+ if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
+ {
+ exec_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */
+ exec_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */
+ }
+ else if (timer_mode & TMR_MODE_FSK)
+ exec_cmd (midi_dev, 0x81, 0); /* Use FSK sync */
+ }
+}
+
+static void
+stop_metronome (int midi_dev)
+{
+ exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */
+}
+
+static void
+setup_metronome (int midi_dev)
+{
+ int numerator, denominator;
+ int clks_per_click, num_32nds_per_beat;
+ int beats_per_measure;
+
+ numerator = ((unsigned) metronome_mode >> 24) & 0xff;
+ denominator = ((unsigned) metronome_mode >> 16) & 0xff;
+ clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
+ num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
+ beats_per_measure = (numerator * 4) >> denominator;
+
+ if (!metronome_mode)
+ exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */
+ else
+ {
+ exec_cmd (midi_dev, 0xE4, clks_per_click);
+ exec_cmd (midi_dev, 0xE6, beats_per_measure);
+ exec_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */
+ }
+}
+
+static int
+start_timer (int midi_dev)
+{
+ tmr_reset ();
+ set_timer_mode (midi_dev);
+
+ if (tmr_running)
+ return TIMER_NOT_ARMED; /* Already running */
+
+ if (timer_mode & TMR_INTERNAL)
+ {
+ exec_cmd (midi_dev, 0x02, 0); /* Send MIDI start */
+ tmr_running = 1;
+ return TIMER_NOT_ARMED;
+ }
+ else
+ {
+ exec_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */
+ exec_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */
+ exec_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */
+ exec_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */
+ }
+
+ return TIMER_ARMED;
+}
+
+static int
+mpu_timer_open (int dev, int mode)
+{
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ if (timer_open)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 50;
+ exec_cmd (midi_dev, 0xE0, 50);
+ curr_timebase = hw_timebase = 120;
+ set_timebase (midi_dev, 120);
+ timer_open = 1;
+ metronome_mode = 0;
+ set_timer_mode (midi_dev);
+
+ exec_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */
+ exec_cmd (midi_dev, 0x95, 0); /* Enable clock to host */
+
+ return 0;
+}
+
+static void
+mpu_timer_close (int dev)
+{
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ timer_open = tmr_running = 0;
+ exec_cmd (midi_dev, 0x15, 0); /* Stop all */
+ exec_cmd (midi_dev, 0x94, 0); /* Disable clock to host */
+ exec_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */
+ stop_metronome (midi_dev);
+}
+
+static int
+mpu_timer_event (int dev, unsigned char *event)
+{
+ unsigned char command = event[1];
+ unsigned long parm = *(unsigned int *) &event[4];
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ switch (command)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ if (tmr_running)
+ break;
+ return start_timer (midi_dev);
+ break;
+
+ case TMR_STOP:
+ exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */
+ stop_metronome (midi_dev);
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ if (tmr_running)
+ break;
+ exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */
+ setup_metronome (midi_dev);
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+
+ if (exec_cmd (midi_dev, 0xE0, parm) < 0)
+ printk ("MPU: Can't set tempo to %d\n", (int) parm);
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ case TMR_TIMESIG:
+ if (metronome_mode) /* Metronome enabled */
+ {
+ metronome_mode = parm;
+ setup_metronome (midi_dev);
+ }
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+mpu_timer_get_time (int dev)
+{
+ if (!timer_open)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+mpu_timer_ioctl (int dev,
+ unsigned int command, unsigned int arg)
+{
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ switch (command)
+ {
+ case SNDCTL_TMR_SOURCE:
+ {
+ int parm = IOCTL_IN (arg) & timer_caps;
+
+ if (parm != 0)
+ {
+ timer_mode = parm;
+
+ if (timer_mode & TMR_MODE_CLS)
+ exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */
+ else if (timer_mode & TMR_MODE_SMPTE)
+ exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */
+ }
+
+ return IOCTL_OUT (arg, timer_mode);
+ }
+ break;
+
+ case SNDCTL_TMR_START:
+ if (tmr_running)
+ return 0;
+ start_timer (midi_dev);
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */
+ stop_metronome (midi_dev);
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ if (tmr_running)
+ return 0;
+ tmr_running = 1;
+ exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ set_timebase (midi_dev, val);
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+ int ret;
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ if ((ret = exec_cmd (midi_dev, 0xE0, val)) < 0)
+ {
+ printk ("MPU: Can't set tempo to %d\n", (int) val);
+ return ret;
+ }
+
+ curr_tempo = val;
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ metronome_mode = IOCTL_IN (arg);
+ setup_metronome (midi_dev);
+ return 0;
+ break;
+
+ default:
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+mpu_timer_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+static struct sound_timer_operations mpu_timer =
+{
+ {"MPU-401 Timer", 0},
+ 10, /* Priority */
+ 0, /* Local device link */
+ mpu_timer_open,
+ mpu_timer_close,
+ mpu_timer_event,
+ mpu_timer_get_time,
+ mpu_timer_ioctl,
+ mpu_timer_arm
+};
+
+static void
+mpu_timer_interrupt (void)
+{
+
+ if (!timer_open)
+ return;
+
+ if (!tmr_running)
+ return;
+
+ curr_clocks++;
+ curr_ticks = clocks2ticks (curr_clocks);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+}
+
+static void
+timer_ext_event (struct mpu_config *devc, int event, int parm)
+{
+ int midi_dev = devc->devno;
+
+ if (!devc->timer_flag)
+ return;
+
+ switch (event)
+ {
+ case TMR_CLOCK:
+ printk ("<MIDI clk>");
+ break;
+
+ case TMR_START:
+ printk ("Ext MIDI start\n");
+ if (!tmr_running)
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 1;
+ setup_metronome (midi_dev);
+ next_event_time = 0;
+ STORE (SEQ_START_TIMER ());
+ }
+ break;
+
+ case TMR_STOP:
+ printk ("Ext MIDI stop\n");
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 0;
+ stop_metronome (midi_dev);
+ STORE (SEQ_STOP_TIMER ());
+ }
+ break;
+
+ case TMR_CONTINUE:
+ printk ("Ext MIDI continue\n");
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 1;
+ setup_metronome (midi_dev);
+ STORE (SEQ_CONTINUE_TIMER ());
+ }
+ break;
+
+ case TMR_SPP:
+ printk ("Songpos: %d\n", parm);
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ STORE (SEQ_SONGPOS (parm));
+ }
+ break;
+ }
+}
+
+static void
+mpu_timer_init (int midi_dev)
+{
+ struct mpu_config *devc;
+ int n;
+
+ devc = &dev_conf[midi_dev];
+
+ if (timer_initialized)
+ return; /* There is already a similar timer */
+
+ timer_initialized = 1;
+
+ mpu_timer.devlink = midi_dev;
+ dev_conf[midi_dev].timer_flag = 1;
+
+#if 1
+ if (num_sound_timers >= MAX_TIMER_DEV)
+ n = 0; /* Overwrite the system timer */
+ else
+ n = num_sound_timers++;
+#else
+ n = 0;
+#endif
+ sound_timer_devs[n] = &mpu_timer;
+
+ if (devc->version < 0x20) /* Original MPU-401 */
+ timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
+ else
+ {
+ /*
+ * The version number 2.0 is used (at least) by the
+ * MusicQuest cards and the Roland Super-MPU.
+ *
+ * MusicQuest has given a special meaning to the bits of the
+ * revision number. The Super-MPU returns 0.
+ */
+
+ if (devc->revision)
+ timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
+
+ if (devc->revision & 0x02)
+ timer_caps |= TMR_MODE_CLS;
+
+#if 0
+ if (devc->revision & 0x04)
+ timer_caps |= TMR_MODE_SMPTE;
+#endif
+
+ if (devc->revision & 0x40)
+ max_timebase = 10; /* Has the 216 and 240 ppqn modes */
+ }
+
+ timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
+
+}
+
+#endif
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c
new file mode 100644
index 000000000000..0acd0c6203a0
--- /dev/null
+++ b/sys/i386/isa/sound/opl3.c
@@ -0,0 +1,1225 @@
+/*
+ * sound/opl3.c
+ *
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Major improvements to the FM handling 30AUG92 by Rob Hooft,
+ */
+/*
+ * hooft@chem.ruu.nl
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+#include "opl3.h"
+
+#define MAX_VOICE 18
+#define OFFS_4OP 11 /*
+ * * * Definitions for the operators OP3 and
+ * * OP4 * * begin here */
+
+static int opl3_enabled = 0;
+static int left_address = 0x388, right_address = 0x388, both_address = 0;
+
+static int nr_voices = 9;
+static int logical_voices[MAX_VOICE] =
+{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+
+struct voice_info
+ {
+ unsigned char keyon_byte;
+ long bender;
+ long bender_range;
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ int mode;
+ };
+
+static struct voice_info voices[MAX_VOICE];
+static struct voice_alloc_info *voice_alloc;
+static struct channel_info *chn_info;
+
+static struct sbi_instrument *instrmap;
+static struct sbi_instrument *active_instrument[MAX_VOICE] =
+{NULL};
+
+static struct synth_info fm_info =
+{"OPL-2", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0};
+
+static int already_initialized = 0;
+
+static int opl3_ok = 0;
+static int opl3_busy = 0;
+static int fm_model = 0; /*
+
+
+ * * * * 0=no fm, 1=mono, 2=SB Pro 1, 3=SB
+ * Pro 2 * * */
+
+static int store_instr (int instr_no, struct sbi_instrument *instr);
+static void freq_to_fnum (int freq, int *block, int *fnum);
+static void opl3_command (int io_addr, unsigned int addr, unsigned int val);
+static int opl3_kill_note (int dev, int voice, int note, int velocity);
+static unsigned char connection_mask = 0x00;
+
+void
+enable_opl3_mode (int left, int right, int both)
+{
+ if (opl3_enabled)
+ return;
+
+ opl3_enabled = 1;
+ left_address = left;
+ right_address = right;
+ both_address = both;
+ fm_info.capabilities = SYNTH_CAP_OPL3;
+ fm_info.synth_subtype = FM_TYPE_OPL3;
+}
+
+static void
+enter_4op_mode (void)
+{
+ int i;
+ static int voices_4op[MAX_VOICE] =
+ {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};
+
+ connection_mask = 0x3f; /* Connect all possible 4 OP voices */
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f);
+
+ for (i = 0; i < 3; i++)
+ physical_voices[i].voice_mode = 4;
+ for (i = 3; i < 6; i++)
+ physical_voices[i].voice_mode = 0;
+
+ for (i = 9; i < 12; i++)
+ physical_voices[i].voice_mode = 4;
+ for (i = 12; i < 15; i++)
+ physical_voices[i].voice_mode = 0;
+
+ for (i = 0; i < 12; i++)
+ logical_voices[i] = voices_4op[i];
+ voice_alloc->max_voice = nr_voices = 12;
+}
+
+static int
+opl3_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+
+ case SNDCTL_FM_LOAD_INSTR:
+ {
+ struct sbi_instrument ins;
+
+ IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins));
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+ return RET_ERROR (EINVAL);
+ }
+
+ pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0);
+ return store_instr (ins.channel, &ins);
+ }
+ break;
+
+ case SNDCTL_SYNTH_INFO:
+ fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices;
+
+ IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info));
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ if (opl3_enabled)
+ enter_4op_mode ();
+ return 0;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+}
+
+int
+opl3_detect (int ioaddr)
+{
+ /*
+ * This function returns 1 if the FM chicp is present at the given I/O port
+ * The detection algorithm plays with the timer built in the FM chip and
+ * looks for a change in the status register.
+ *
+ * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+ * boards.
+ *
+ * Note2! The chip is initialized if detected.
+ */
+
+ unsigned char stat1, stat2;
+ int i;
+
+ if (already_initialized)
+ {
+ return 0; /*
+ * Do avoid duplicate initializations
+ */
+ }
+
+ if (opl3_enabled)
+ ioaddr = left_address;
+
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /*
+ * Reset
+ * timers
+ * 1
+ * and
+ * 2
+ */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /*
+ * Reset the
+ * IRQ of FM
+ * * chicp
+ */
+
+ stat1 = INB (ioaddr); /*
+ * Read status register
+ */
+
+ if ((stat1 & 0xE0) != 0x00)
+ {
+ return 0; /*
+ * Should be 0x00
+ */
+ }
+
+ opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /*
+ * Set timer 1 to
+ * 0xff
+ */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER,
+ TIMER2_MASK | TIMER1_START); /*
+ * Unmask and start timer 1
+ */
+
+ /*
+ * Now we have to delay at least 80 msec
+ */
+
+ for (i = 0; i < 50; i++)
+ tenmicrosec (); /*
+ * To be sure
+ */
+
+ stat2 = INB (ioaddr); /*
+ * Read status after timers have expired
+ */
+
+ /*
+ * Stop the timers
+ */
+
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /*
+ * Reset
+ * timers
+ * 1
+ * and
+ * 2
+ */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /*
+ * Reset the
+ * IRQ of FM
+ * * chicp
+ */
+
+ if ((stat2 & 0xE0) != 0xc0)
+ {
+ return 0; /*
+ * There is no YM3812
+ */
+ }
+
+ /*
+ * There is a FM chicp in this address. Now set some default values.
+ */
+
+ for (i = 0; i < 9; i++)
+ opl3_command (ioaddr, KEYON_BLOCK + i, 0); /*
+ * Note off
+ */
+
+ opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+ opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /*
+ * Melodic mode.
+ */
+
+ return 1;
+}
+
+static int
+opl3_kill_note (int dev, int voice, int note, int velocity)
+{
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ voice_alloc->map[voice] = 0;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ DEB (printk ("Kill note %d\n", voice));
+
+ if (map->voice_mode == 0)
+ return 0;
+
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20);
+
+ voices[voice].keyon_byte = 0;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200; /*
+ * 200 cents = 2 semitones
+ */
+ voices[voice].orig_freq = 0;
+ voices[voice].current_freq = 0;
+ voices[voice].mode = 0;
+
+ return 0;
+}
+
+#define HIHAT 0
+#define CYMBAL 1
+#define TOMTOM 2
+#define SNARE 3
+#define BDRUM 4
+#define UNDEFINED TOMTOM
+#define DEFAULT TOMTOM
+
+static int
+store_instr (int instr_no, struct sbi_instrument *instr)
+{
+
+ if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled))
+ printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
+ memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
+
+ return 0;
+}
+
+static int
+opl3_set_instr (int dev, int voice, int instr_no)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+ return 0;
+
+ active_instrument[voice] = &instrmap[instr_no];
+ return 0;
+}
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so : Volume
+ * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+char fm_volume_table[128] =
+{-64, -48, -40, -35, -32, -29, -27, -26, /*
+ * 0 - 7
+ */
+ -24, -23, -21, -20, -19, -18, -18, -17, /*
+ * 8 - 15
+ */
+ -16, -15, -15, -14, -13, -13, -12, -12, /*
+ * 16 - 23
+ */
+ -11, -11, -10, -10, -10, -9, -9, -8, /*
+ * 24 - 31
+ */
+ -8, -8, -7, -7, -7, -6, -6, -6,/*
+ * 32 - 39
+ */
+ -5, -5, -5, -5, -4, -4, -4, -4,/*
+ * 40 - 47
+ */
+ -3, -3, -3, -3, -2, -2, -2, -2,/*
+ * 48 - 55
+ */
+ -2, -1, -1, -1, -1, 0, 0, 0, /*
+ * 56 - 63
+ */
+ 0, 0, 0, 1, 1, 1, 1, 1, /*
+ * 64 - 71
+ */
+ 1, 2, 2, 2, 2, 2, 2, 2, /*
+ * 72 - 79
+ */
+ 3, 3, 3, 3, 3, 3, 3, 4, /*
+ * 80 - 87
+ */
+ 4, 4, 4, 4, 4, 4, 4, 5, /*
+ * 88 - 95
+ */
+ 5, 5, 5, 5, 5, 5, 5, 5, /*
+ * 96 - 103
+ */
+ 6, 6, 6, 6, 6, 6, 6, 6, /*
+ * 104 - 111
+ */
+ 6, 7, 7, 7, 7, 7, 7, 7, /*
+ * 112 - 119
+ */
+ 7, 7, 7, 8, 8, 8, 8, 8}; /*
+
+
+ * * * * 120 - 127 */
+
+static void
+calc_vol (unsigned char *regbyte, int volume)
+{
+ int level = (~*regbyte & 0x3f);
+
+ if (level)
+ level += fm_volume_table[volume];
+
+ if (level > 0x3f)
+ level = 0x3f;
+ if (level < 0)
+ level = 0;
+
+ *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void
+set_voice_volume (int voice, int volume)
+{
+ unsigned char vol1, vol2, vol3, vol4;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (instr->channel < 0)
+ return;
+
+ if (voices[voice].mode == 0)
+ return;
+
+ if (voices[voice].mode == 2)
+ { /*
+ * 2 OP voice
+ */
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+
+ if ((instr->operators[10] & 0x01))
+ { /*
+ * Additive synthesis
+ */
+ calc_vol (&vol1, volume);
+ calc_vol (&vol2, volume);
+ }
+ else
+ { /*
+ * FM synthesis
+ */
+ calc_vol (&vol2, volume);
+ }
+
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /*
+ * Modulator
+ * volume
+ */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /*
+ * Carrier
+ * volume
+ */
+ }
+ else
+ { /*
+ * 4 OP voice
+ */
+ int connection;
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ vol3 = instr->operators[OFFS_4OP + 2];
+ vol4 = instr->operators[OFFS_4OP + 3];
+
+ /*
+ * The connection method for 4 OP voices is defined by the rightmost
+ * bits at the offsets 10 and 10+OFFS_4OP
+ */
+
+ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ calc_vol (&vol4, volume); /*
+ * Just the OP 4 is carrier
+ */
+ break;
+
+ case 1:
+ calc_vol (&vol2, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ case 2:
+ calc_vol (&vol1, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ case 3:
+ calc_vol (&vol1, volume);
+ calc_vol (&vol3, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ default: /*
+ * Why ??
+ */ ;
+ }
+
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
+ }
+}
+
+static int
+opl3_start_note (int dev, int voice, int note, int volume)
+{
+ unsigned char data, fpc;
+ int block, fnum, freq, voice_mode;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return 0;
+
+ if (note == 255) /*
+ * Just change the volume
+ */
+ {
+ set_voice_volume (voice, volume);
+ return 0;
+ }
+
+ /*
+ * Kill previous note before playing
+ */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /*
+ * Carrier
+ * volume to
+ * min
+ */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /*
+ * Modulator
+ * volume to
+ */
+
+ if (map->voice_mode == 4)
+ {
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+ }
+
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /*
+ * Note
+ * off
+ */
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (instr->channel < 0)
+ {
+ printk (
+ "OPL3: Initializing voice %d with undefined instrument\n",
+ voice);
+ return 0;
+ }
+
+ if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+ return 0; /*
+ * Cannot play
+ */
+
+ voice_mode = map->voice_mode;
+
+ if (voice_mode == 4)
+ {
+ int voice_shift;
+
+ voice_shift = (map->ioaddr == left_address) ? 0 : 3;
+ voice_shift += map->voice_num;
+
+ if (instr->key != OPL3_PATCH) /*
+ * Just 2 OP patch
+ */
+ {
+ voice_mode = 2;
+ connection_mask &= ~(1 << voice_shift);
+ }
+ else
+ {
+ connection_mask |= (1 << voice_shift);
+ }
+
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+ }
+
+ /*
+ * Set Sound Characteristics
+ */
+ opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+ opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+ /*
+ * Set Attack/Decay
+ */
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+ /*
+ * Set Sustain/Release
+ */
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+ /*
+ * Set Wave Select
+ */
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+ /*
+ * Set Feedback/Connection
+ */
+ fpc = instr->operators[10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /*
+ * Ensure that at least one chn is enabled
+ */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num,
+ fpc);
+
+ /*
+ * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+ */
+
+ if (voice_mode == 4)
+ {
+
+ /*
+ * Set Sound Characteristics
+ */
+ opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+ opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+ /*
+ * Set Attack/Decay
+ */
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+ /*
+ * Set Sustain/Release
+ */
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+ /*
+ * Set Wave Select
+ */
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+ /*
+ * Set Feedback/Connection
+ */
+ fpc = instr->operators[OFFS_4OP + 10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /*
+ * Ensure that at least one chn is enabled
+ */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+ }
+
+ voices[voice].mode = voice_mode;
+
+ set_voice_volume (voice, volume);
+
+ freq = voices[voice].orig_freq = note_to_freq (note) / 1000;
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ freq_to_fnum (freq, &block, &fnum);
+
+ /*
+ * Play note
+ */
+
+ data = fnum & 0xff; /*
+ * Least significant bits of fnumber
+ */
+ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+ voices[voice].keyon_byte = data;
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+ if (voice_mode == 4)
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+
+ return 0;
+}
+
+static void
+freq_to_fnum (int freq, int *block, int *fnum)
+{
+ int f, octave;
+
+ /*
+ * Converts the note frequency to block and fnum values for the FM chip
+ */
+ /*
+ * First try to compute the block -value (octave) where the note belongs
+ */
+
+ f = freq;
+
+ octave = 5;
+
+ if (f == 0)
+ octave = 0;
+ else if (f < 261)
+ {
+ while (f < 261)
+ {
+ octave--;
+ f <<= 1;
+ }
+ }
+ else if (f > 493)
+ {
+ while (f > 493)
+ {
+ octave++;
+ f >>= 1;
+ }
+ }
+
+ if (octave > 7)
+ octave = 7;
+
+ *fnum = freq * (1 << (20 - octave)) / 49716;
+ *block = octave;
+}
+
+static void
+opl3_command (int io_addr, unsigned int addr, unsigned int val)
+{
+ int i;
+
+ /*
+ * The original 2-OP synth requires a quite long delay after writing to a
+ * register. The OPL-3 survives with just two INBs
+ */
+
+ OUTB ((unsigned char) (addr & 0xff), io_addr); /*
+ * Select register
+ *
+ */
+
+ if (!opl3_enabled)
+ tenmicrosec ();
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+
+ OUTB ((unsigned char) (val & 0xff), io_addr + 1); /*
+ * Write to register
+ *
+ */
+
+ if (!opl3_enabled)
+ {
+ tenmicrosec ();
+ tenmicrosec ();
+ tenmicrosec ();
+ }
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+}
+
+static void
+opl3_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < nr_voices; i++)
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff);
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff);
+
+ if (physical_voices[logical_voices[i]].voice_mode == 4)
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff);
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff);
+ }
+
+ opl3_kill_note (dev, i, 0, 64);
+ }
+
+ if (opl3_enabled)
+ {
+ voice_alloc->max_voice = nr_voices = 18;
+
+ for (i = 0; i < 18; i++)
+ logical_voices[i] = i;
+
+ for (i = 0; i < 18; i++)
+ physical_voices[i].voice_mode = 2;
+
+ }
+
+}
+
+static int
+opl3_open (int dev, int mode)
+{
+ if (!opl3_ok)
+ return RET_ERROR (ENXIO);
+ if (opl3_busy)
+ return RET_ERROR (EBUSY);
+ opl3_busy = 1;
+
+ connection_mask = 0x00; /*
+ * Just 2 OP voices
+ */
+ if (opl3_enabled)
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+ return 0;
+}
+
+static void
+opl3_close (int dev)
+{
+ opl3_busy = 0;
+ voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9;
+ fm_info.nr_drums = 0;
+ fm_info.perc_mode = 0;
+
+ opl3_reset (dev);
+}
+
+static void
+opl3_hw_control (int dev, unsigned char *event)
+{
+}
+
+static int
+opl3_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct sbi_instrument ins;
+
+ if (count < sizeof (ins))
+ {
+ printk ("FM Error: Patch record too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs);
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+ return RET_ERROR (EINVAL);
+ }
+ ins.key = format;
+
+ return store_instr (ins.channel, &ins);
+}
+
+static void
+opl3_panning (int dev, int voice, int pressure)
+{
+}
+
+static void
+opl3_volume_method (int dev, int mode)
+{
+}
+
+#define SET_VIBRATO(cell) { \
+ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+ if (pressure > 110) \
+ tmp |= 0x40; /* Vibrato on */ \
+ opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+
+static void
+opl3_aftertouch (int dev, int voice, int pressure)
+{
+ int tmp;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ DEB (printk ("Aftertouch %d\n", voice));
+
+ if (map->voice_mode == 0)
+ return;
+
+ /*
+ * Adjust the amount of vibrato depending the pressure
+ */
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (voices[voice].mode == 4)
+ {
+ int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ SET_VIBRATO (4);
+ break;
+
+ case 1:
+ SET_VIBRATO (2);
+ SET_VIBRATO (4);
+ break;
+
+ case 2:
+ SET_VIBRATO (1);
+ SET_VIBRATO (4);
+ break;
+
+ case 3:
+ SET_VIBRATO (1);
+ SET_VIBRATO (3);
+ SET_VIBRATO (4);
+ break;
+
+ }
+ /*
+ * Not implemented yet
+ */
+ }
+ else
+ {
+ SET_VIBRATO (1);
+
+ if ((instr->operators[10] & 0x01)) /*
+ * Additive synthesis
+ */
+ SET_VIBRATO (2);
+ }
+}
+
+#undef SET_VIBRATO
+
+static void
+bend_pitch (int dev, int voice, int value)
+{
+ unsigned char data;
+ int block, fnum, freq;
+ struct physical_voice_info *map;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return;
+
+ voices[voice].bender = value;
+ if (!value)
+ return;
+ if (!(voices[voice].keyon_byte & 0x20))
+ return; /*
+ * Not keyed on
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ freq_to_fnum (freq, &block, &fnum);
+
+ data = fnum & 0xff; /*
+ * Least significant bits of fnumber
+ */
+ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /*
+ * *
+ * KEYON|OCTAVE|MS
+ *
+ * * bits * *
+ * of * f-num
+ *
+ */
+ voices[voice].keyon_byte = data;
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+}
+
+static void
+opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ bend_pitch (dev, voice, value);
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+ }
+}
+
+static int
+opl3_patchmgr (int dev, struct patmgr_info *rec)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+opl3_bender (int dev, int voice, int value)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ bend_pitch (dev, voice, value);
+}
+
+static int
+opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p, avail_voices;
+ struct sbi_instrument *instr;
+ int is4op;
+ int instr_no;
+
+ if (chn < 0 || chn > 15)
+ instr_no = 0;
+ else
+ instr_no = chn_info[chn].pgm_num;
+
+ instr = &instrmap[instr_no];
+ if (instr->channel < 0 || /* Instrument not loaded */
+ nr_voices != 12) /* Not in 4 OP mode */
+ is4op = 0;
+ else if (nr_voices == 12) /* 4 OP mode */
+ is4op = (instr->key == OPL3_PATCH);
+ else
+ is4op = 0;
+
+ if (is4op)
+ {
+ p = 0;
+ avail_voices = 6;
+ }
+ else
+ {
+ if (nr_voices == 12) /* 4 OP mode. Use the '2 OP only' voices first */
+ p = 6;
+ else
+ p = 0;
+ avail_voices = nr_voices;
+ }
+
+ /*
+ * Now try to find a free voice
+ */
+
+ for (i = 0; i < avail_voices; i++)
+ {
+ if (alloc->map[p] == 0)
+ {
+ return p;
+ }
+ p = (p + 1) % nr_voices;
+ }
+
+ /*
+ * Insert some kind of priority mechanism here.
+ */
+
+ printk ("OPL3: Out of free voices\n");
+ return 0; /* All voices in use. Select the first one. */
+}
+
+static struct synth_operations opl3_operations =
+{
+ &fm_info,
+ 0,
+ SYNTH_TYPE_FM,
+ FM_TYPE_ADLIB,
+ opl3_open,
+ opl3_close,
+ opl3_ioctl,
+ opl3_kill_note,
+ opl3_start_note,
+ opl3_set_instr,
+ opl3_reset,
+ opl3_hw_control,
+ opl3_load_patch,
+ opl3_aftertouch,
+ opl3_controller,
+ opl3_panning,
+ opl3_volume_method,
+ opl3_patchmgr,
+ opl3_bender,
+ opl3_alloc_voice
+};
+
+long
+opl3_init (long mem_start)
+{
+ int i;
+
+ PERMANENT_MALLOC (struct sbi_instrument *, instrmap,
+ SBFM_MAXINSTR * sizeof (*instrmap), mem_start);
+
+ if (num_synths >= MAX_SYNTH_DEV)
+ printk ("OPL3 Error: Too many synthesizers\n");
+ else
+ {
+ synth_devs[num_synths++] = &opl3_operations;
+ voice_alloc = &opl3_operations.alloc;
+ chn_info = &opl3_operations.chn_info[0];
+ }
+
+ fm_model = 0;
+ opl3_ok = 1;
+ if (opl3_enabled)
+ {
+ printk (" <Yamaha OPL-3 FM>");
+ fm_model = 2;
+ voice_alloc->max_voice = nr_voices = 18;
+ fm_info.nr_drums = 0;
+ fm_info.capabilities |= SYNTH_CAP_OPL3;
+#ifndef SCO
+ strcpy (fm_info.name, "Yamaha OPL-3");
+#endif
+
+ for (i = 0; i < 18; i++)
+ if (physical_voices[i].ioaddr == USE_LEFT)
+ physical_voices[i].ioaddr = left_address;
+ else
+ physical_voices[i].ioaddr = right_address;
+
+
+ opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /*
+ * Enable
+ * OPL-3
+ * mode
+ */
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /*
+ * Select
+ * all
+ * 2-OP
+ * *
+ * voices
+ */
+ }
+ else
+ {
+ printk (" <Yamaha 2-OP FM>");
+ fm_model = 1;
+ voice_alloc->max_voice = nr_voices = 9;
+ fm_info.nr_drums = 0;
+
+ for (i = 0; i < 18; i++)
+ physical_voices[i].ioaddr = left_address;
+ };
+
+ already_initialized = 1;
+ for (i = 0; i < SBFM_MAXINSTR; i++)
+ instrmap[i].channel = -1;
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/opl3.h b/sys/i386/isa/sound/opl3.h
new file mode 100644
index 000000000000..ea7901fc60a5
--- /dev/null
+++ b/sys/i386/isa/sound/opl3.h
@@ -0,0 +1,260 @@
+/*
+ * opl3.h - Definitions of the OPL-3 registers
+ *
+ * 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.
+ *
+ */
+
+/*
+ * The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ * of the right side.
+ *
+ * Another special register at the right side is at offset 4. It contains
+ * a bit mask defining which voices are used as 4 OP voices.
+ *
+ * The percussive mode is implemented in the left side only.
+ *
+ * With the above exeptions the both sides can be operated independently.
+ *
+ * A 4 OP voice can be created by setting the corresponding
+ * bit at offset 4 of the right side.
+ *
+ * For example setting the rightmost bit (0x01) changes the
+ * first voice on the right side to the 4 OP mode. The fourth
+ * voice is made inaccessible.
+ *
+ * If a voice is set to the 2 OP mode, it works like 2 OP modes
+ * of the original YM3812 (AdLib). In addition the voice can
+ * be connected the left, right or both stereo channels. It can
+ * even be left unconnected. This works with 4 OP voices also.
+ *
+ * The stereo connection bits are located in the FEEDBACK_CONNECTION
+ * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ * in the second half of the voice.
+ */
+
+/*
+ * Register numbers for the global registers
+ */
+
+#define TEST_REGISTER 0x01
+#define ENABLE_WAVE_SELECT 0x20
+
+#define TIMER1_REGISTER 0x02
+#define TIMER2_REGISTER 0x03
+#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
+#define IRQ_RESET 0x80
+#define TIMER1_MASK 0x40
+#define TIMER2_MASK 0x20
+#define TIMER1_START 0x01
+#define TIMER2_START 0x02
+
+#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
+#define RIGHT_4OP_0 0x01
+#define RIGHT_4OP_1 0x02
+#define RIGHT_4OP_2 0x04
+#define LEFT_4OP_0 0x08
+#define LEFT_4OP_1 0x10
+#define LEFT_4OP_2 0x20
+
+#define OPL3_MODE_REGISTER 0x05 /* Right side */
+#define OPL3_ENABLE 0x01
+
+#define KBD_SPLIT_REGISTER 0x08 /* Left side */
+#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
+#define KEYBOARD_SPLIT 0x40
+
+#define PERCUSSION_REGISTER 0xbd /* Left side only */
+#define TREMOLO_DEPTH 0x80
+#define VIBRATO_DEPTH 0x40
+#define PERCUSSION_ENABLE 0x20
+#define BASSDRUM_ON 0x10
+#define SNAREDRUM_ON 0x08
+#define TOMTOM_ON 0x04
+#define CYMBAL_ON 0x02
+#define HIHAT_ON 0x01
+
+/*
+ * Offsets to the register banks for operators. To get the
+ * register number just add the operator offset to the bank offset
+ *
+ * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+ #define AM_VIB 0x20
+ #define TREMOLO_ON 0x80
+ #define VIBRATO_ON 0x40
+ #define SUSTAIN_ON 0x20
+ #define KSR 0x10 /* Key scaling rate */
+ #define MULTIPLE_MASK 0x0f /* Frequency multiplier */
+
+ /*
+ * KSL/Total level (0x40 to 0x55)
+ */
+#define KSL_LEVEL 0x40
+#define KSL_MASK 0xc0 /* Envelope scaling bits */
+#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
+
+/*
+ * Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY 0x60
+#define ATTACK_MASK 0xf0
+#define DECAY_MASK 0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE 0x80
+#define SUSTAIN_MASK 0xf0
+#define RELEASE_MASK 0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT 0xe0
+
+/*
+ * Offsets to the register banks for voices. Just add to the
+ * voice number to get the register number.
+ *
+ * F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW 0xa0
+
+/*
+ * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK 0xb0
+#define KEYON_BIT 0x20
+#define BLOCKNUM_MASK 0x1c
+#define FNUM_HIGH_MASK 0x03
+
+/*
+ * Feedback / Connection (0xc0 to 0xc8)
+ *
+ * These registers have two new bits when the OPL-3 mode
+ * is selected. These bits controls connecting the voice
+ * to the stereo channels. For 4 OP voices this bit is
+ * defined in the second half of the voice (add 3 to the
+ * register offset).
+ *
+ * For 4 OP voices the connection bit is used in the
+ * both halfs (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION 0xc0
+#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
+#define CONNECTION_BIT 0x01
+/*
+ * In the 4 OP mode there is four possible configurations how the
+ * operators can be connected together (in 2 OP modes there is just
+ * AM or FM). The 4 OP connection mode is defined by the rightmost
+ * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs.
+ *
+ * First half Second half Mode
+ *
+ * +---+
+ * v |
+ * 0 0 >+-1-+--2--3--4-->
+ *
+ *
+ *
+ * +---+
+ * | |
+ * 0 1 >+-1-+--2-+
+ * |->
+ * >--3----4-+
+ *
+ * +---+
+ * | |
+ * 1 0 >+-1-+-----+
+ * |->
+ * >--2--3--4-+
+ *
+ * +---+
+ * | |
+ * 1 1 >+-1-+--+
+ * |
+ * >--2--3-+->
+ * |
+ * >--4----+
+ */
+#define STEREO_BITS 0x30 /* OPL-3 only */
+#define VOICE_TO_LEFT 0x10
+#define VOICE_TO_RIGHT 0x20
+
+/*
+ * Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+ unsigned char voice_num;
+ unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+ unsigned short ioaddr; /* I/O port (left or right side) */
+ unsigned char op[4]; /* Operator offsets */
+ };
+
+/*
+ * There is 18 possible 2 OP voices
+ * (9 in the left and 9 in the right).
+ * The first OP is the modulator and 2nd is the carrier.
+ *
+ * The first three voices in the both sides may be connected
+ * with another voice to a 4 OP voice. For example voice 0
+ * can be connected with voice 3. The operators of voice 3 are
+ * used as operators 3 and 4 of the new 4 OP voice.
+ * In this case the 2 OP voice number 0 is the 'first half' and
+ * voice 3 is the second.
+ */
+
+#define USE_LEFT 0
+#define USE_RIGHT 1
+
+static struct physical_voice_info physical_voices[18] =
+{
+/* No Mode Side OP1 OP2 OP3 OP4 */
+/* --------------------------------------------------- */
+ { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
+ { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
+ { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
+
+ { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
+ { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
+ { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
+};
diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h
new file mode 100644
index 000000000000..0b0ac6295c2f
--- /dev/null
+++ b/sys/i386/isa/sound/os.h
@@ -0,0 +1,306 @@
+#ifndef _OS_H_
+#define _OS_H_
+/*
+ * OS specific settings for 386BSD
+ *
+ * This chould be used as an example when porting the driver to a new
+ * operating systems.
+ *
+ * What you should do is to rewrite the soundcard.c and os.h (this file).
+ * You should create a new subdirectory and put these two files there.
+ * In addition you have to do a makefile.<OS>.
+ *
+ * If you have to make changes to other than these two files, please contact me
+ * before making the changes. It's possible that I have already made the
+ * change.
+ */
+
+/*
+ * Insert here the includes required by your kernel.
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "proc.h"
+#include "user.h"
+#include "conf.h"
+#include "file.h"
+#include "uio.h"
+/* #include "kernel.h" */
+#include "syslog.h"
+#include "errno.h"
+#include "malloc.h"
+#include "buf.h"
+#include "i386/isa/isa_device.h"
+
+/* These few lines are used by 386BSD (only??). */
+
+#if NSND > 0
+#define KERNEL_SOUNDCARD
+#else
+#undef KERNEL_SOUNDCARD
+#endif
+
+
+/*
+ * Rest of the file is compiled only if the driver is really required.
+ */
+#ifdef CONFIGURE_SOUNDCARD
+
+/*
+ * select() is currently implemented in Linux specific way. Don't enable.
+ * I don't remember what the SHORT_BANNERS means so forget it.
+ */
+
+#undef ALLOW_SELECT
+#define SHORT_BANNERS
+
+/* The soundcard.h could be in a nonstandard place so inclyde it here. */
+#include "soundcard.h"
+
+/*
+ * Here is the first portability problem. Every OS has it's own way to
+ * pass a pointer to the buffer in read() and write() calls. In Linux it's
+ * just a char*. In BSD it's struct uio. This parameter is passed to
+ * all functions called from read() or write(). Since nothing can be
+ * assumed about this structure, the driver uses set of macros for
+ * accessing the user buffer.
+ *
+ * The driver reads/writes bytes in the user buffer sequentially which
+ * means that calls like uiomove() can be used.
+ *
+ * snd_rw_buf is the type which is passed to the device file specific
+ * read() and write() calls.
+ *
+ * The following macros are used to move date to and from the
+ * user buffer. These macros should be used only when the
+ * target or source parameter has snd_rw_buf type.
+ * The offs parameter is a offset relative to the beginning of
+ * the user buffer. In Linux the offset is required but for example
+ * BSD passes the offset info in the uio structure. It could be usefull
+ * if these macros verify that the offs parameter and the value in
+ * the snd_rw_buf structure are equal.
+ */
+typedef struct uio snd_rw_buf;
+
+/*
+ * Move bytes from the buffer which the application given in a
+ * write() call.
+ * offs is position relative to the beginning of the buffer in
+ * user space. The count is number of bytes to be moved.
+ */
+#define COPY_FROM_USER(target, source, offs, count) \
+ if (uiomove(target, count, source)) { \
+ printf ("sb: Bad copyin()!\n"); \
+ } else
+/* Like COPY_FOM_USER but for writes. */
+#define COPY_TO_USER(target, offs, source, count) \
+ if (uiomove(source, count, target)) { \
+ printf ("sb: Bad copyout()!\n"); \
+ } else
+/*
+ * The following macros are like COPY_*_USER but work just with one byte (8bit),
+ * short (16 bit) or long (32 bit) at a time.
+ * The same restrictions apply than for COPY_*_USER
+ */
+#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, addr);}
+#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, addr);}
+#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, addr);}
+#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, addr);}
+
+/*
+ * The way how the ioctl arguments are passed is another nonportable thing.
+ * In Linux the argument is just a pointer directly to the user segment. On
+ * 386bsd the data is already moved to the kernel space. The following
+ * macros should handle the difference.
+ */
+
+/*
+ * IOCTL_FROM_USER is used to copy a record pointed by the argument to
+ * a buffer in the kernel space. On 386bsd it can be done just by calling
+ * memcpy. With Linux a memcpy_from_fs should be called instead.
+ * Parameters of the following macros are like in the COPY_*_USER macros.
+ */
+
+/*
+ * When the ioctl argument points to a record or array (longer than 32 bits),
+ * the macros IOCTL_*_USER are used. It's assumed that the source and target
+ * parameters are direct memory addresses.
+ */
+#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);}
+#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);}
+/* The following macros are used if the ioctl argument points to 32 bit int */
+#define IOCTL_IN(arg) (*(int*)arg)
+#define IOCTL_OUT(arg, ret) *(int*)arg = ret
+
+/*
+ * When the driver displays something to the console, printk() will be called.
+ * The name can be changed here.
+ */
+#define printk printf
+
+/*
+ * The following macros define an interface to the process management.
+ */
+
+struct snd_wait {
+ int mode; int aborting;
+ };
+
+/*
+ * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define
+ * a structure which can be passed as a parameter to a sleep(). The second
+ * parameter is name of a flag variable (must be defined as int).
+ */
+#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \
+ static volatile struct snd_wait flag = {0}
+/* Like the above but defines an array of wait queues and flags */
+#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \
+ static volatile struct snd_wait flag = {{0}}
+
+#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
+#define SET_ABORT_FLAG(q, f) f.aborting = 1
+#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
+#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP)
+/*
+ * This driver handles interrupts little bit nonstandard way. The following
+ * macro is used to test if the current process has received a signal which
+ * is aborts the process. This macro is called from close() to see if the
+ * buffers should be discarded. If this kind info is not available, a constant
+ * 1 or 0 could be returned (1 should be better than 0).
+ * I'm not sure if the following is correct for 386BSD.
+ */
+#define PROCESS_ABORTING(q, f) (f.aborting | curproc->p_sig)
+
+/*
+ * The following macro calls sleep. It should be implemented such that
+ * the process is resumed if it receives a signal. The following is propably
+ * not the way how it should be done on 386bsd.
+ * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(),
+ * and the second is a workarea parameter. The third is a timeout
+ * in ticks. Zero means no timeout.
+ */
+#define DO_SLEEP(q, f, time_limit) \
+ { \
+ int flag, chn; \
+ f.mode = WK_SLEEP; \
+ q = &chn; \
+ flag=tsleep(&chn, (PRIBIO-5)|PCATCH, "sndint", time_limit); \
+ if(flag == ERESTART) f.aborting = 1;\
+ else f.aborting = 0;\
+ f.mode &= ~WK_SLEEP; \
+ }
+/* An the following wakes up a process */
+#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wakeup(q);}
+
+/*
+ * Timing macros. This driver assumes that there is a timer running in the
+ * kernel. The timer should return a value which is increased once at every
+ * timer tick. The macro HZ should return the number of such ticks/sec.
+ */
+
+#ifndef HZ
+extern int hz;
+#define HZ hz
+#endif
+
+/*
+ * GET_TIME() returns current value of the counter incremented at timer
+ * ticks. This can overflow, so the timeout might be real big...
+ *
+ */
+unsigned long get_time(void);
+#define GET_TIME() get_time()
+/*#define GET_TIME() (lbolt) /* Returns current time (1/HZ secs since boot) */
+
+/*
+ * The following three macros are called before and after atomic
+ * code sequences. The flags parameter has always type of unsigned long.
+ * The macro DISABLE_INTR() should ensure that all interrupts which
+ * may invoke any part of the driver (timer, soundcard interrupts) are
+ * disabled.
+ * RESTORE_INTR() should return the interrupt status back to the
+ * state when DISABLE_INTR() was called. The flags parameter is
+ * a variable which can carry 32 bits of state information between
+ * DISABLE_INTR() and RESTORE_INTR() calls.
+ */
+#define DISABLE_INTR(flags) flags = splhigh()
+#define RESTORE_INTR(flags) splx(flags)
+
+/*
+ * INB() and OUTB() should be obvious. NOTE! The order of
+ * paratemeters of OUTB() is different than on some other
+ * operating systems.
+ */
+
+#define INB inb
+/*
+ * The outb(0, 0x80) is just for slowdown. It's bit unsafe since
+ * this address could be used for something usefull.
+ */
+#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);}
+
+/* memcpy() was not defined og 386bsd. Lets define it here */
+#define memcpy(d, s, c) bcopy(s, d, c)
+
+/*
+ * When a error (such as EINVAL) is returned by a function,
+ * the following macro is used. The driver assumes that a
+ * error is signalled by returning a negative value.
+ */
+
+#define RET_ERROR(err) -(err)
+
+/*
+ KERNEL_MALLOC() allocates requested number of memory and
+ KERNEL_FREE is used to free it.
+ These macros are never called from interrupt, in addition the
+ nbytes will never be more than 4096 bytes. Generally the driver
+ will allocate memory in blocks of 4k. If the kernel has just a
+ page level memory allocation, 4K can be safely used as the size
+ (the nbytes parameter can be ignored).
+*/
+#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK)
+#define KERNEL_FREE(addr) free(addr, M_TEMP)
+
+/*
+ * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr)
+ * returns size bytes of
+ * (kernel virtual) memory which will never get freed by the driver.
+ * This macro is called only during boot. The linux_ptr is a linux specific
+ * parameter which should be ignored in other operating systems.
+ * The mem_ptr is a pointer variable where the macro assigns pointer to the
+ * memory area. The type is the type of the mem_ptr.
+ */
+#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \
+ {mem_ptr = (typecast)malloc(size, M_DEVBUF, M_NOWAIT); \
+ if (!mem_ptr)panic("SOUND: Cannot allocate memory\n");}
+
+/*
+ * The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if
+ * required. The name is the variable/name to be used and the proc is
+ * the procedure to be called when the timer expires.
+ */
+
+#define DEFINE_TIMER(name, proc)
+
+/*
+ * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks.
+ */
+
+#define ACTIVATE_TIMER(name, proc, time) \
+ timeout(proc, 0, time);
+/*
+ * The rest of this file is not complete yet. The functions using these
+ * macros will not work
+ */
+#define ALLOC_DMA_CHN(chn) (0)
+#define RELEASE_DMA_CHN(chn) (0)
+#define DMA_MODE_READ 0
+#define DMA_MODE_WRITE 1
+#define RELEASE_IRQ(irq_no)
+
+#endif
+#endif
diff --git a/sys/i386/isa/sound/pas.h b/sys/i386/isa/sound/pas.h
new file mode 100644
index 000000000000..70c61dc57f5c
--- /dev/null
+++ b/sys/i386/isa/sound/pas.h
@@ -0,0 +1,250 @@
+/* */
+/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */
+/* */
+/* Feel free to use this header file in any application you create that has support for the Media Vision */
+/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */
+/* */
+/* - cmetz@thor.tjhsst.edu */
+/* */
+/* Notes: */
+/* */
+/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */
+/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */
+/* they don't actually have a direct connection. */
+/* */
+/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */
+/* PAS cards are pretty defunct now, so no attempt is made here to support them. */
+/* */
+/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */
+/* incompatibilities, there still are differences that need to be accounted for. */
+/* */
+/* Card CD-ROM interface PCM chip Mixer chip FM chip */
+/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
+/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */
+/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */
+/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */
+/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
+/* */
+#define PAS_DEFAULT_BASE 0x388
+
+/* Symbolic Name Value R W Subsystem Description */
+#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */
+#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */
+#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */
+#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */
+#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */
+#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */
+
+#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */
+#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */
+#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */
+#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */
+#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */
+#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */
+
+#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */
+ #define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */
+ #define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */
+ #define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */
+ #define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */
+ #define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */
+ #define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */
+ #define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */
+#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */
+ #define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */
+ #define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */
+#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */
+ #define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */
+#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */
+
+#define IO_CONFIGURATION_1 0xF388 /* R W Control */
+ #define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */
+#define IO_CONFIGURATION_2 0xF389 /* R W Control */
+ #define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */
+#define IO_CONFIGURATION_3 0xF38A /* R W Control */
+ #define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */
+
+#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */
+ #define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */
+ #define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */
+ #define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */
+ #define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */
+ #define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */
+#define EMULATION_ADDRESS 0xF789 /* R W Control */
+ #define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */
+ #define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */
+#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */
+ #define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */
+ #define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */
+ #define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */
+
+#define OPERATION_MODE_1 0xEF8B /* R Control */
+ #define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */
+ #define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */
+ #define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */
+#define OPERATION_MODE_2 0xFF8B /* R Control */
+ #define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */
+ #define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */
+ #define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */
+
+#define INTERRUPT_MASK 0x0B8B /* R W Control */
+ #define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */
+ #define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */
+ #define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */
+ #define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */
+ #define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */
+ #define I_M_BOARD_REV 0xE0 /* R Control Board revision */
+
+#define INTERRUPT_STATUS 0x0B89 /* R W Control */
+ #define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */
+ #define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */
+ #define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */
+ #define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */
+ #define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */
+ #define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */
+ #define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */
+ #define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */
+
+#define FILTER_FREQUENCY 0x0B8A /* R W Control */
+ #define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */
+#if 0
+ struct { /* R W Mixer Filter translation */
+ unsigned int freq:24;
+ unsigned int value:8;
+ } F_F_FILTER_translate[] =
+ { { 73500, 0x01 }, /* 73500Hz - divide by 16 */
+ { 65333, 0x02 }, /* 65333Hz - divide by 18 */
+ { 49000, 0x09 }, /* 49000Hz - divide by 24 */
+ { 36750, 0x11 }, /* 36750Hz - divide by 32 */
+ { 24500, 0x19 }, /* 24500Hz - divide by 48 */
+ { 18375, 0x07 }, /* 18375Hz - divide by 64 */
+ { 12783, 0x0f }, /* 12783Hz - divide by 92 */
+ { 12250, 0x04 }, /* 12250Hz - divide by 96 */
+ { 9188, 0x17 }, /* 9188Hz - divide by 128 */
+ { 6125, 0x1f }, /* 6125Hz - divide by 192 */
+ };
+#endif
+ #define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */
+ #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */
+ #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */
+
+#define PAS_NONE 0
+#define PAS_PLUS 1
+#define PAS_CDPC 2
+#define PAS_16 3
+#define PAS_16D 4
+
+#ifdef DEFINE_TRANSLATIONS
+ unsigned char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
+ { 4, 1, 2, 3, 0, 5, 6, 7 };
+ unsigned char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 };
+ unsigned char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
+ { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 };
+ unsigned char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
+ { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 };
+ unsigned char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
+ { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 };
+ unsigned char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 };
+#else
+ extern unsigned char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */
+ extern unsigned char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */
+ extern unsigned char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */
+ extern unsigned char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */
+ extern unsigned char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */
+ extern unsigned char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */
+#endif
+
+#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */
+ #define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */
+ #define P_M_MV508_DATA 0x00
+ #define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */
+ #define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */
+ #define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */
+ #define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */
+ #define P_M_MV508_VOLUME 0x00
+
+ #define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */
+ #define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */
+
+ #define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */
+ #define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */
+ #define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */
+ #define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */
+ #define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */
+
+ #define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */
+ #define P_M_MV508_ENHANCE_BITS 0x03
+ #define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */
+ #define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */
+ #define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */
+ #define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */
+
+ #define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */
+ #define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */
+ #define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */
+ #define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */
+ #define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */
+ #define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */
+ #define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */
+ #define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */
+
+#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */
+ #define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */
+ #define S_M_FM_RESET 0x02 /* R W FM FM chip reset */
+ #define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */
+ #define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */
+ #define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */
+ #define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */
+
+#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */
+ #define P_C_MIXER_CROSS_FIELD 0x0f
+ #define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */
+ #define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */
+ #define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */
+ #define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */
+ #define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */
+ #define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */
+ #define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */
+ #define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */
+ #define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */
+ #define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */
+
+#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */
+ #define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */
+ #define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */
+ #define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */
+
+ /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */
+ #define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */
+ #define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */
+
+ #define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */
+
+#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */
+#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */
+
+#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */
+ #define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */
+ #define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */
+ #define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */
+ #define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */
+ #define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */
+ #define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */
+ #define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */
+ #define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */
+
+#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */
+ #define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */
+ #define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */
+ #define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */
+ #define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */
+ #define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */
+ #define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */
+ #define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */
+ #define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */
+
+#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */
+#define MIDI_DATA 0x178A /* R W MIDI Midi data register */
+#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */
diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c
new file mode 100644
index 000000000000..1bfd73fcc404
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_card.c
@@ -0,0 +1,407 @@
+#define _PAS2_CARD_C_
+/*
+ * sound/pas2_card.c
+ *
+ * Detection routine for the Pro Audio Spectrum cards.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#define DEFINE_TRANSLATIONS
+#include "pas.h"
+
+/*
+ * The Address Translation code is used to convert I/O register addresses to
+ * be relative to the given base -register
+ */
+
+int translat_code;
+static int pas_intr_mask = 0;
+static int pas_irq = 0;
+
+static char pas_model;
+static char *pas_model_names[] =
+{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
+
+/*
+ * pas_read() and pas_write() are equivalents of INB() and OUTB()
+ */
+/*
+ * These routines perform the I/O address translation required
+ */
+/*
+ * to support other than the default base address
+ */
+
+unsigned char
+pas_read (int ioaddr)
+{
+ return INB (ioaddr ^ translat_code);
+}
+
+void
+pas_write (unsigned char data, int ioaddr)
+{
+ OUTB (data, ioaddr ^ translat_code);
+}
+
+void
+pas2_msg (char *foo)
+{
+ printk (" PAS2: %s.\n", foo);
+}
+
+/******************* Begin of the Interrupt Handler ********************/
+
+void
+pasintr (int unused)
+{
+ int status;
+
+ status = pas_read (INTERRUPT_STATUS);
+ pas_write (status, INTERRUPT_STATUS); /*
+ * Clear interrupt
+ */
+
+ if (status & I_S_PCM_SAMPLE_BUFFER_IRQ)
+ {
+#ifndef EXCLUDE_AUDIO
+ pas_pcm_interrupt (status, 1);
+#endif
+ status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ;
+ }
+ if (status & I_S_MIDI_IRQ)
+ {
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+ pas_midi_interrupt ();
+#endif
+#endif
+ status &= ~I_S_MIDI_IRQ;
+ }
+
+}
+
+int
+pas_set_intr (int mask)
+{
+ int err;
+
+ if (!mask)
+ return 0;
+
+ if (!pas_intr_mask)
+ {
+ if ((err = snd_set_irq_handler (pas_irq, pasintr)) < 0)
+ return err;
+ }
+ pas_intr_mask |= mask;
+
+ pas_write (pas_intr_mask, INTERRUPT_MASK);
+ return 0;
+}
+
+int
+pas_remove_intr (int mask)
+{
+ if (!mask)
+ return 0;
+
+ pas_intr_mask &= ~mask;
+ pas_write (pas_intr_mask, INTERRUPT_MASK);
+
+ if (!pas_intr_mask)
+ {
+ snd_release_irq (pas_irq);
+ }
+ return 0;
+}
+
+/******************* End of the Interrupt handler **********************/
+
+/******************* Begin of the Initialization Code ******************/
+
+int
+config_pas_hw (struct address_info *hw_config)
+{
+ char ok = 1;
+ unsigned int_ptrs; /* scsi/sound interrupt pointers */
+
+ pas_irq = hw_config->irq;
+
+ pas_write (0x00, INTERRUPT_MASK);
+
+ pas_write (0x36, SAMPLE_COUNTER_CONTROL); /*
+ * Local timer control *
+ * register
+ */
+
+ pas_write (0x36, SAMPLE_RATE_TIMER); /*
+ * Sample rate timer (16 bit)
+ */
+ pas_write (0, SAMPLE_RATE_TIMER);
+
+ pas_write (0x74, SAMPLE_COUNTER_CONTROL); /*
+ * Local timer control *
+ * register
+ */
+
+ pas_write (0x74, SAMPLE_BUFFER_COUNTER); /*
+ * Sample count register (16
+ * * bit)
+ */
+ pas_write (0, SAMPLE_BUFFER_COUNTER);
+
+ pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY);
+ pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL);
+ pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /*
+ * |
+ * S_M_OPL3_DUAL_MONO
+ */ , SERIAL_MIXER);
+
+ pas_write (I_C_1_BOOT_RESET_ENABLE, IO_CONFIGURATION_1);
+
+ if (pas_irq < 0 || pas_irq > 15)
+ {
+ printk ("PAS2: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ }
+ else
+ {
+ int_ptrs = pas_read (IO_CONFIGURATION_3);
+ int_ptrs |= I_C_3_PCM_IRQ_translate[pas_irq] & 0xf;
+ pas_write (int_ptrs, IO_CONFIGURATION_3);
+ if (!I_C_3_PCM_IRQ_translate[pas_irq])
+ {
+ printk ("PAS2: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ }
+ }
+
+ if (hw_config->dma < 0 || hw_config->dma > 7)
+ {
+ printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ }
+ else
+ {
+ pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2);
+ if (!I_C_2_PCM_DMA_translate[hw_config->dma])
+ {
+ printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ }
+ }
+
+ /*
+ * This fixes the timing problems of the PAS due to the Symphony chipset
+ * as per Media Vision. Only define this if your PAS doesn't work correctly.
+ */
+#ifdef SYMPHONY_PAS
+ OUTB (0x05, 0xa8);
+ OUTB (0x60, 0xa9);
+#endif
+
+#ifdef BROKEN_BUS_CLOCK
+ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1);
+#else
+ /*
+ * pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1);
+ */
+ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1);
+#endif
+ pas_write (0x18, SYSTEM_CONFIGURATION_3); /*
+ * ???
+ */
+
+ pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /*
+ * Sets mute
+ * off and *
+ * selects
+ * filter
+ * rate * of
+ * 17.897 kHz
+ */
+
+ if (pas_model == PAS_16 || pas_model == PAS_16D)
+ pas_write (8, PRESCALE_DIVIDER);
+ else
+ pas_write (0, PRESCALE_DIVIDER);
+
+ pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
+ pas_write (5, PARALLEL_MIXER);
+
+#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
+
+ {
+ struct address_info *sb_config;
+
+ if ((sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ unsigned char irq_dma;
+
+ /*
+ * Turn on Sound Blaster compatibility
+ */
+ /*
+ * bit 1 = SB emulation
+ */
+ /*
+ * bit 0 = MPU401 emulation (CDPC only :-( )
+ */
+ pas_write (0x02, COMPATIBILITY_ENABLE);
+
+ /*
+ * "Emulation address"
+ */
+ pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS);
+
+ if (!E_C_SB_DMA_translate[sb_config->dma])
+ printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n",
+ sb_config->dma);
+
+ if (!E_C_SB_IRQ_translate[sb_config->irq])
+ printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n",
+ sb_config->irq);
+
+ irq_dma = E_C_SB_DMA_translate[sb_config->dma] |
+ E_C_SB_IRQ_translate[sb_config->irq];
+
+ pas_write (irq_dma, EMULATION_CONFIGURATION);
+ }
+ }
+#endif
+
+ if (!ok)
+ pas2_msg ("Driver not enabled");
+
+ return ok;
+}
+
+int
+detect_pas_hw (struct address_info *hw_config)
+{
+ unsigned char board_id, foo;
+
+ /*
+ * WARNING: Setting an option like W:1 or so that disables warm boot reset
+ * of the card will screw up this detect code something fierce. Adding code
+ * to handle this means possibly interfering with other cards on the bus if
+ * you have something on base port 0x388. SO be forewarned.
+ */
+
+ OUTB (0xBC, MASTER_DECODE); /*
+ * Talk to first board
+ */
+ OUTB (hw_config->io_base >> 2, MASTER_DECODE); /*
+ * Set base address
+ */
+ translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base;
+ pas_write (1, WAIT_STATE); /*
+ * One wait-state
+ */
+
+ board_id = pas_read (INTERRUPT_MASK);
+
+ if (board_id == 0xff)
+ return 0;
+
+ /*
+ * We probably have a PAS-series board, now check for a PAS2-series board
+ * by trying to change the board revision bits. PAS2-series hardware won't
+ * let you do this - the bits are read-only.
+ */
+
+ foo = board_id ^ 0xe0;
+
+ pas_write (foo, INTERRUPT_MASK);
+ foo = INB (INTERRUPT_MASK);
+ pas_write (board_id, INTERRUPT_MASK);
+
+ if (board_id != foo) /*
+ * Not a PAS2
+ */
+ return 0;
+
+ pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f];
+
+ return pas_model;
+}
+
+long
+attach_pas_card (long mem_start, struct address_info *hw_config)
+{
+ pas_irq = hw_config->irq;
+
+ if (detect_pas_hw (hw_config))
+ {
+
+ if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]))
+ {
+ printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
+ }
+
+ if (config_pas_hw (hw_config))
+ {
+
+#ifndef EXCLUDE_AUDIO
+ mem_start = pas_pcm_init (mem_start, hw_config);
+#endif
+
+#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
+
+ sb_dsp_disable_midi (); /*
+ * The SB emulation don't support *
+ * midi
+ */
+#endif
+
+#ifndef EXCLUDE_YM3812
+ enable_opl3_mode (0x388, 0x38a, 0);
+#endif
+
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+ mem_start = pas_midi_init (mem_start);
+#endif
+#endif
+
+ pas_init_mixer ();
+ }
+ }
+
+ return mem_start;
+}
+
+int
+probe_pas (struct address_info *hw_config)
+{
+ return detect_pas_hw (hw_config);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c
new file mode 100644
index 000000000000..1c6bf5937a03
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_midi.c
@@ -0,0 +1,338 @@
+/*
+ * sound/pas2_midi.c
+ *
+ * The low level driver for the PAS Midi Interface.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI)
+
+static int midi_busy = 0, input_opened = 0;
+static int my_dev;
+static volatile int ofifo_bytes = 0;
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static int
+pas_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int err;
+ unsigned long flags;
+ unsigned char ctrl;
+
+
+ if (midi_busy)
+ {
+ printk ("PAS2: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ /*
+ * Reset input and output FIFO pointers
+ */
+ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO,
+ MIDI_CONTROL);
+
+ DISABLE_INTR (flags);
+
+ if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0)
+ return err;
+
+ /*
+ * Enable input available and output FIFO empty interrupts
+ */
+
+ ctrl = 0;
+ input_opened = 0;
+ midi_input_intr = input;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ ctrl |= M_C_ENA_INPUT_IRQ;/*
+ * Enable input
+ */
+ input_opened = 1;
+ }
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ ctrl |= M_C_ENA_OUTPUT_IRQ | /*
+ * Enable output
+ */
+ M_C_ENA_OUTPUT_HALF_IRQ;
+ }
+
+ pas_write (ctrl,
+ MIDI_CONTROL);
+
+ /*
+ * Acknowledge any pending interrupts
+ */
+
+ pas_write (0xff, MIDI_STATUS);
+ ofifo_bytes = 0;
+
+ RESTORE_INTR (flags);
+
+ midi_busy = 1;
+ qlen = qhead = qtail = 0;
+ return 0;
+}
+
+static void
+pas_midi_close (int dev)
+{
+
+ /*
+ * Reset FIFO pointers, disable intrs
+ */
+ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL);
+
+ pas_remove_intr (I_M_MIDI_IRQ_ENABLE);
+ midi_busy = 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+ int fifo_space, x;
+
+ fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f;
+
+ if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /*
+ * Fifo
+ * full
+ */
+ {
+ return 0; /*
+ * Upper layer will call again
+ */
+ }
+
+ ofifo_bytes++;
+
+ pas_write (midi_byte, MIDI_DATA);
+
+ return 1;
+}
+
+static int
+pas_midi_out (int dev, unsigned char midi_byte)
+{
+
+ unsigned long flags;
+
+ /*
+ * Drain the local queue first
+ */
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Output the byte if the local queue is empty.
+ */
+
+ if (!qlen)
+ if (dump_to_midi (midi_byte))
+ return 1; /*
+ * OK
+ */
+
+ /*
+ * Put to the local queue
+ */
+
+ if (qlen >= 256)
+ return 0; /*
+ * Local queue full
+ */
+
+ DISABLE_INTR (flags);
+
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+pas_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+pas_midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+pas_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+pas_midi_kick (int dev)
+{
+ ofifo_bytes = 0;
+}
+
+static int
+pas_buffer_status (int dev)
+{
+ return !qlen;
+}
+
+#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations pas_midi_operations =
+{
+ {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
+ &std_midi_synth,
+ pas_midi_open,
+ pas_midi_close,
+ pas_midi_ioctl,
+ pas_midi_out,
+ pas_midi_start_read,
+ pas_midi_end_read,
+ pas_midi_kick,
+ NULL, /*
+ * command
+ */
+ pas_buffer_status,
+ NULL
+};
+
+long
+pas_midi_init (long mem_start)
+{
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &pas_midi_operations;
+ return mem_start;
+}
+
+void
+pas_midi_interrupt (void)
+{
+ unsigned char stat;
+ int i, incount;
+ unsigned long flags;
+
+ stat = pas_read (MIDI_STATUS);
+
+ if (stat & M_S_INPUT_AVAIL) /*
+ * Input byte available
+ */
+ {
+ incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /*
+ * Input FIFO count
+ */
+ if (!incount)
+ incount = 16;
+
+ for (i = 0; i < incount; i++)
+ if (input_opened)
+ {
+ midi_input_intr (my_dev, pas_read (MIDI_DATA));
+ }
+ else
+ pas_read (MIDI_DATA); /*
+ * Flush
+ */
+ }
+
+ if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY))
+ {
+ if (!(stat & M_S_OUTPUT_EMPTY))
+ {
+ ofifo_bytes = 8;
+ }
+ else
+ {
+ ofifo_bytes = 0;
+ }
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+ }
+
+ if (stat & M_S_FRAMING_ERROR)
+ printk ("MIDI framing error\n");
+
+ if (stat & M_S_OUTPUT_OVERRUN)
+ {
+ printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
+ ofifo_bytes = 100;
+ }
+
+ pas_write (stat, MIDI_STATUS);/*
+ * Acknowledge interrupts
+ */
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c
new file mode 100644
index 000000000000..c125b47c181a
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_mixer.c
@@ -0,0 +1,398 @@
+#define _PAS2_MIXER_C_
+
+/*
+ * sound/pas2_mixer.c
+ *
+ * Mixer routines for the Pro Audio Spectrum cards.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#include "pas.h"
+
+#define TRACE(what) /*
+ * * * (what) */
+
+extern int translat_code;
+
+static int rec_devices = (SOUND_MASK_MIC); /*
+
+
+ * * * * Default *
+ * recording * source
+ *
+ * * */
+static int mode_control = 0;
+
+#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_ALTPCM)
+
+#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
+ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
+ SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x3232, /*
+ * Master Volume
+ */
+ 0x3232, /*
+ * Bass
+ */
+ 0x3232, /*
+ * Treble
+ */
+ 0x5050, /*
+ * FM
+ */
+ 0x4b4b, /*
+ * PCM
+ */
+ 0x3232, /*
+ * PC Speaker
+ */
+ 0x4b4b, /*
+ * Ext Line
+ */
+ 0x4b4b, /*
+ * Mic
+ */
+ 0x4b4b, /*
+ * CD
+ */
+ 0x6464, /*
+ * Recording monitor
+ */
+ 0x4b4b, /*
+ * SB PCM
+ */
+ 0x6464}; /*
+
+
+ * * * * Recording level */
+
+static int
+mixer_output (int right_vol, int left_vol, int div, int bits,
+ int mixer /*
+ * Input or output mixer
+ */ )
+{
+ int left = left_vol * div / 100;
+ int right = right_vol * div / 100;
+
+ /*
+ * The Revision D cards have a problem with their MVA508 interface. The
+ * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+ * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+ * 1. We don't need to do this because the call to pas_write more than
+ * compensates for the timing problems.
+ */
+
+ if (bits & P_M_MV508_MIXER)
+ { /*
+ * Select input or output mixer
+ */
+ left |= mixer;
+ right |= mixer;
+ }
+
+ if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
+ { /*
+ * Bass and trebble are mono devices
+ */
+ pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
+ pas_write (left, PARALLEL_MIXER);
+ right_vol = left_vol;
+ }
+ else
+ {
+ pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
+ pas_write (left, PARALLEL_MIXER);
+ pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
+ pas_write (right, PARALLEL_MIXER);
+ }
+
+ return (left_vol | (right_vol << 8));
+}
+
+void
+set_mode (int new_mode)
+{
+ pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
+ pas_write (new_mode, PARALLEL_MIXER);
+
+ mode_control = new_mode;
+}
+
+static int
+pas_mixer_set (int whichDev, unsigned int level)
+{
+ int left, right, devmask, changed, i, mixer = 0;
+
+ TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+ left = level & 0x7f;
+ right = (level & 0x7f00) >> 8;
+
+ if (whichDev < SOUND_MIXER_NRDEVICES)
+ if ((1 << whichDev) & rec_devices)
+ mixer = P_M_MV508_INPUTMIX;
+ else
+ mixer = P_M_MV508_OUTPUTMIX;
+
+ switch (whichDev)
+ {
+ case SOUND_MIXER_VOLUME: /*
+ * Master volume (0-63)
+ */
+ levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0);
+ break;
+
+ /*
+ * Note! Bass and Treble are mono devices. Will use just the left
+ * channel.
+ */
+ case SOUND_MIXER_BASS: /*
+ * Bass (0-12)
+ */
+ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0);
+ break;
+ case SOUND_MIXER_TREBLE: /*
+ * Treble (0-12)
+ */
+ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0);
+ break;
+
+ case SOUND_MIXER_SYNTH: /*
+ * Internal synthesizer (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
+ break;
+ case SOUND_MIXER_PCM: /*
+ * PAS PCM (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
+ break;
+ case SOUND_MIXER_ALTPCM: /*
+ * SB PCM (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
+ break;
+ case SOUND_MIXER_SPEAKER: /*
+ * PC speaker (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
+ break;
+ case SOUND_MIXER_LINE: /*
+ * External line (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
+ break;
+ case SOUND_MIXER_CD: /*
+ * CD (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
+ break;
+ case SOUND_MIXER_MIC: /*
+ * External microphone (0-31)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
+ break;
+ case SOUND_MIXER_IMIX: /*
+ * Recording monitor (0-31) (Only available *
+ * on the Output Mixer)
+ */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
+ P_M_MV508_OUTPUTMIX);
+ break;
+ case SOUND_MIXER_RECLEV: /*
+ * Recording level (0-15)
+ */
+ levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0);
+ break;
+
+ case SOUND_MIXER_MUTE:
+ return 0;
+ break;
+
+ case SOUND_MIXER_ENHANCE:
+ i = 0;
+ level &= 0x7f;
+ if (level)
+ i = (level / 20) - 1;
+
+ mode_control &= ~P_M_MV508_ENHANCE_BITS;
+ mode_control |= P_M_MV508_ENHANCE_BITS;
+ set_mode (mode_control);
+
+ if (i)
+ i = (i + 1) * 20;
+ return i;
+ break;
+
+ case SOUND_MIXER_LOUD:
+ mode_control &= ~P_M_MV508_LOUDNESS;
+ if (level)
+ mode_control |= P_M_MV508_LOUDNESS;
+ set_mode (mode_control);
+ return !!level; /*
+ * 0 or 1
+ */
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+ changed = devmask ^ rec_devices;
+ rec_devices = devmask;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (changed & (1 << i))
+ {
+ pas_mixer_set (i, levels[i]);
+ }
+ return rec_devices;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return (levels[whichDev]);
+}
+
+/*****/
+
+static int
+getmixer (int dev, int chn)
+{
+ if (chn == P_M_MV508_RIGHT)
+ {
+ return (levels[dev] >> 8) & 0x7f;
+ }
+ else
+ {
+ return levels[dev] & 0x7f;
+ }
+}
+
+static void
+pas_mixer_reset (void)
+{
+ int foo;
+
+ TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ pas_mixer_set (foo, levels[foo]);
+
+ set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
+}
+
+int
+pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+ else
+ { /*
+ * Read parameters
+ */
+
+ switch (cmd & 0xff)
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, rec_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0); /*
+ * No special capabilities
+ */
+ break;
+
+ case SOUND_MIXER_MUTE:
+ return IOCTL_OUT (arg, 0); /*
+ * No mute yet
+ */
+ break;
+
+ case SOUND_MIXER_ENHANCE:
+ if (!(mode_control & P_M_MV508_ENHANCE_BITS))
+ return IOCTL_OUT (arg, 0);
+ return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20);
+ break;
+
+ case SOUND_MIXER_LOUD:
+ if (mode_control & P_M_MV508_LOUDNESS)
+ return IOCTL_OUT (arg, 1);
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, levels[cmd & 0xff]);
+ }
+ }
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+ pas_mixer_ioctl
+};
+
+int
+pas_init_mixer (void)
+{
+ pas_mixer_reset ();
+
+ if (num_mixers < MAX_MIXER_DEV)
+ mixer_devs[num_mixers++] = &pas_mixer_operations;
+ return 1;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c
new file mode 100644
index 000000000000..e4c9a63c384c
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_pcm.c
@@ -0,0 +1,453 @@
+#define _PAS2_PCM_C_
+/*
+ * sound/pas2_pcm.c
+ *
+ * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO)
+
+#define TRACE(WHAT) /*
+ * * * (WHAT) */
+
+#define PAS_PCM_INTRBITS (0x08)
+/*
+ * Sample buffer timer interrupt enable
+ */
+
+#define PCM_NON 0
+#define PCM_DAC 1
+#define PCM_ADC 2
+
+static unsigned long pcm_speed = 0; /* sampling rate */
+static unsigned char pcm_channels = 1; /* channels (1 or 2) */
+static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
+static unsigned char pcm_filter = 0; /* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count = 0;
+static unsigned short pcm_bitsok = 8; /* mask of OK bits */
+static int my_devnum = 0;
+
+int
+pcm_set_speed (int arg)
+{
+ int foo, tmp;
+ unsigned long flags;
+
+ if (arg > 44100)
+ arg = 44100;
+ if (arg < 5000)
+ arg = 5000;
+
+ foo = (1193180 + (arg / 2)) / arg;
+ arg = 1193180 / foo;
+
+ if (pcm_channels & 2)
+ foo = foo >> 1;
+
+ pcm_speed = arg;
+
+ tmp = pas_read (FILTER_FREQUENCY);
+
+ /*
+ * Set anti-aliasing filters according to sample rate. You reall *NEED*
+ * to enable this feature for all normal recording unless you want to
+ * experiment with aliasing effects.
+ * These filters apply to the selected "recording" source.
+ * I (pfw) don't know the encoding of these 5 bits. The values shown
+ * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+*/
+#if !defined NO_AUTO_FILTER_SET
+ tmp &= 0xe0;
+ if (pcm_speed >= 2 * 17897)
+ tmp |= 0x21;
+ else if (pcm_speed >= 2 * 15909)
+ tmp |= 0x22;
+ else if (pcm_speed >= 2 * 11931)
+ tmp |= 0x29;
+ else if (pcm_speed >= 2 * 8948)
+ tmp |= 0x31;
+ else if (pcm_speed >= 2 * 5965)
+ tmp |= 0x39;
+ else if (pcm_speed >= 2 * 2982)
+ tmp |= 0x24;
+ pcm_filter = tmp;
+#endif
+
+ DISABLE_INTR (flags);
+
+ pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
+ pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
+ pas_write (tmp, FILTER_FREQUENCY);
+
+ RESTORE_INTR (flags);
+
+ return pcm_speed;
+}
+
+int
+pcm_set_channels (int arg)
+{
+
+ if ((arg != 1) && (arg != 2))
+ return pcm_channels;
+
+ if (arg != pcm_channels)
+ {
+ pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
+
+ pcm_channels = arg;
+ pcm_set_speed (pcm_speed);/*
+ * The speed must be reinitialized
+ */
+ }
+
+ return pcm_channels;
+}
+
+int
+pcm_set_bits (int arg)
+{
+ if ((arg & pcm_bitsok) != arg)
+ return pcm_bits;
+
+ if (arg != pcm_bits)
+ {
+ pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
+
+ pcm_bits = arg;
+ }
+
+ return pcm_bits;
+}
+
+static int
+pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return pcm_set_speed (arg);
+ return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return pcm_speed;
+ return IOCTL_OUT (arg, pcm_speed);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return pcm_set_channels (arg + 1) - 1;
+ return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return pcm_set_channels (arg);
+ return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return pcm_channels;
+ return IOCTL_OUT (arg, pcm_channels);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return pcm_set_bits (arg);
+ return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return pcm_bits;
+ return IOCTL_OUT (arg, pcm_bits);
+
+ case SOUND_PCM_WRITE_FILTER: /*
+ * NOT YET IMPLEMENTED
+ */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ pcm_filter = IOCTL_IN (arg);
+ case SOUND_PCM_READ_FILTER:
+ return IOCTL_OUT (arg, pcm_filter);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+pas_pcm_reset (int dev)
+{
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
+
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
+}
+
+static int
+pas_pcm_open (int dev, int mode)
+{
+ int err;
+
+ TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
+
+ if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
+ return err;
+
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ return RET_ERROR (EBUSY);
+ }
+
+ pcm_count = 0;
+
+ return 0;
+}
+
+static void
+pas_pcm_close (int dev)
+{
+ unsigned long flags;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ pas_pcm_reset (dev);
+ DMAbuf_close_dma (dev);
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ pcm_mode = PCM_NON;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags, cnt;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+ cnt = count;
+ if (audio_devs[dev]->dmachan > 3)
+ cnt >>= 1;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /*
+ * Auto mode on. No need to react
+ */
+
+ DISABLE_INTR (flags);
+
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+
+ if (count != pcm_count)
+ {
+ pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+ pcm_count = count;
+ }
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+ pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+ pcm_mode = PCM_DAC;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+ int cnt;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+ cnt = count;
+ if (audio_devs[dev]->dmachan > 3)
+ cnt >>= 1;
+
+ if (audio_devs[my_devnum]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /*
+ * Auto mode on. No need to react
+ */
+
+ DISABLE_INTR (flags);
+
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+
+ if (count != pcm_count)
+ {
+ pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+ pcm_count = count;
+ }
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+ pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+ pcm_mode = PCM_ADC;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+static int
+pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+
+static struct audio_operations pas_pcm_operations =
+{
+ "Pro Audio Spectrum",
+ DMA_AUTOMODE,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ pas_pcm_open,
+ pas_pcm_close,
+ pas_pcm_output_block,
+ pas_pcm_start_input,
+ pas_pcm_ioctl,
+ pas_pcm_prepare_for_input,
+ pas_pcm_prepare_for_output,
+ pas_pcm_reset,
+ pas_pcm_reset,
+ NULL,
+ NULL
+};
+
+long
+pas_pcm_init (long mem_start, struct address_info *hw_config)
+{
+ TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
+
+ pcm_bitsok = 8;
+ if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
+ pcm_bitsok |= 16;
+
+ pcm_set_speed (DSP_DEFAULT_SPEED);
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations;
+ audio_devs[my_devnum]->dmachan = hw_config->dma;
+ audio_devs[my_devnum]->buffcount = 1;
+ audio_devs[my_devnum]->buffsize = 2 * DSP_BUFFSIZE;
+ }
+ else
+ printk ("PAS2: Too many PCM devices available\n");
+
+ return mem_start;
+}
+
+void
+pas_pcm_interrupt (unsigned char status, int cause)
+{
+ if (cause == 1) /*
+ * PCM buffer done
+ */
+ {
+ /*
+ * Halt the PCM first. Otherwise we don't have time to start a new
+ * block before the PCM chip proceeds to the next sample
+ */
+
+ if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE))
+ {
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+ }
+
+ switch (pcm_mode)
+ {
+
+ case PCM_DAC:
+ DMAbuf_outputintr (my_devnum, 1);
+ break;
+
+ case PCM_ADC:
+ DMAbuf_inputintr (my_devnum);
+ break;
+
+ default:
+ printk ("PAS: Unexpected PCM interrupt\n");
+ }
+ }
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c
new file mode 100644
index 000000000000..a77626609106
--- /dev/null
+++ b/sys/i386/isa/sound/patmgr.c
@@ -0,0 +1,264 @@
+/*
+ * sound/patmgr.c
+ *
+ * The patch maneger interface for the /dev/sequencer
+ *
+ * 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.
+ *
+ */
+
+#define PATMGR_C
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER)
+
+DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV],
+ server_wait_flag[MAX_SYNTH_DEV]);
+
+static struct patmgr_info *mbox[MAX_SYNTH_DEV] =
+{NULL};
+static volatile int msg_direction[MAX_SYNTH_DEV] =
+{0};
+
+static int pmgr_opened[MAX_SYNTH_DEV] =
+{0};
+
+#define A_TO_S 1
+#define S_TO_A 2
+
+DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag);
+
+int
+pmgr_open (int dev)
+{
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (pmgr_opened[dev])
+ return RET_ERROR (EBUSY);
+ pmgr_opened[dev] = 1;
+
+ RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]);
+
+ return 0;
+}
+
+void
+pmgr_release (int dev)
+{
+
+ if (mbox[dev]) /*
+ * Killed in action. Inform the client
+ */
+ {
+
+ mbox[dev]->key = PM_ERROR;
+ mbox[dev]->parm1 = RET_ERROR (EIO);
+
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ WAKE_UP (appl_proc, appl_wait_flag);
+ }
+
+ pmgr_opened[dev] = 0;
+}
+
+int
+pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+ int ok = 0;
+
+ if (count != sizeof (struct patmgr_info))
+ {
+ printk ("PATMGR%d: Invalid read count\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+ {
+ DISABLE_INTR (flags);
+
+ while (!(mbox[dev] && msg_direction[dev] == A_TO_S) &&
+ !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+ {
+ DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0);
+ }
+
+ if (mbox[dev] && msg_direction[dev] == A_TO_S)
+ {
+ COPY_TO_USER (buf, 0, (char *) mbox[dev], count);
+ msg_direction[dev] = 0;
+ ok = 1;
+ }
+
+ RESTORE_INTR (flags);
+
+ }
+
+ if (!ok)
+ return RET_ERROR (EINTR);
+ return count;
+}
+
+int
+pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+
+ if (count < 4)
+ {
+ printk ("PATMGR%d: Write count < 4\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ COPY_FROM_USER (mbox[dev], buf, 0, 4);
+
+ if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
+ {
+ int tmp_dev;
+
+ tmp_dev = ((unsigned short *) mbox[dev])[2];
+ if (tmp_dev != dev)
+ return RET_ERROR (ENXIO);
+
+ return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev],
+ buf, 4, count, 1);
+ }
+
+ if (count != sizeof (struct patmgr_info))
+ {
+ printk ("PATMGR%d: Invalid write count\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ /*
+ * If everything went OK, there should be a preallocated buffer in the
+ * mailbox and a client waiting.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev] && !msg_direction[dev])
+ {
+ COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
+ msg_direction[dev] = S_TO_A;
+
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ {
+ WAKE_UP (appl_proc, appl_wait_flag);
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return count;
+}
+
+int
+pmgr_access (int dev, struct patmgr_info *rec)
+{
+ unsigned long flags;
+ int err = 0;
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev])
+ printk (" PATMGR: Server %d mbox full. Why?\n", dev);
+ else
+ {
+ rec->key = PM_K_COMMAND;
+ mbox[dev] = rec;
+ msg_direction[dev] = A_TO_S;
+
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
+ {
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
+
+ if (msg_direction[dev] != S_TO_A)
+ {
+ rec->key = PM_ERROR;
+ rec->parm1 = RET_ERROR (EIO);
+ }
+ else if (rec->key == PM_ERROR)
+ {
+ err = rec->parm1;
+ if (err > 0)
+ err = -err;
+ }
+
+ mbox[dev] = NULL;
+ msg_direction[dev] = 0;
+ }
+
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+int
+pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
+ unsigned long p3, unsigned long p4)
+{
+ unsigned long flags;
+ int err = 0;
+
+ if (!pmgr_opened[dev])
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev])
+ printk (" PATMGR: Server %d mbox full. Why?\n", dev);
+ else
+ {
+ mbox[dev] =
+ (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info));
+
+ mbox[dev]->key = PM_K_EVENT;
+ mbox[dev]->command = event;
+ mbox[dev]->parm1 = p1;
+ mbox[dev]->parm2 = p2;
+ mbox[dev]->parm3 = p3;
+ msg_direction[dev] = A_TO_S;
+
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
+ {
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
+ if (mbox[dev])
+ KERNEL_FREE (mbox[dev]);
+ mbox[dev] = NULL;
+ msg_direction[dev] = 0;
+ }
+
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pss.c b/sys/i386/isa/sound/pss.c
new file mode 100644
index 000000000000..6a0e7a8327ff
--- /dev/null
+++ b/sys/i386/isa/sound/pss.c
@@ -0,0 +1,924 @@
+/* Marc.Hoffman@analog.com
+
+ This is a pss driver.
+
+ it is based on Greg.Yukna@analog.com @file{host} for DOG
+
+ Unfortunately I can't distribute the ld file needed to
+ make the pss card to emulate the SB stuff.
+
+ I have provided a simple interface to the PSS unlike the
+ DOG version. to download a new algorithim just cat it to
+ /dev/pss 14,9.
+
+ You really need to rebuild this with the synth.ld file
+
+ get the <synth>.ld from your dos directory maybe
+ voyetra\dsp001.ld
+
+ ld2inc < synth.ld > synth-ld.h
+ (make config does the same).
+
+ rebuild
+
+ Okay if you blow things away no problem just
+
+ main(){ioctl(open("/dev/pss"),SNDCTL_PSS_RESET)};
+
+ and everything will be okay.
+
+ At first I was going to wory about applications that were using
+ the sound stuff and disallow the use of /dev/pss. But for
+ now I figured it doesn't matter.
+
+ And if you change algos all the other applications running die off
+ due to DMA problems. Yeah just pull the plug and watch em die.
+
+ If the registers get hosed
+ main(){ioctl(open("/dev/pss"),SNDCTL_PSS_SETUP_REGISTERS)};
+
+ Probably everything else can be done via mmap
+
+ Oh if you want to develope code for the ADSP-21xx or Program the
+ 1848 just send me mail and I will hook you up.
+
+ marc.hoffman@analog.com
+
+ */
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PSS)
+
+#ifndef PSS_MSS_BASE
+#define PSS_MSS_BASE 0
+#endif
+
+#ifndef PSS_MPU_BASE
+#define PSS_MPU_BASE 0
+#endif
+
+#ifndef PSS_MPU_IRQ
+#define PSS_MPU_IRQ 0
+#endif
+
+#undef DEB
+#define DEB(x) x
+
+#include "pss.h"
+
+static int pss_ok = 0;
+static int sb_ok = 0;
+
+static int pss_base;
+static int pss_irq;
+static int pss_dma;
+
+static int gamePort = 0;
+
+static int sbInt;
+static int cdPol;
+static int cdAddr = 0; /* 0x340; */
+static int cdInt = 10;
+
+/* Define these by hand in local.h */
+static int wssAddr = PSS_MSS_BASE;
+static int midiAddr = PSS_MPU_BASE;
+static int midiInt = PSS_MPU_IRQ;
+
+static int SoundPortAddress;
+static int SoundPortData;
+static int speaker = 1;
+
+
+static struct pss_speaker default_speaker =
+{0, 0, 0, PSS_STEREO};
+
+DEFINE_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
+
+#include "synth-ld.h"
+
+static int pss_download_boot (unsigned char *block, int size);
+static int pss_reset_dsp (void);
+
+static inline void
+pss_outpw (unsigned short port, unsigned short value)
+{
+ __asm__ __volatile__ ("outw %w0, %w1"
+ : /* no outputs */
+ :"a" (value), "d" (port));
+}
+
+static inline unsigned int
+pss_inpw (unsigned short port)
+{
+ unsigned int _v;
+ __asm__ __volatile__ ("inw %w1,%w0"
+ :"=a" (_v):"d" (port), "0" (0));
+
+ return _v;
+}
+
+static void
+PSS_write (int data)
+{
+ int i, limit;
+
+ limit = GET_TIME () + 10; /* The timeout is 0.1 secods */
+ /*
+ * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 5000000 && GET_TIME () < limit; i++)
+ {
+ if (pss_inpw (pss_base + PSS_STATUS) & PSS_WRITE_EMPTY)
+ {
+ pss_outpw (pss_base + PSS_DATA, data);
+ return;
+ }
+ }
+ printk ("PSS: DSP Command (%04x) Timeout.\n", data);
+ printk ("IRQ conflict???\n");
+}
+
+
+static void
+pss_setaddr (int addr, int configAddr)
+{
+ int val;
+
+ val = pss_inpw (configAddr);
+ val &= ADDR_MASK;
+ val |= (addr << 4);
+ pss_outpw (configAddr, val);
+}
+
+/*_____ pss_checkint
+ This function tests an interrupt number to see if
+ it is availible. It takes the interrupt button
+ as it's argument and returns TRUE if the interrupt
+ is ok.
+*/
+static int
+pss_checkint (int intNum)
+{
+ int val;
+ int ret;
+ int i;
+
+ /*_____ Set the interrupt bits */
+ switch (intNum)
+ {
+ case 3:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_3_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 5:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_5_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 7:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_7_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 9:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_9_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 10:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_10_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 11:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_11_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 12:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_12_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ default:
+ printk ("unknown interupt selected. %d\n", intNum);
+ return 0;
+ }
+
+ /*_____ Set the interrupt test bit */
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val |= INT_TEST_BIT;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+
+ /*_____ Check if the interrupt is in use */
+ /*_____ Do it a few times in case there is a delay */
+ ret = 0;
+ for (i = 0; i < 5; i++)
+ {
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ if (val & INT_TEST_PASS)
+ {
+ ret = 1;
+ break;
+ }
+ }
+ /*_____ Clear the Test bit and the interrupt bits */
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_TEST_BIT_MASK;
+ val &= INT_MASK;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ return (ret);
+}
+
+/*____ pss_setint
+ This function sets the correct bits in the
+ configuration register to
+ enable the chosen interrupt.
+*/
+static void
+pss_setint (int intNum, int configAddress)
+{
+ int val;
+
+ switch (intNum)
+ {
+ case 0:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ pss_outpw (configAddress, val);
+ break;
+ case 3:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_3_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 5:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_5_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 7:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_7_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 9:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_9_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 10:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_10_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 11:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_11_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 12:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_12_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ default:
+ printk ("pss_setint unkown int\n");
+ }
+}
+
+
+/*____ pss_setsbint
+ This function sets the correct bits in the
+ SoundBlaster configuration PSS register to
+ enable the chosen interrupt.
+ It takes a interrupt button as its argument.
+*/
+static void
+pss_setsbint (int intNum)
+{
+ int val;
+ int sbConfigAddress;
+
+ sbConfigAddress = pss_base + SB_CONFIG;
+ switch (intNum)
+ {
+ case 3:
+ val = pss_inpw (sbConfigAddress);
+ val &= INT_MASK;
+ val |= INT_3_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ case 5:
+ val = pss_inpw (sbConfigAddress);
+ val &= INT_MASK;
+ val |= INT_5_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ case 7:
+ val = pss_inpw (sbConfigAddress);
+ val &= INT_MASK;
+ val |= INT_7_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ default:
+ printk ("pss_setsbint: unknown_int\n");
+ }
+}
+
+/*____ pss_setsbdma
+ This function sets the correct bits in the
+ SoundBlaster configuration PSS register to
+ enable the chosen DMA channel.
+ It takes a DMA button as its argument.
+*/
+static void
+pss_setsbdma (int dmaNum)
+{
+ int val;
+ int sbConfigAddress;
+
+ sbConfigAddress = pss_base + SB_CONFIG;
+
+ switch (dmaNum)
+ {
+ case 1:
+ val = pss_inpw (sbConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_1_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ default:
+ printk ("Personal Sound System ERROR! pss_setsbdma: unknown_dma\n");
+ }
+}
+
+/*____ pss_setwssdma
+ This function sets the correct bits in the
+ WSS configuration PSS register to
+ enable the chosen DMA channel.
+ It takes a DMA button as its argument.
+*/
+static void
+pss_setwssdma (int dmaNum)
+{
+ int val;
+ int wssConfigAddress;
+
+ wssConfigAddress = pss_base + PSS_WSS_CONFIG;
+
+ switch (dmaNum)
+ {
+ case 0:
+ val = pss_inpw (wssConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_0_BITS;
+ pss_outpw (wssConfigAddress, val);
+ break;
+ case 1:
+ val = pss_inpw (wssConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_1_BITS;
+ pss_outpw (wssConfigAddress, val);
+ break;
+ case 3:
+ val = pss_inpw (wssConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_3_BITS;
+ pss_outpw (wssConfigAddress, val);
+ break;
+ default:
+ printk ("Personal Sound System ERROR! pss_setwssdma: unknown_dma\n");
+ }
+}
+
+
+/*_____ SetSpeakerOut
+ This function sets the Volume, Bass, Treble and Mode of
+ the speaker out channel.
+ */
+void
+pss_setspeaker (struct pss_speaker *spk)
+{
+ PSS_write (SET_MASTER_COMMAND);
+ if (spk->volume > PHILLIPS_VOL_MAX)
+ spk->volume = PHILLIPS_VOL_MAX;
+ if (spk->volume < PHILLIPS_VOL_MIN)
+ spk->volume = PHILLIPS_VOL_MIN;
+
+ PSS_write (MASTER_VOLUME_LEFT
+ | (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_VOLUME_RIGHT
+ | (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
+
+ if (spk->bass > PHILLIPS_BASS_MAX)
+ spk->bass = PHILLIPS_BASS_MAX;
+ if (spk->bass < PHILLIPS_BASS_MIN)
+ spk->bass = PHILLIPS_BASS_MIN;
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_BASS
+ | (PHILLIPS_BASS_CONSTANT + spk->bass / PHILLIPS_BASS_STEP));
+
+ if (spk->treb > PHILLIPS_TREBLE_MAX)
+ spk->treb = PHILLIPS_TREBLE_MAX;
+ if (spk->treb < PHILLIPS_TREBLE_MIN)
+ spk->treb = PHILLIPS_TREBLE_MIN;
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_TREBLE
+ | (PHILLIPS_TREBLE_CONSTANT + spk->treb / PHILLIPS_TREBLE_STEP));
+
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_SWITCH | spk->mode);
+}
+
+static void
+pss_init1848 (void)
+{
+ /*_____ Wait for 1848 to init */
+ while (INB (SoundPortAddress) & SP_IN_INIT);
+
+ /*_____ Wait for 1848 to autocal */
+ OUTB (SoundPortAddress, SP_TEST_AND_INIT);
+ while (INB (SoundPortData) & AUTO_CAL_IN_PROG);
+}
+
+static int
+pss_configure_registers_to_look_like_sb (void)
+{
+ pss_setaddr (wssAddr, pss_base + PSS_WSS_CONFIG);
+
+ SoundPortAddress = wssAddr + 4;
+ SoundPortData = wssAddr + 5;
+
+ DEB (printk ("Turning Game Port %s.\n",
+ gamePort ? "On" : "Off"));
+
+ /*_____ Turn on the Game port */
+ if (gamePort)
+ pss_outpw (pss_base + PSS_STATUS,
+ pss_inpw (pss_base + PSS_STATUS) | GAME_BIT);
+ else
+ pss_outpw (pss_base + PSS_STATUS,
+ pss_inpw (pss_base + PSS_STATUS) & GAME_BIT_MASK);
+
+
+ DEB (printk ("PSS attaching base %x irq %d dma %d\n",
+ pss_base, pss_irq, pss_dma));
+
+ /* Check if sb is enabled if it is check the interrupt */
+ pss_outpw (pss_base + SB_CONFIG, 0);
+
+ if (pss_irq != 0)
+ {
+ DEB (printk ("PSS Emulating Sound Blaster ADDR %04x\n", pss_base));
+ DEB (printk ("PSS SBC: attaching base %x irq %d dma %d\n",
+ SBC_BASE, SBC_IRQ, SBC_DMA));
+
+ if (pss_checkint (SBC_IRQ) == 0)
+ {
+ printk ("PSS! attach: int_error\n");
+ return 0;
+ }
+
+ pss_setsbint (SBC_IRQ);
+ pss_setsbdma (SBC_DMA);
+ sb_ok = 1;
+ }
+ else
+ {
+ sb_ok = 0;
+ printk ("PSS: sound blaster error init\n");
+ }
+
+ /* Check if cd is enabled if it is check the interrupt */
+ pss_outpw (pss_base + CD_CONFIG, 0);
+
+ if (cdAddr != 0)
+ {
+ DEB (printk ("PSS:CD drive %x irq: %d", cdAddr, cdInt));
+ if (cdInt != 0)
+ {
+ if (pss_checkint (cdInt) == 0)
+ {
+ printk ("Can't allocate cdInt %d\n", cdInt);
+ }
+ else
+ {
+ int val;
+
+ printk ("CD poll ");
+ pss_setaddr (cdAddr, pss_base + CD_CONFIG);
+ pss_setint (cdInt, pss_base + CD_CONFIG);
+
+ /* set the correct bit in the
+ configuration register to
+ set the irq polarity for the CD-Rom.
+ NOTE: This bit is in the address config
+ field, It must be configured after setting
+ the CD-ROM ADDRESS!!! */
+ val = pss_inpw (pss_base + CD_CONFIG);
+ pss_outpw (pss_base + CD_CONFIG, 0);
+ val &= CD_POL_MASK;
+ if (cdPol)
+ val |= CD_POL_BIT;
+ pss_outpw (pss_base + CD_CONFIG, val);
+ }
+ }
+ }
+
+ /* Check if midi is enabled if it is check the interrupt */
+ pss_outpw (pss_base + MIDI_CONFIG, 0);
+ if (midiAddr != 0)
+ {
+ printk ("midi init %x %d\n", midiAddr, midiInt);
+ if (pss_checkint (midiInt) == 0)
+ {
+ printk ("midi init int error %x %d\n", midiAddr, midiInt);
+ }
+ else
+ {
+ pss_setaddr (midiAddr, pss_base + MIDI_CONFIG);
+ pss_setint (midiInt, pss_base + MIDI_CONFIG);
+ }
+ }
+ return 1;
+}
+
+long
+attach_pss (long mem_start, struct address_info *hw_config)
+{
+ if (pss_ok)
+ {
+ if (hw_config)
+ {
+ printk (" <PSS-ESC614>");
+ }
+
+ return mem_start;
+ }
+
+ pss_ok = 1;
+
+ if (pss_configure_registers_to_look_like_sb () == 0)
+ return mem_start;
+
+ if (sb_ok)
+ if (pss_synthLen
+ && pss_download_boot (pss_synth, pss_synthLen))
+ {
+ if (speaker)
+ pss_setspeaker (&default_speaker);
+ pss_ok = 1;
+ }
+ else
+ pss_reset_dsp ();
+
+ return mem_start;
+}
+
+int
+probe_pss (struct address_info *hw_config)
+{
+ pss_base = hw_config->io_base;
+ pss_irq = hw_config->irq;
+ pss_dma = hw_config->dma;
+
+ if ((pss_inpw (pss_base + 4) & 0xff00) == 0x4500)
+ {
+ attach_pss (0, hw_config);
+ return 1;
+ }
+ printk (" fail base %x irq %d dma %d\n", pss_base, pss_irq, pss_dma);
+ return 0;
+}
+
+
+static int
+pss_reattach (void)
+{
+ pss_ok = 0;
+ attach_pss (0, 0);
+ return 1;
+}
+
+static int
+pss_reset_dsp ()
+{
+ unsigned long i, limit = GET_TIME () + 10;
+
+ pss_outpw (pss_base + PSS_CONTROL, 0x2000);
+
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ pss_inpw (pss_base + PSS_CONTROL);
+
+ pss_outpw (pss_base + PSS_CONTROL, 0x0000);
+
+ return 1;
+}
+
+
+static int
+pss_download_boot (unsigned char *block, int size)
+{
+ int i, limit, val, count;
+
+ printk ("PSS: downloading boot code synth.ld... ");
+
+ /*_____ Warn DSP software that a boot is coming */
+ pss_outpw (pss_base + PSS_DATA, 0x00fe);
+
+ limit = GET_TIME () + 10;
+
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ if (pss_inpw (pss_base + PSS_DATA) == 0x5500)
+ break;
+
+ pss_outpw (pss_base + PSS_DATA, *block++);
+
+ pss_reset_dsp ();
+ printk ("start ");
+
+ count = 1;
+ while (1)
+ {
+ int j;
+
+ for (j = 0; j < 327670; j++)
+ {
+ /*_____ Wait for BG to appear */
+ if (pss_inpw (pss_base + PSS_STATUS) & PSS_FLAG3)
+ break;
+ }
+
+ if (j == 327670)
+ {
+ /* It's ok we timed out when the file was empty */
+ if (count >= size)
+ break;
+ else
+ {
+ printk ("\nPSS: DownLoad timeout problems, byte %d=%d\n",
+ count, size);
+ return 0;
+ }
+ }
+ /*_____ Send the next byte */
+ pss_outpw (pss_base + PSS_DATA, *block++);
+ count++;
+ }
+
+ /*_____ Why */
+ pss_outpw (pss_base + PSS_DATA, 0);
+
+ limit = GET_TIME () + 10;
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ val = pss_inpw (pss_base + PSS_STATUS);
+
+ printk ("downloaded\n");
+
+ limit = GET_TIME () + 10;
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ {
+ val = pss_inpw (pss_base + PSS_STATUS);
+ if (val & 0x4000)
+ break;
+ }
+
+ /* now read the version */
+ for (i = 0; i < 32000; i++)
+ {
+ val = pss_inpw (pss_base + PSS_STATUS_REG);
+ if (val & PSS_READ_FULL)
+ break;
+ }
+ if (i == 32000)
+ return 0;
+
+ val = pss_inpw (pss_base + PSS_DATA_REG);
+
+ return 1;
+}
+
+
+/* The following is a simple device driver for the pss.
+ All I really care about is comunication to and from the pss.
+
+ The ability to reinitialize the <synth.ld> This will be
+ default when release is choosen.
+
+ SNDCTL_PSS_DOWNLOAD:
+
+ Okay we need to creat new minor numbers for the
+ DOWNLOAD functionality.
+
+ 14,0x19 -- /dev/pssld where a read operation would output the
+ current ld to user space
+ where a write operation would effectively
+ download a new ld.
+
+ 14,0x09 -- /dev/psecho would open up a comunication path to the
+ esc614 asic. Given the ability to send
+ messages to the asic and recive messages too.
+
+ All messages would get read and written in the
+ same manner. It would be up to the application
+ and the ld to maintain a relationship
+ of what the messages mean.
+
+ for this device we need to implement select. */
+#define CODE_BUFFER_LEN (64*1024)
+static char *code_buffer;
+static int code_length;
+
+static int lock_pss = 0;
+
+int
+pss_open (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ DEB (printk ("pss_open\n"));
+
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ if (lock_pss)
+ return 0;
+
+ lock_pss = 1;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+ if (mode == O_WRONLY)
+ {
+ printk ("pss-open for WRONLY\n");
+ code_length = 0;
+ }
+
+ RESET_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
+ return 1;
+}
+
+void
+pss_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ DEB (printk ("pss_release\n"));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+ if (mode == O_WRONLY && code_length > 0)
+ {
+#ifdef linux
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
+#endif
+ if (!pss_download_boot (code_buffer, code_length))
+ {
+ pss_reattach ();
+ }
+ }
+ lock_pss = 0;
+}
+
+int
+pss_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p;
+
+ DEB (printk ("pss_read\n"));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+ p = 0;
+ c = count;
+
+ return count - c;
+}
+
+int
+pss_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ DEB (printk ("pss_write\n"));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+ dev = dev >> 4;
+
+ if (count) /* Flush output */
+ {
+ COPY_FROM_USER (&code_buffer[code_length], buf, 0, count);
+ code_length += count;
+ }
+ return count;
+}
+
+
+int
+pss_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ DEB (printk ("pss_ioctl dev=%d cmd=%x\n", dev, cmd));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+ case SNDCTL_PSS_RESET:
+ pss_reattach ();
+ return 1;
+
+ case SNDCTL_PSS_SETUP_REGISTERS:
+ pss_configure_registers_to_look_like_sb ();
+ return 1;
+
+ case SNDCTL_PSS_SPEAKER:
+ {
+ struct pss_speaker params;
+ COPY_FROM_USER (&params, (char *) arg, 0, sizeof (struct pss_speaker));
+
+ pss_setspeaker (&params);
+ return 0;
+ }
+ default:
+ return RET_ERROR (EIO);
+ }
+}
+
+/* This is going to be used to implement
+ waiting on messages sent from the DSP and to the
+ DSP when comunication is used via the pss directly.
+
+ We need to find out if the pss can generate a diffrent
+ interupt other than the one it has been setup for.
+
+ This way we can carry on a conversation with the pss
+ on a seprate chanel. This would be usefull for debugging. */
+
+pss_select (int dev, struct fileinfo * file, int sel_type, select_table * wait)
+{
+ return 0;
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ select_wait (&pss_sleeper, wait);
+ return 0;
+ break;
+
+ case SEL_OUT:
+ select_wait (&pss_sleeper, wait);
+ return 0;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+long
+pss_init (long mem_start)
+{
+ DEB (printk ("pss_init\n"));
+ if (pss_ok)
+ {
+ code_buffer = mem_start;
+ mem_start += CODE_BUFFER_LEN;
+ }
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pss.h b/sys/i386/isa/sound/pss.h
new file mode 100644
index 000000000000..e020af6ae9b1
--- /dev/null
+++ b/sys/i386/isa/sound/pss.h
@@ -0,0 +1,371 @@
+/******************************************************************************
+
+ def.h
+
+ Version 1.3 11/2/93
+
+ Copyright (c) 1993 Analog Devices Inc. All rights reserved
+
+******************************************************************************/
+/* Port offsets from base port for Sound Blaster DSP */
+#define DSP_PORT_CMSD0 0x00 /* C/MS music voice 1-6 data port, write only */
+#define DSP_PORT_CMSR0 0x01 /* C/MS music voice 1-6 register port, write only */
+#define DSP_PORT_CMSD1 0x02 /* C/MS music voice 7-12 data port, write only */
+#define DSP_PORT_CMSR1 0x03 /* C/MS music voice 7-12 register port, write only */
+
+#define DSP_PORT_STATUS 0x04 /* DSP Status bits, read only */
+#define DSP_PORT_CONTROL 0x04 /* DSP Control bits, write only */
+#define DSP_PORT_DATA_LSB 0x05 /* Read or write LSB of 16 bit data */
+
+
+#define DSP_PORT_RESET 0x06 /* DSP Reset, write only */
+#define DSP_PORT_07h 0x07 /* reserved port */
+
+#define DSP_PORT_FMD0 0x08 /* FM music data/status port, read/write */
+#define DSP_PORT_FMR0 0x09 /* FM music data/status port, write only */
+
+#define DSP_PORT_RDDATA 0x0A /* DSP Read data, read only reading signals DSP */
+#define DSP_PORT_0Bh 0x0B /* reserved port */
+#define DSP_PORT_WRDATA 0x0C /* DSP Write data or command, write */
+#define DSP_PORT_WRBUSY 0x0C /* DSP Write buffer status (bit 7), read */
+#define DSP_PORT_0Dh 0x0D /* reserved port */
+#define DSP_PORT_DATAAVAIL 0x0E /* DSP Data available status (bit 7), read only */
+#define DSP_PORT_INTERFACE 0x0E /* Sets DMA Channel and Interrupt, write only */
+#define DSP_PORT_0Fh 0x0F /* reserved port (used on Pro cards) */
+
+#define ADDR_MASK 0x003f
+
+#define INT_MASK 0xffc7
+#define INT_3_BITS 0x0008
+#define INT_5_BITS 0x0010
+#define INT_7_BITS 0x0018
+#define INT_9_BITS 0x0020
+#define INT_10_BITS 0x0028
+#define INT_11_BITS 0x0030
+#define INT_12_BITS 0x0038
+
+#define GAME_BIT 0x0400
+#define GAME_BIT_MASK 0xfbff
+
+#define INT_TEST_BIT 0x0200
+#define INT_TEST_PASS 0x0100
+#define INT_TEST_BIT_MASK 0xFDFF
+
+#define DMA_MASK 0xfff8
+#define DMA_0_BITS 0x0001
+#define DMA_1_BITS 0x0002
+#define DMA_3_BITS 0x0003
+#define DMA_5_BITS 0x0004
+#define DMA_6_BITS 0x0005
+#define DMA_7_BITS 0x0006
+
+#define DMA_TEST_BIT 0x0080
+#define DMA_TEST_PASS 0x0040
+#define DMA_TEST_BIT_MASK 0xFF7F
+
+
+/* Echo DSP Flags */
+
+#define DSP_FLAG3 0x10
+#define DSP_FLAG2 0x08
+#define DSP_FLAG1 0x80
+#define DSP_FLAG0 0x40
+
+#define PSS_CONFIG 0x10
+#define PSS_WSS_CONFIG 0x12
+#define SB_CONFIG 0x14
+#define MIDI_CONFIG 0x18
+#define CD_CONFIG 0x16
+#define UART_CONFIG 0x1a
+
+#define PSS_DATA 0x00
+#define PSS_STATUS 0x02
+#define PSS_CONTROL 0x02
+#define PSS_ID_VERS 0x04
+
+#define PSS_FLAG3 0x0800
+#define PSS_FLAG2 0x0400
+#define PSS_FLAG1 0x1000
+#define PSS_FLAG0 0x0800
+
+/*_____ WSS defines */
+#define WSS_BASE_ADDRESS 0x530
+#define WSS_CONFIG 0x0
+#define WSS_VERSION 0x03
+#define WSS_SP0 0x04
+#define WSS_SP1 0x05
+#define WSS_SP2 0x06
+#define WSS_SP3 0x07
+
+/*_____ SoundPort register addresses */
+
+#define SP_LIN_SOURCE_CTRL 0x00
+#define SP_RIN_SOURCE_CTRL 0x01
+#define SP_LIN_GAIN_CTRL 0x10
+#define SP_RIN_GAIN_CTRL 0x11
+#define SP_LAUX1_CTRL 0x02
+#define SP_RAUX1_CTRL 0x03
+#define SP_LAUX2_CTRL 0x04
+#define SP_RAUX2_CTRL 0x05
+#define SP_LOUT_CTRL 0x06
+#define SP_ROUT_CTRL 0x07
+#define SP_CLK_FORMAT 0x48
+#define SP_INT_CONF 0x09
+#define SP_INT_CONF_MCE 0x49
+#define SP_PIN_CTRL 0x0a
+#define SP_TEST_INIT 0x0b
+#define SP_MISC_CTRL 0x0c
+#define SP_MIX_CTRL 0x0d
+#define SP_DMA_UCNT 0x0e
+#define SP_DMA_LCNT 0x0f
+
+/*_____ Gain constants */
+
+#define GAIN_0 0x00
+#define GAIN_1_5 0x01
+#define GAIN_3 0x02
+#define GAIN_4_5 0x03
+#define GAIN_6 0x04
+#define GAIN_7_5 0x05
+#define GAIN_9 0x06
+#define GAIN_10_5 0x07
+#define GAIN_12 0x08
+#define GAIN_13_5 0x09
+#define GAIN_15 0x0a
+#define GAIN_16_5 0x0b
+#define GAIN_18 0x0c
+#define GAIN_19_5 0x0d
+#define GAIN_21 0x0e
+#define GAIN_22_5 0x0f
+#define MUTE 0XFFFF
+
+/*_____ Attenuation constants */
+
+#define ATTEN_0 0x00
+#define ATTEN_1_5 0x01
+#define ATTEN_3 0x02
+#define ATTEN_4_5 0x03
+#define ATTEN_6 0x04
+#define ATTEN_7_5 0x05
+#define ATTEN_9 0x06
+#define ATTEN_10_5 0x07
+#define ATTEN_12 0x08
+#define ATTEN_13_5 0x09
+#define ATTEN_15 0x0a
+#define ATTEN_16_5 0x0b
+#define ATTEN_18 0x0c
+#define ATTEN_19_5 0x0d
+#define ATTEN_21 0x0e
+#define ATTEN_22_5 0x0f
+
+
+#define PSS_WRITE_EMPTY 0x8000
+
+#define CD_POL_MASK 0xFFBF
+#define CD_POL_BIT 0x0040
+
+
+
+/******************************************************************************
+
+ host.h
+
+ Version 1.2 9/27/93
+
+ Copyright (c) 1993 Analog Devices Inc. All rights reserved
+
+******************************************************************************/
+#define SB_WRITE_FULL 0x80
+#define SB_READ_FULL 0x80
+#define SB_WRITE_STATUS 0x0C
+#define SB_READ_STATUS 0x0E
+#define SB_READ_DATA 0x0A
+#define SB_WRITE_DATA 0x0C
+
+#define PSS_DATA_REG 0x00
+#define PSS_STATUS_REG 0x02
+#define PSS_WRITE_EMPTY 0x8000
+#define PSS_READ_FULL 0x4000
+
+/*_____ 1848 Sound Port bit defines */
+
+#define SP_IN_INIT 0x80
+#define MODE_CHANGE_ENABLE 0x40
+#define MODE_CHANGE_MASK 0xbf
+#define TRANSFER_DISABLE 0x20
+#define TRANSFER_DISABLE_MASK 0xdf
+#define ADDRESS_MASK 0xf0
+
+/*_____ Status bits */
+#define INTERRUPT_STATUS 0x01
+#define PLAYBACK_READY 0x02
+#define PLAYBACK_LEFT 0x04
+/*_____ pbright is not left */
+#define PLAYBACK_UPPER 0x08
+/*_____ bplower is not upper */
+
+#define SAMPLE_OVERRUN 0x10
+#define SAMPLE_UNDERRUN 0x10
+#define CAPTURE_READY 0x20
+#define CAPTURE_LEFT 0x40
+/*_____ cpright is not left */
+#define CAPTURE_UPPER 0x08
+/*_____ cplower is not upper */
+
+/*_____ Input & Output regs bits */
+#define LINE_INPUT 0x80
+#define AUX_INPUT 0x40
+#define MIC_INPUT 0x80
+#define MIXED_DAC_INPUT 0xC0
+#define INPUT_GAIN_MASK 0xf0
+#define INPUT_MIC_GAIN_ENABLE 0x20
+#define INPUT_MIC_GAIN_MASK 0xdf
+#define INPUT_SOURCE_MASK 0x3f
+#define AUX_INPUT_ATTEN_MASK 0xf0
+#define AUX_INPUT_MUTE 0x80
+#define AUX_INPUT_MUTE_MASK 0x7f
+#define OUTPUT_MUTE 0x80
+#define OUTPUT_MUTE_MASK 0x7f
+#define OUTPUT_ATTEN_MASK 0xc0
+
+/*_____ Clock and Data format reg bits */
+#define CLOCK_SELECT_MASK 0xfe
+#define CLOCK_XTAL2 0x01
+#define CLOCK_XTAL1 0x00
+#define CLOCK_FREQ_MASK 0xf1
+#define STEREO_MONO_MASK 0xef
+#define STEREO 0x10
+#define AUDIO_MONO 0x00
+#define LINEAR_COMP_MASK 0xdf
+#define LINEAR 0x00
+#define COMPANDED 0x20
+#define FORMAT_MASK 0xbf
+#define PCM 0x00
+#define ULAW 0x00
+#define TWOS_COMP 0x40
+#define ALAW 0x40
+
+/*_____ Interface Configuration reg bits */
+#define PLAYBACK_ENABLE 0x01
+#define PLAYBACK_ENABLE_MASK 0xfe
+#define CAPTURE_ENABLE 0x02
+#define CAPTURE_ENABLE_MASK 0xfd
+#define SINGLE_DMA 0x04
+#define SINGLE_DMA_MASK 0xfb
+#define DUAL_DMA 0x00
+#define AUTO_CAL_ENABLE 0x08
+#define AUTO_CAL_DISABLE_MASK 0xf7
+#define PLAYBACK_PIO_ENABLE 0x40
+#define PLAYBACK_DMA_MASK 0xbf
+#define CAPTURE_PIO_ENABLE 0x80
+#define CAPTURE_DMA_MASK 0x7f
+
+/*_____ Pin control bits */
+#define INTERRUPT_ENABLE 0x02
+#define INTERRUPT_MASK 0xfd
+
+/*_____ Test and init reg bits */
+#define OVERRANGE_LEFT_MASK 0xfc
+#define OVERRANGE_RIGHT_MASK 0xf3
+#define DATA_REQUEST_STATUS 0x10
+#define AUTO_CAL_IN_PROG 0x20
+#define PLAYBACK_UNDERRUN 0x40
+#define CAPTURE_UNDERRUN 0x80
+
+/*_____ Miscellaneous Control reg bits */
+#define ID_MASK 0xf0
+
+/*_____ Digital Mix Control reg bits */
+#define DIGITAL_MIX1_MUTE_MASK 0xfe
+#define MIX_ATTEN_MASK 0x03
+
+/*_____ 1848 Sound Port reg defines */
+
+#define SP_LEFT_INPUT_CONTROL 0x0
+#define SP_RIGHT_INPUT_CONTROL 0x1
+#define SP_LEFT_AUX1_CONTROL 0x2
+#define SP_RIGHT_AUX1_CONTROL 0x3
+#define SP_LEFT_AUX2_CONTROL 0x4
+#define SP_RIGHT_AUX2_CONTROL 0x5
+#define SP_LEFT_OUTPUT_CONTROL 0x6
+#define SP_RIGHT_OUTPUT_CONTROL 0x7
+#define SP_CLOCK_DATA_FORMAT 0x8
+#define SP_INTERFACE_CONFIG 0x9
+#define SP_PIN_CONTROL 0xA
+#define SP_TEST_AND_INIT 0xB
+#define SP_MISC_INFO 0xC
+#define SP_DIGITAL_MIX 0xD
+#define SP_UPPER_BASE_COUNT 0xE
+#define SP_LOWER_BASE_COUNT 0xF
+
+#define HOST_SP_ADDR (0x534)
+#define HOST_SP_DATA (0x535)
+
+
+/******************************************************************************
+
+ phillips.h
+
+ Version 1.2 9/27/93
+
+ Copyright (c) 1993 Analog Devices Inc. All rights reserved
+
+******************************************************************************/
+/*_____ Phillips control SW defines */
+
+/*_____ Settings and ranges */
+#define VOLUME_MAX 6
+#define VOLUME_MIN (-64)
+#define VOLUME_RANGE 70
+#define VOLUME_STEP 2
+#define BASS_MAX 15
+#define BASS_MIN (-12)
+#define BASS_STEP 2
+#define BASS_RANGE 27
+#define TREBLE_MAX 12
+#define TREBLE_MIN (-12)
+#define TREBLE_STEP 2
+#define TREBLE_RANGE 24
+
+#define VOLUME_CONSTANT 252
+#define BASS_CONSTANT 246
+#define TREBLE_CONSTANT 246
+
+/*_____ Software commands */
+#define SET_MASTER_COMMAND 0x0010
+#define MASTER_VOLUME_LEFT 0x0000
+#define MASTER_VOLUME_RIGHT 0x0100
+#define MASTER_BASS 0x0200
+#define MASTER_TREBLE 0x0300
+#define MASTER_SWITCH 0x0800
+
+#define STEREO_MODE 0x00ce
+#define PSEUDO_MODE 0x00d6
+#define SPATIAL_MODE 0x00de
+#define MONO_MODE 0x00c6
+
+
+#define PSS_STEREO 0x00ce
+#define PSS_PSEUDO 0x00d6
+#define PSS_SPATIAL 0x00de
+#define PSS_MONO 0x00c6
+
+#define PHILLIPS_VOL_MIN -64
+#define PHILLIPS_VOL_MAX 6
+#define PHILLIPS_VOL_DELTA 70
+#define PHILLIPS_VOL_INITIAL -20
+#define PHILLIPS_VOL_CONSTANT 252
+#define PHILLIPS_VOL_STEP 2
+#define PHILLIPS_BASS_MIN -12
+#define PHILLIPS_BASS_MAX 15
+#define PHILLIPS_BASS_DELTA 27
+#define PHILLIPS_BASS_INITIAL 0
+#define PHILLIPS_BASS_CONSTANT 246
+#define PHILLIPS_BASS_STEP 2
+#define PHILLIPS_TREBLE_MIN -12
+#define PHILLIPS_TREBLE_MAX 12
+#define PHILLIPS_TREBLE_DELTA 24
+#define PHILLIPS_TREBLE_INITIAL 0
+#define PHILLIPS_TREBLE_CONSTANT 246
+#define PHILLIPS_TREBLE_STEP 2
+
diff --git a/sys/i386/isa/sound/sb.h b/sys/i386/isa/sound/sb.h
new file mode 100644
index 000000000000..bb8ae12d7e60
--- /dev/null
+++ b/sys/i386/isa/sound/sb.h
@@ -0,0 +1,28 @@
+#define DSP_RESET (sbc_base + 0x6)
+#define DSP_READ (sbc_base + 0xA)
+#define DSP_WRITE (sbc_base + 0xC)
+#define DSP_COMMAND (sbc_base + 0xC)
+#define DSP_STATUS (sbc_base + 0xC)
+#define DSP_DATA_AVAIL (sbc_base + 0xE)
+#define DSP_DATA_AVL16 (sbc_base + 0xF)
+#define MIXER_ADDR (sbc_base + 0x4)
+#define MIXER_DATA (sbc_base + 0x5)
+#define OPL3_LEFT (sbc_base + 0x0)
+#define OPL3_RIGHT (sbc_base + 0x2)
+#define OPL3_BOTH (sbc_base + 0x8)
+/* DSP Commands */
+
+#define DSP_CMD_SPKON 0xD1
+#define DSP_CMD_SPKOFF 0xD3
+#define DSP_CMD_DMAON 0xD0
+#define DSP_CMD_DMAOFF 0xD4
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT 1
+#define IMODE_INPUT 2
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+#define NORMAL_MIDI 0
+#define UART_MIDI 1
+
diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c
new file mode 100644
index 000000000000..e3078bca7bf7
--- /dev/null
+++ b/sys/i386/isa/sound/sb16_dsp.c
@@ -0,0 +1,568 @@
+/*
+ * sound/sb16_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip.
+ *
+ * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de)
+ *
+ * based on SB-driver by (C) Hannu Savolainen
+ *
+ * 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.
+ *
+ */
+
+#define DEB(x)
+#define DEB1(x)
+/*
+ * #define DEB_DMARES
+ */
+#include "sound_config.h"
+#include "sb.h"
+#include "sb_mixer.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO)
+
+extern int sbc_base;
+
+static int sb16_dsp_ok = 0;/*
+
+
+ * * * * Set to 1 after successful *
+ * * initialization */
+static int dsp_16bit = 0;
+static int dsp_stereo = 0;
+static int dsp_current_speed = 8000; /*
+
+
+ * * * * DSP_DEFAULT_SPEED; */
+static int dsp_busy = 0;
+static int dma16, dma8;
+static unsigned long dsp_count = 0;
+
+static int irq_mode = IMODE_NONE; /*
+
+
+ * * * * IMODE_INPUT, IMODE_OUTPUT
+ * or * * IMODE_NONE */
+static int my_dev = 0;
+
+static volatile int intr_active = 0;
+
+static int sb16_dsp_open (int dev, int mode);
+static void sb16_dsp_close (int dev);
+static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
+static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount);
+static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount);
+static void sb16_dsp_reset (int dev);
+static void sb16_dsp_halt (int dev);
+static int dsp_set_speed (int);
+static int dsp_set_stereo (int);
+static void dsp_cleanup (void);
+int sb_reset_dsp (void);
+
+static struct audio_operations sb16_dsp_operations =
+{
+ "SoundBlaster 16",
+ DMA_AUTOMODE,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ sb16_dsp_open,
+ sb16_dsp_close,
+ sb16_dsp_output_block,
+ sb16_dsp_start_input,
+ sb16_dsp_ioctl,
+ sb16_dsp_prepare_for_input,
+ sb16_dsp_prepare_for_output,
+ sb16_dsp_reset,
+ sb16_dsp_halt,
+ NULL,
+ NULL
+};
+
+static int
+sb_dsp_command01 (unsigned char val)
+{
+ int i = 1 << 16;
+
+ while (--i & (!INB (DSP_STATUS) & 0x80));
+ if (!i)
+ printk ("SB16 sb_dsp_command01 Timeout\n");
+ return sb_dsp_command (val);
+}
+
+static int
+dsp_set_speed (int mode)
+{
+ DEB (printk ("dsp_set_speed(%d)\n", mode));
+ if (mode)
+ {
+ if (mode < 5000)
+ mode = 5000;
+ if (mode > 44100)
+ mode = 44100;
+ dsp_current_speed = mode;
+ }
+ return mode;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ DEB (printk ("dsp_set_stereo(%d)\n", mode));
+
+ dsp_stereo = mode;
+
+ return mode;
+}
+
+static int
+dsp_set_bits (int arg)
+{
+ DEB (printk ("dsp_set_bits(%d)\n", arg));
+
+ if (arg)
+ switch (arg)
+ {
+ case 8:
+ dsp_16bit = 0;
+ break;
+ case 16:
+ dsp_16bit = 1;
+ break;
+ default:
+ dsp_16bit = 0;
+ }
+ return dsp_16bit ? 16 : 8;
+}
+
+static int
+sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return dsp_set_speed (arg);
+ return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo (arg);
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo + 1;
+ return IOCTL_OUT (arg, dsp_stereo + 1);
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return dsp_set_bits (arg);
+ return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return dsp_16bit ? 16 : 8;
+ return IOCTL_OUT (arg, dsp_16bit ? 16 : 8);
+
+ case SOUND_PCM_WRITE_FILTER: /*
+ * NOT YET IMPLEMENTED
+ */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static int
+sb16_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ DEB (printk ("sb16_dsp_open()\n"));
+ if (!sb16_dsp_ok)
+ {
+ printk ("SB16 Error: SoundBlaster board not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (intr_active)
+ return RET_ERROR (EBUSY);
+
+ retval = sb_get_irq ();
+ if (retval < 0)
+ return retval;
+
+ sb_reset_dsp ();
+
+ if (ALLOC_DMA_CHN (dma8))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma8);
+ sb_free_irq ();
+ return RET_ERROR (EBUSY);
+ }
+
+ if (dma16 != dma8)
+ if (ALLOC_DMA_CHN (dma16))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma16);
+ sb_free_irq ();
+ RELEASE_DMA_CHN (dma8);
+ return RET_ERROR (EBUSY);
+ }
+
+ irq_mode = IMODE_NONE;
+ dsp_busy = 1;
+
+ return 0;
+}
+
+static void
+sb16_dsp_close (int dev)
+{
+ unsigned long flags;
+
+ DEB (printk ("sb16_dsp_close()\n"));
+ sb_dsp_command01 (0xd9);
+ sb_dsp_command01 (0xd5);
+
+ DISABLE_INTR (flags);
+ RELEASE_DMA_CHN (dma8);
+
+ if (dma16 != dma8)
+ RELEASE_DMA_CHN (dma16);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_busy = 0;
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk ("output_block: %x %d %d\n", buf, count, intrflag);
+ if (intrflag)
+ {
+ int pos, chan = audio_devs[dev]->dmachan;
+
+ DISABLE_INTR (flags);
+ clear_dma_ff (chan);
+ disable_dma (chan);
+ pos = get_dma_residue (chan);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+ printk ("dmapos=%d %x\n", pos, pos);
+ }
+#endif
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == dsp_count)
+ {
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+ return; /*
+ * Auto mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb16_dsp_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+ sb_dsp_command (0x41);
+ sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_16bit ? 0xb6 : 0xc6));
+ sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10 : 0)));
+ sb_dsp_command01 ((unsigned char) (cnt & 0xff));
+ sb_dsp_command ((unsigned char) (cnt >> 8));
+
+ dsp_count = cnt;
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk ("start_input: %x %d %d\n", buf, count, intrflag);
+ if (intrflag)
+ {
+ int pos, chan = audio_devs[dev]->dmachan;
+
+ DISABLE_INTR (flags);
+ clear_dma_ff (chan);
+ disable_dma (chan);
+ pos = get_dma_residue (chan);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+ printk ("dmapos=%d %x\n", pos, pos);
+ }
+#endif
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == dsp_count)
+ {
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+ return; /*
+ * Auto mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb_reset_dsp ();
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ sb_dsp_command (0x42);
+ sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_16bit ? 0xbe : 0xce));
+ sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10 : 0)));
+ sb_dsp_command01 ((unsigned char) (cnt & 0xff));
+ sb_dsp_command ((unsigned char) (cnt >> 8));
+
+ dsp_count = cnt;
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static int
+sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static int
+sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static void
+dsp_cleanup (void)
+{
+ irq_mode = IMODE_NONE;
+ intr_active = 0;
+}
+
+static void
+sb16_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_halt (int dev)
+{
+ if (dsp_16bit)
+ {
+ sb_dsp_command01 (0xd9);
+ sb_dsp_command01 (0xd5);
+ }
+ else
+ {
+ sb_dsp_command01 (0xda);
+ sb_dsp_command01 (0xd0);
+ }
+}
+
+static void
+set_irq_hw (int level)
+{
+ int ival;
+
+ switch (level)
+ {
+ case 5:
+ ival = 2;
+ break;
+ case 7:
+ ival = 4;
+ break;
+ case 9:
+ ival = 1;
+ break;
+ case 10:
+ ival = 8;
+ break;
+ default:
+ printk ("SB16_IRQ_LEVEL %d does not exist\n", level);
+ return;
+ }
+ sb_setmixer (IRQ_NR, ival);
+}
+
+long
+sb16_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ extern int sbc_major, sbc_minor;
+
+ if (sbc_major < 4)
+ return mem_start; /* Not a SB16 */
+
+#ifndef SCO
+ sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor);
+#endif
+
+ printk (" <%s>", sb16_dsp_operations.name);
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &sb16_dsp_operations;
+ audio_devs[my_dev]->dmachan = hw_config->dma;
+ audio_devs[my_dev]->buffcount = 1;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+ sb16_dsp_ok = 1;
+ return mem_start;
+}
+
+int
+sb16_dsp_detect (struct address_info *hw_config)
+{
+ struct address_info *sb_config;
+ extern int sbc_major;
+
+ if (sb16_dsp_ok)
+ return 1; /* Can't drive two cards */
+
+ if (!(sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ printk ("SB16 Error: Plain SB not configured\n");
+ return 0;
+ }
+
+ /*
+ * sb_setmixer(OPSW,0xf); if(sb_getmixer(OPSW)!=0xf) return 0;
+ */
+
+ if (!sb_reset_dsp ())
+ return 0;
+
+ if (sbc_major < 4) /* Set by the plain SB driver */
+ return 0; /* Not a SB16 */
+
+ if (hw_config->dma < 4)
+ if (hw_config->dma != sb_config->dma)
+ {
+ printk ("SB16 Error: Invalid DMA channel %d/%d\n",
+ sb_config->dma, hw_config->dma);
+ return 0;
+ }
+
+ dma16 = hw_config->dma;
+ dma8 = sb_config->dma;
+ set_irq_hw (sb_config->irq);
+ sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma));
+
+ DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma));
+
+ /*
+ * dsp_showmessage(0xe3,99);
+ */
+ sb16_dsp_ok = 1;
+ return 1;
+}
+
+void
+sb16_dsp_interrupt (int unused)
+{
+ int data;
+
+ data = INB (DSP_DATA_AVL16); /*
+ * Interrupt acknowledge
+ */
+
+ if (intr_active)
+ switch (irq_mode)
+ {
+ case IMODE_OUTPUT:
+ intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c
new file mode 100644
index 000000000000..7a3fdb1a3be6
--- /dev/null
+++ b/sys/i386/isa/sound/sb16_midi.c
@@ -0,0 +1,304 @@
+/*
+ * sound/sb16_midi.c
+ *
+ * The low level driver for the MPU-401 UART emulation of the SB16.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI)
+
+#include "sb.h"
+
+#define DATAPORT (sb16midi_base)
+#define COMDPORT (sb16midi_base+1)
+#define STATPORT (sb16midi_base+1)
+
+#define sb16midi_status() INB(STATPORT)
+#define input_avail() (!(sb16midi_status()&INPUT_AVAIL))
+#define output_ready() (!(sb16midi_status()&OUTPUT_READY))
+#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT)
+#define sb16midi_read() INB(DATAPORT)
+#define sb16midi_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x40
+#define INPUT_AVAIL 0x80
+#define MPU_ACK 0xFE
+#define MPU_RESET 0xFF
+#define UART_MODE_ON 0x3F
+
+static int sb16midi_opened = 0;
+static int sb16midi_base = 0x330;
+static int sb16midi_detected = 0;
+static int my_dev;
+extern int sbc_base;
+
+static int reset_sb16midi (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+sb16midi_input_loop (void)
+{
+ while (input_avail ())
+ {
+ unsigned char c = sb16midi_read ();
+
+ if (sb16midi_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+}
+
+void
+sb16midiintr (int unit)
+{
+ if (input_avail ())
+ sb16midi_input_loop ();
+}
+
+static int
+sb16midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (sb16midi_opened)
+ {
+ return RET_ERROR (EBUSY);
+ }
+
+ sb16midi_input_loop ();
+
+ midi_input_intr = input;
+ sb16midi_opened = mode;
+
+ return 0;
+}
+
+static void
+sb16midi_close (int dev)
+{
+ sb16midi_opened = 0;
+}
+
+static int
+sb16midi_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ sb16midi_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+
+ if (!output_ready ())
+ {
+ printk ("MPU-401: Timeout\n");
+ return 0;
+ }
+
+ sb16midi_write (midi_byte);
+ return 1;
+}
+
+static int
+sb16midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb16midi_kick (int dev)
+{
+}
+
+static int
+sb16midi_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+#define MIDI_SYNTH_NAME "SoundBlaster 16 Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations sb16midi_operations =
+{
+ {"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI},
+ &std_midi_synth,
+ sb16midi_open,
+ sb16midi_close,
+ sb16midi_ioctl,
+ sb16midi_out,
+ sb16midi_start_read,
+ sb16midi_end_read,
+ sb16midi_kick,
+ NULL,
+ sb16midi_buffer_status,
+ NULL
+};
+
+
+long
+attach_sb16midi (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ sb16midi_base = hw_config->io_base;
+
+ if (!sb16midi_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ sb16midi_cmd (UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ printk (" <SoundBlaster MPU-401>");
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &sb16midi_operations;
+ return mem_start;
+}
+
+static int
+reset_sb16midi (void)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
+
+ ok = 0;
+
+ DISABLE_INTR (flags);
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ sb16midi_cmd (MPU_RESET); /*
+ * Send MPU-401 RESET Command
+ */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ }
+
+ sb16midi_opened = 0;
+ if (ok)
+ sb16midi_input_loop (); /*
+ * Flush input before enabling interrupts
+ */
+
+ RESTORE_INTR (flags);
+
+ return ok;
+}
+
+
+int
+probe_sb16midi (struct address_info *hw_config)
+{
+ int ok = 0;
+ int i;
+ extern int sbc_major;
+
+ if (sbc_major < 4)
+ return 0; /* Not a SB16 */
+
+ sb16midi_base = hw_config->io_base;
+
+ if (sb_get_irq () < 0)
+ return 0;
+
+ ok = reset_sb16midi ();
+
+ sb16midi_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c
new file mode 100644
index 000000000000..f7588e1ca172
--- /dev/null
+++ b/sys/i386/isa/sound/sb_card.c
@@ -0,0 +1,52 @@
+/*
+ * sound/sb_card.c
+ *
+ * Detection routine for the SoundBlaster cards.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+long
+attach_sb_card (long mem_start, struct address_info *hw_config)
+{
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI)
+ if (!sb_dsp_detect (hw_config))
+ return mem_start;
+ mem_start = sb_dsp_init (mem_start, hw_config);
+#endif
+
+ return mem_start;
+}
+
+int
+probe_sb (struct address_info *hw_config)
+{
+ return sb_dsp_detect (hw_config);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c
new file mode 100644
index 000000000000..37be35c4cfca
--- /dev/null
+++ b/sys/i386/isa/sound/sb_dsp.c
@@ -0,0 +1,863 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro).
+ *
+ * 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.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support Sound Galaxy NX Pro
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+#include "sb.h"
+#include "sb_mixer.h"
+#undef SB_TEST_IRQ
+
+int sbc_base = 0;
+static int sbc_irq = 0;
+static int open_mode = 0; /* Read, write or both */
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+int sb_dsp_ok = 0; /*
+
+
+ * * * * Set to 1 after successful
+ * initialization * */
+static int midi_disabled = 0;
+int sb_dsp_highspeed = 0;
+int sbc_major = 1, sbc_minor = 0; /*
+
+
+ * * * * DSP version */
+static int dsp_stereo = 0;
+static int dsp_current_speed = DSP_DEFAULT_SPEED;
+static int sb16 = 0;
+static int irq_verified = 0;
+
+int sb_midi_mode = NORMAL_MIDI;
+int sb_midi_busy = 0; /*
+
+
+ * * * * 1 if the process has output
+ * to * * MIDI */
+int sb_dsp_busy = 0;
+
+volatile int sb_irq_mode = IMODE_NONE; /*
+
+
+ * * * * IMODE_INPUT, *
+ * IMODE_OUTPUT * * or *
+ * IMODE_NONE */
+static volatile int irq_ok = 0;
+
+int sb_duplex_midi = 0;
+static int my_dev = 0;
+
+volatile int sb_intr_active = 0;
+
+static int dsp_speed (int);
+static int dsp_set_stereo (int mode);
+int sb_dsp_command (unsigned char val);
+
+#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
+
+/*
+ * Common code for the midi and pcm functions
+ */
+
+int
+sb_dsp_command (unsigned char val)
+{
+ int i;
+ unsigned long limit;
+
+ limit = GET_TIME () + HZ / 10;/*
+ * The timeout is 0.1 secods
+ */
+
+ /*
+ * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 500000 && GET_TIME () < limit; i++)
+ {
+ if ((INB (DSP_STATUS) & 0x80) == 0)
+ {
+ OUTB (val, DSP_COMMAND);
+ return 1;
+ }
+ }
+
+ printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
+ printk ("IRQ conflict???\n");
+ return 0;
+}
+
+void
+sbintr (int unit)
+{
+ int status;
+
+#ifndef EXCLUDE_SBPRO
+ if (sb16)
+ {
+ unsigned char src = sb_getmixer (IRQ_STAT); /*
+
+
+ * * * * Interrupt
+ * source * *
+ * register */
+
+#ifndef EXCLUDE_SB16
+ if (src & 3)
+ sb16_dsp_interrupt (unit);
+
+#ifndef EXCLUDE_MIDI
+ if (src & 4)
+ sb16midiintr (unit); /*
+ * SB MPU401 interrupt
+ */
+#endif
+
+#endif
+
+ if (!(src & 1))
+ return; /*
+ * Not a DSP interupt
+ */
+ }
+#endif
+
+ status = INB (DSP_DATA_AVAIL);/*
+ * Clear interrupt
+ */
+
+ if (sb_intr_active)
+ switch (sb_irq_mode)
+ {
+ case IMODE_OUTPUT:
+ sb_intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ sb_intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ /*
+ * A complete buffer has been input. Let's start new one
+ */
+ break;
+
+ case IMODE_INIT:
+ sb_intr_active = 0;
+ irq_ok = 1;
+ break;
+
+ case IMODE_MIDI:
+#ifndef EXCLUDE_MIDI
+ sb_midi_interrupt (unit);
+#endif
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+static int sb_irq_usecount = 0;
+
+int
+sb_get_irq (void)
+{
+ int ok;
+
+ if (!sb_irq_usecount)
+ if ((ok = snd_set_irq_handler (sbc_irq, sbintr)) < 0)
+ return ok;
+
+ sb_irq_usecount++;
+
+ return 0;
+}
+
+void
+sb_free_irq (void)
+{
+ if (!sb_irq_usecount)
+ return;
+
+ sb_irq_usecount--;
+
+ if (!sb_irq_usecount)
+ snd_release_irq (sbc_irq);
+}
+
+int
+sb_reset_dsp (void)
+{
+ int loopc;
+
+ OUTB (1, DSP_RESET);
+ tenmicrosec ();
+ OUTB (0, DSP_RESET);
+ tenmicrosec ();
+ tenmicrosec ();
+ tenmicrosec ();
+
+ for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /*
+ * Wait
+ * for
+ * data
+ * *
+ * available
+ * status
+ */
+
+ if (INB (DSP_READ) != 0xAA)
+ return 0; /*
+ * Sorry
+ */
+
+ return 1;
+}
+
+#endif
+
+#ifndef EXCLUDE_AUDIO
+
+static void
+dsp_speaker (char state)
+{
+ if (state)
+ sb_dsp_command (DSP_CMD_SPKON);
+ else
+ sb_dsp_command (DSP_CMD_SPKOFF);
+}
+
+static int
+dsp_speed (int speed)
+{
+ unsigned char tconst;
+ unsigned long flags;
+ int max_speed = 44100;
+
+ if (speed < 4000)
+ speed = 4000;
+
+ /*
+ * Older SB models don't support higher speeds than 22050.
+ */
+
+ if (sbc_major < 2 ||
+ (sbc_major == 2 && sbc_minor == 0))
+ max_speed = 22050;
+
+ /*
+ * SB models earlier than SB Pro have low limit for the input speed.
+ */
+ if (open_mode != OPEN_WRITE) /* Recording is possible */
+ if (sbc_major < 3) /* Limited input speed with these cards */
+ if (sbc_major == 2 && sbc_minor > 0)
+ max_speed = 15000;
+ else
+ max_speed = 13000;
+
+ if (speed > max_speed)
+ speed = max_speed; /*
+ * Invalid speed
+ */
+
+ if (dsp_stereo && speed > 22050)
+ speed = 22050;
+ /*
+ * Max. stereo speed is 22050
+ */
+
+ if ((speed > 22050) && sb_midi_busy)
+ {
+ printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n");
+ speed = 22050;
+ }
+
+ if (dsp_stereo)
+ speed *= 2;
+
+ /*
+ * Now the speed should be valid
+ */
+
+ if (speed > 22050)
+ { /*
+ * High speed mode
+ */
+ int tmp;
+
+ tconst = (unsigned char) ((65536 -
+ ((256000000 + speed / 2) / speed)) >> 8);
+ sb_dsp_highspeed = 1;
+
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x40))
+ sb_dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
+ }
+ else
+ {
+ int tmp;
+
+ sb_dsp_highspeed = 0;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x40))/*
+ * Set time constant
+ */
+ sb_dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
+ }
+
+ if (dsp_stereo)
+ speed /= 2;
+
+ dsp_current_speed = speed;
+ return speed;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ dsp_stereo = 0;
+
+#ifdef EXCLUDE_SBPRO
+ return 0;
+#else
+ if (sbc_major < 3 || sb16)
+ return 0; /*
+ * Sorry no stereo
+ */
+
+ if (mode && sb_midi_busy)
+ {
+ printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
+ return 0;
+ }
+
+ dsp_stereo = !!mode;
+ return dsp_stereo;
+#endif
+}
+
+static void
+sb_dsp_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+
+ if (!sb_irq_mode)
+ dsp_speaker (ON);
+
+ sb_irq_mode = IMODE_OUTPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+ count--;
+
+ if (sb_dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x48))/*
+ * High speed size
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x91);/*
+ * High speed 8 bit DAC
+ */
+ }
+ else
+ printk ("SB Error: Unable to start (high speed) DAC\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x14))/*
+ * 8-bit DAC (DMA)
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ }
+ else
+ printk ("SB Error: Unable to start DAC\n");
+ RESTORE_INTR (flags);
+ }
+ sb_intr_active = 1;
+}
+
+static void
+sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag,
+ int restart_dma)
+{
+ /*
+ * Start a DMA input to the buffer pointed by dmaqtail
+ */
+
+ unsigned long flags;
+
+ if (!sb_irq_mode)
+ dsp_speaker (OFF);
+
+ sb_irq_mode = IMODE_INPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+ count--;
+
+ if (sb_dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x48))/*
+ * High speed size
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x99);/*
+ * High speed 8 bit ADC
+ */
+ }
+ else
+ printk ("SB Error: Unable to start (high speed) ADC\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x24))/*
+ * 8-bit ADC (DMA)
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ }
+ else
+ printk ("SB Error: Unable to start ADC\n");
+ RESTORE_INTR (flags);
+ }
+
+ sb_intr_active = 1;
+}
+
+static void
+dsp_cleanup (void)
+{
+ sb_intr_active = 0;
+}
+
+static int
+sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+
+ if (sbc_major == 3) /*
+ * SB Pro
+ */
+ {
+ if (dsp_stereo)
+ sb_dsp_command (0xa8);
+ else
+ sb_dsp_command (0xa0);
+
+ dsp_speed (dsp_current_speed); /*
+ * Speed must be recalculated if
+ * #channels * changes
+ */
+ }
+ return 0;
+}
+
+static int
+sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (ON);
+
+#ifndef EXCLUDE_SBPRO
+ if (sbc_major == 3) /*
+ * SB Pro
+ */
+ {
+ sb_mixer_set_stereo (dsp_stereo);
+ dsp_speed (dsp_current_speed); /*
+ * Speed must be recalculated if
+ * #channels * changes
+ */
+ }
+#endif
+ return 0;
+}
+
+static void
+sb_dsp_halt_xfer (int dev)
+{
+}
+
+static int
+verify_irq (void)
+{
+#if 0
+ DEFINE_WAIT_QUEUE (testq, testf);
+
+ irq_ok = 0;
+
+ if (sb_get_irq () == -1)
+ {
+ printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
+ return 0;
+ }
+
+
+ sb_irq_mode = IMODE_INIT;
+
+ sb_dsp_command (0xf2); /*
+ * This should cause immediate interrupt
+ */
+
+ DO_SLEEP (testq, testf, HZ / 5);
+
+ sb_free_irq ();
+
+ if (!irq_ok)
+ {
+ printk ("SB Warning: IRQ%d test not passed!", sbc_irq);
+ irq_ok = 1;
+ }
+#else
+ irq_ok = 1;
+#endif
+ return irq_ok;
+}
+
+static int
+sb_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: SoundBlaster board not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI))
+ {
+ printk ("SB: PCM not possible during MIDI input\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (!irq_verified)
+ {
+ verify_irq ();
+ irq_verified = 1;
+ }
+ else if (!irq_ok)
+ printk ("SB Warning: Incorrect IRQ setting %d\n",
+ sbc_irq);
+
+ retval = sb_get_irq ();
+ if (retval)
+ return retval;
+
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ sb_free_irq ();
+ printk ("SB: DMA Busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ sb_irq_mode = IMODE_NONE;
+
+ sb_dsp_busy = 1;
+ open_mode = mode;
+
+ return 0;
+}
+
+static void
+sb_dsp_close (int dev)
+{
+ DMAbuf_close_dma (dev);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+ sb_dsp_busy = 0;
+ sb_dsp_highspeed = 0;
+ open_mode = 0;
+}
+
+static int
+sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return dsp_speed (arg);
+ return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo + 1;
+ return IOCTL_OUT (arg, dsp_stereo + 1);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo (arg);
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_WRITE_BITS:
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return 8;
+ return IOCTL_OUT (arg, 8);/*
+ * Only 8 bits/sample supported
+ */
+ break;
+
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ return RET_ERROR (EINVAL);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_speed (dsp_current_speed);
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+int
+sb_dsp_detect (struct address_info *hw_config)
+{
+ sbc_base = hw_config->io_base;
+ sbc_irq = hw_config->irq;
+
+ if (sb_dsp_ok)
+ return 0; /*
+ * Already initialized
+ */
+
+ if (!sb_reset_dsp ())
+ return 0;
+
+ return 1; /*
+ * Detected
+ */
+}
+
+#ifndef EXCLUDE_AUDIO
+static struct audio_operations sb_dsp_operations =
+{
+ "SoundBlaster",
+ NOTHING_SPECIAL,
+ AFMT_U8, /* Just 8 bits. Poor old SB */
+ NULL,
+ sb_dsp_open,
+ sb_dsp_close,
+ sb_dsp_output_block,
+ sb_dsp_start_input,
+ sb_dsp_ioctl,
+ sb_dsp_prepare_for_input,
+ sb_dsp_prepare_for_output,
+ sb_dsp_reset,
+ sb_dsp_halt_xfer,
+ NULL, /* local_qlen */
+ NULL /* copy_from_user */
+};
+
+#endif
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ int i;
+ int mixer_type = 0;
+
+ sbc_major = sbc_minor = 0;
+ sb_dsp_command (0xe1); /*
+ * Get version
+ */
+
+ for (i = 1000; i; i--)
+ {
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ { /*
+ * wait for Data Ready
+ */
+ if (sbc_major == 0)
+ sbc_major = INB (DSP_READ);
+ else
+ {
+ sbc_minor = INB (DSP_READ);
+ break;
+ }
+ }
+ }
+
+ if (sbc_major == 2 || sbc_major == 3)
+ sb_duplex_midi = 1;
+
+ if (sbc_major == 4)
+ sb16 = 1;
+
+#ifndef EXCLUDE_SBPRO
+ if (sbc_major >= 3)
+ mixer_type = sb_mixer_init (sbc_major);
+#endif
+
+#ifndef EXCLUDE_YM8312
+
+ if (sbc_major > 3 ||
+ (sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */
+ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+#endif
+
+ if (sbc_major >= 3)
+ {
+#ifndef SCO
+# ifdef __SGNXPRO__
+ if (mixer_type == 2)
+ {
+ sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+ }
+ else
+# endif
+ {
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+ }
+#endif
+ }
+ else
+ {
+#ifndef SCO
+ sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
+#endif
+ }
+
+ printk (" <%s>", sb_dsp_operations.name);
+
+#ifndef EXCLUDE_AUDIO
+#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+ if (!sb16) /*
+ * There is a better driver for SB16
+ */
+#endif
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations;
+ audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;
+ audio_devs[my_dev]->dmachan = hw_config->dma;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+#endif
+
+#ifndef EXCLUDE_MIDI
+ if (!midi_disabled && !sb16) /*
+ * Midi don't work in the SB emulation mode *
+ * of PAS, SB16 has better midi interface
+ */
+ sb_midi_init (sbc_major);
+#endif
+
+ sb_dsp_ok = 1;
+ return mem_start;
+}
+
+void
+sb_dsp_disable_midi (void)
+{
+ midi_disabled = 1;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_midi.c b/sys/i386/isa/sound/sb_midi.c
new file mode 100644
index 000000000000..a78225ed2732
--- /dev/null
+++ b/sys/i386/isa/sound/sb_midi.c
@@ -0,0 +1,252 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DS chips.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_MIDI)
+
+#include "sb.h"
+#undef SB_TEST_IRQ
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+extern int sb_dsp_ok; /* Set to 1 atfer successful initialization */
+extern int sbc_base;
+
+extern int sb_midi_mode;
+extern int sb_midi_busy; /*
+
+
+ * * * * 1 if the process has output to MIDI
+ *
+ */
+extern int sb_dsp_busy;
+extern int sb_dsp_highspeed;
+
+extern volatile int sb_irq_mode;
+extern int sb_duplex_midi;
+extern int sb_intr_active;
+int input_opened = 0;
+static int my_dev;
+
+void (*midi_input_intr) (int dev, unsigned char data);
+
+static int
+sb_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int ret;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: MIDI hardware not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (sb_midi_busy)
+ return RET_ERROR (EBUSY);
+
+ if (mode != OPEN_WRITE && !sb_duplex_midi)
+ {
+ if (num_midis == 1)
+ printk ("SoundBlaster: Midi input not currently supported\n");
+ return RET_ERROR (EPERM);
+ }
+
+ sb_midi_mode = NORMAL_MIDI;
+ if (mode != OPEN_WRITE)
+ {
+ if (sb_dsp_busy || sb_intr_active)
+ return RET_ERROR (EBUSY);
+ sb_midi_mode = UART_MIDI;
+ }
+
+ if (sb_dsp_highspeed)
+ {
+ printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_irq_mode = IMODE_MIDI;
+
+ sb_reset_dsp ();
+
+ if (!sb_dsp_command (0x35))
+ return RET_ERROR (EIO); /*
+ * Enter the UART mode
+ */
+ sb_intr_active = 1;
+
+ if ((ret = sb_get_irq ()) < 0)
+ {
+ sb_reset_dsp ();
+ return 0; /*
+ * IRQ not free
+ */
+ }
+ input_opened = 1;
+ midi_input_intr = input;
+ }
+
+ sb_midi_busy = 1;
+
+ return 0;
+}
+
+static void
+sb_midi_close (int dev)
+{
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_reset_dsp (); /*
+ * The only way to kill the UART mode
+ */
+ sb_free_irq ();
+ }
+ sb_intr_active = 0;
+ sb_midi_busy = 0;
+ input_opened = 0;
+}
+
+static int
+sb_midi_out (int dev, unsigned char midi_byte)
+{
+ unsigned long flags;
+
+ if (sb_midi_mode == NORMAL_MIDI)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x38))
+ sb_dsp_command (midi_byte);
+ else
+ printk ("SB Error: Unable to send a MIDI byte\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ sb_dsp_command (midi_byte); /*
+ * UART write
+ */
+
+ return 1;
+}
+
+static int
+sb_midi_start_read (int dev)
+{
+ if (sb_midi_mode != UART_MIDI)
+ {
+ printk ("SoundBlaster: MIDI input not implemented.\n");
+ return RET_ERROR (EPERM);
+ }
+ return 0;
+}
+
+static int
+sb_midi_end_read (int dev)
+{
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_reset_dsp ();
+ sb_intr_active = 0;
+ }
+ return 0;
+}
+
+static int
+sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EPERM);
+}
+
+void
+sb_midi_interrupt (int dummy)
+{
+ unsigned long flags;
+ unsigned char data;
+
+ DISABLE_INTR (flags);
+
+ data = INB (DSP_READ);
+ if (input_opened)
+ midi_input_intr (my_dev, data);
+
+ RESTORE_INTR (flags);
+}
+
+#define MIDI_SYNTH_NAME "SoundBlaster Midi"
+#define MIDI_SYNTH_CAPS 0
+#include "midi_synth.h"
+
+static struct midi_operations sb_midi_operations =
+{
+ {"SoundBlaster", 0, 0, SNDCARD_SB},
+ &std_midi_synth,
+ sb_midi_open,
+ sb_midi_close,
+ sb_midi_ioctl,
+ sb_midi_out,
+ sb_midi_start_read,
+ sb_midi_end_read,
+ NULL, /*
+ * Kick
+ */
+ NULL, /*
+ * command
+ */
+ NULL, /*
+ * buffer_status
+ */
+ NULL
+};
+
+void
+sb_midi_init (int model)
+{
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return;
+ }
+
+ std_midi_synth.midi_dev = num_midis;
+ my_dev = num_midis;
+ midi_devs[num_midis++] = &sb_midi_operations;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_mixer.c b/sys/i386/isa/sound/sb_mixer.c
new file mode 100644
index 000000000000..312a15c76fb9
--- /dev/null
+++ b/sys/i386/isa/sound/sb_mixer.c
@@ -0,0 +1,453 @@
+
+/*
+ * sound/sb_mixer.c
+ *
+ * The low level mixer driver for the SoundBlaster Pro and SB16 cards.
+ *
+ * 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.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support the Sound Galaxy NX Pro mixer.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO)
+#define __SB_MIXER_C__
+
+#include "sb.h"
+#include "sb_mixer.h"
+#undef SB_TEST_IRQ
+
+extern int sbc_base;
+
+static int mixer_initialized = 0;
+
+static int supported_rec_devices;
+static int supported_devices;
+static int recmask = 0;
+static int mixer_model;
+static int mixer_caps;
+static mixer_tab *iomap;
+
+void
+sb_setmixer (unsigned int port, unsigned int value)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /*
+ * Select register
+ */
+ tenmicrosec ();
+ OUTB ((unsigned char) (value & 0xff), MIXER_DATA);
+ tenmicrosec ();
+ RESTORE_INTR (flags);
+}
+
+int
+sb_getmixer (unsigned int port)
+{
+ int val;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /*
+ * Select register
+ */
+ tenmicrosec ();
+ val = INB (MIXER_DATA);
+ tenmicrosec ();
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+void
+sb_mixer_set_stereo (int mode)
+{
+ if (!mixer_initialized)
+ return;
+
+ sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC)
+ | (mode ? STEREO_DAC : MONO_DAC)));
+}
+
+/*
+ * Returns:
+ * 0 No mixer detected.
+ * 1 Only a plain Sound Blaster Pro style mixer detected.
+ * 2 The Sound Galaxy NX Pro mixer detected.
+ */
+static int
+detect_mixer (void)
+{
+#ifdef __SGNXPRO__
+ int oldbass, oldtreble;
+
+#endif
+ int retcode = 1;
+
+ /*
+ * Detect the mixer by changing parameters of two volume channels. If the
+ * values read back match with the values written, the mixer is there (is
+ * it?)
+ */
+ sb_setmixer (FM_VOL, 0xff);
+ sb_setmixer (VOC_VOL, 0x33);
+
+ if (sb_getmixer (FM_VOL) != 0xff)
+ return 0; /*
+ * No match
+ */
+ if (sb_getmixer (VOC_VOL) != 0x33)
+ return 0;
+
+#ifdef __SGNXPRO__
+ /* Attempt to detect the SG NX Pro by check for valid bass/treble
+ * registers.
+ */
+ oldbass = sb_getmixer (BASS_LVL);
+ oldtreble = sb_getmixer (TREBLE_LVL);
+
+ sb_setmixer (BASS_LVL, 0xaa);
+ sb_setmixer (TREBLE_LVL, 0x55);
+
+ if ((sb_getmixer (BASS_LVL) != 0xaa) ||
+ (sb_getmixer (TREBLE_LVL) != 0x55))
+ {
+ retcode = 1; /* 1 == Only SB Pro detected */
+ }
+ else
+ retcode = 2; /* 2 == SG NX Pro detected */
+ /* Restore register in either case since SG NX Pro has EEPROM with
+ * 'preferred' values stored.
+ */
+ sb_setmixer (BASS_LVL, oldbass);
+ sb_setmixer (TREBLE_LVL, oldtreble);
+#endif
+ return retcode;
+}
+
+static void
+change_bits (unsigned char *regval, int dev, int chn, int newval)
+{
+ unsigned char mask;
+ int shift;
+
+ mask = (1 << (*iomap)[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /*
+ * Scale it
+ */
+
+ shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1;
+
+ *regval &= ~(mask << shift); /*
+ * Filter out the previous value
+ */
+ *regval |= (newval & mask) << shift; /*
+ * Set the new value
+ */
+}
+
+static int
+sb_mixer_get (int dev)
+{
+ if (!((1 << dev) & supported_devices))
+ return RET_ERROR (EINVAL);
+
+ return levels[dev];
+}
+
+static int
+sb_mixer_set (int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+
+ int regoffs;
+ unsigned char val;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return RET_ERROR (EINVAL);
+
+ if (!(supported_devices & (1 << dev))) /*
+ * Not supported
+ */
+ return RET_ERROR (EINVAL);
+
+ regoffs = (*iomap)[dev][LEFT_CHN].regno;
+
+ if (regoffs == 0)
+ return RET_ERROR (EINVAL);
+
+ val = sb_getmixer (regoffs);
+ change_bits (&val, dev, LEFT_CHN, left);
+
+ levels[dev] = left | (left << 8);
+
+ if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) /*
+ * Change register
+ */
+ {
+ sb_setmixer (regoffs, val); /*
+ * Save the old one
+ */
+ regoffs = (*iomap)[dev][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return left | (left << 8); /*
+ * Just left channel present
+ */
+
+ val = sb_getmixer (regoffs); /*
+ * Read the new one
+ */
+ }
+
+ change_bits (&val, dev, RIGHT_CHN, right);
+ sb_setmixer (regoffs, val);
+
+ levels[dev] = left | (right << 8);
+ return left | (right << 8);
+}
+
+static void
+set_recsrc (int src)
+{
+ sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7));
+}
+
+static int
+set_recmask (int mask)
+{
+ int devmask, i;
+ unsigned char regimageL, regimageR;
+
+ devmask = mask & supported_rec_devices;
+
+ switch (mixer_model)
+ {
+ case 3:
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /*
+ * More than one devices selected. Drop the *
+ * previous selection
+ */
+ devmask &= ~recmask;
+ }
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /*
+ * More than one devices selected. Default to
+ * * mic
+ */
+ devmask = SOUND_MASK_MIC;
+ }
+
+
+ if (devmask ^ recmask) /*
+ * Input source changed
+ */
+ {
+ switch (devmask)
+ {
+
+ case SOUND_MASK_MIC:
+ set_recsrc (SRC_MIC);
+ break;
+
+ case SOUND_MASK_LINE:
+ set_recsrc (SRC_LINE);
+ break;
+
+ case SOUND_MASK_CD:
+ set_recsrc (SRC_CD);
+ break;
+
+ default:
+ set_recsrc (SRC_MIC);
+ }
+ }
+
+ break;
+
+ case 4:
+ if (!devmask)
+ devmask = SOUND_MASK_MIC;
+
+ regimageL = regimageR = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask)
+ {
+ regimageL |= sb16_recmasks_L[i];
+ regimageR |= sb16_recmasks_R[i];
+ }
+ sb_setmixer (SB16_IMASK_L, regimageL);
+ sb_setmixer (SB16_IMASK_R, regimageR);
+ break;
+ }
+
+ recmask = devmask;
+ return recmask;
+}
+
+static int
+sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg)));
+ break;
+
+ default:
+ return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, supported_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, supported_devices &
+ ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, supported_rec_devices);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, mixer_caps);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff));
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations sb_mixer_operations =
+{
+ sb_mixer_ioctl
+};
+
+static void
+sb_mixer_reset (void)
+{
+ int i;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ sb_mixer_set (i, levels[i]);
+ set_recmask (SOUND_MASK_MIC);
+}
+
+/*
+ * Returns a code depending on whether a SG NX Pro was detected.
+ * 1 == Plain SB Pro
+ * 2 == SG NX Pro detected.
+ * 3 == SB16
+ *
+ * Used to update message.
+ */
+int
+sb_mixer_init (int major_model)
+{
+ int mixer_type = 0;
+
+ sb_setmixer (0x00, 0); /* Reset mixer */
+
+ if (!(mixer_type = detect_mixer ()))
+ return 0; /* No mixer. Why? */
+
+ mixer_initialized = 1;
+ mixer_model = major_model;
+
+ switch (major_model)
+ {
+ case 3:
+ mixer_caps = SOUND_CAP_EXCL_INPUT;
+#ifdef __SGNXPRO__
+ if (mixer_type == 2) /* A SGNXPRO was detected */
+ {
+ supported_devices = SGNXPRO_MIXER_DEVICES;
+ supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
+ iomap = &sgnxpro_mix;
+ }
+ else
+#endif
+ {
+ supported_devices = SBPRO_MIXER_DEVICES;
+ supported_rec_devices = SBPRO_RECORDING_DEVICES;
+ iomap = &sbpro_mix;
+ mixer_type = 1;
+ }
+ break;
+
+ case 4:
+ mixer_caps = 0;
+ supported_devices = SB16_MIXER_DEVICES;
+ supported_rec_devices = SB16_RECORDING_DEVICES;
+ iomap = &sb16_mix;
+ mixer_type = 3;
+ break;
+
+ default:
+ printk ("SB Warning: Unsupported mixer type\n");
+ return 0;
+ }
+
+ if (num_mixers < MAX_MIXER_DEV)
+ mixer_devs[num_mixers++] = &sb_mixer_operations;
+ sb_mixer_reset ();
+ return mixer_type;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_mixer.h b/sys/i386/isa/sound/sb_mixer.h
new file mode 100644
index 000000000000..1a5fc8adfd36
--- /dev/null
+++ b/sys/i386/isa/sound/sb_mixer.h
@@ -0,0 +1,221 @@
+/*
+ * 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_RECLEV | \
+ 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
+#define OPSW 0x3c
+
+/*
+ * 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 LEFT_CHN 0
+#define RIGHT_CHN 1
+
+struct mixer_def {
+ unsigned int regno: 8;
+ unsigned int bitoffs:4;
+ unsigned int nbits:4;
+};
+
+
+typedef struct mixer_def mixer_tab[32][2];
+typedef struct mixer_def mixer_ent;
+
+#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
+ {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
+
+#ifdef __SB_MIXER_C__
+mixer_tab sbpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+
+#ifdef __SGNXPRO__
+mixer_tab sgnxpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
+
+mixer_tab sb16_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
+MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
+MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2)
+};
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x5a5a, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4b4b, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b}; /* Recording level */
+
+static unsigned 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 */
+};
+
+static unsigned 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 */
+};
+
+/*
+ * Recording sources (SB Pro)
+ */
+
+#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 */
+
+#endif
diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c
new file mode 100644
index 000000000000..45f06c1edfbf
--- /dev/null
+++ b/sys/i386/isa/sound/sequencer.c
@@ -0,0 +1,1861 @@
+/*
+ * sound/sequencer.c
+ *
+ * The sequencer personality manager.
+ *
+ * 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.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static int sequencer_ok = 0;
+static struct sound_timer_operations *tmr;
+static int tmr_no = -1; /* Currently selected timer */
+static int pending_timer = -1; /* For timer change operation */
+
+/*
+ * Local counts for number of synth and MIDI devices. These are initialized
+ * by the sequencer_open.
+ */
+static int max_mididev = 0;
+static int max_synthdev = 0;
+
+/*
+ * The seq_mode gives the operating mode of the sequencer:
+ * 1 = level1 (the default)
+ * 2 = level2 (extended capabilites)
+ */
+
+#define SEQ_1 1
+#define SEQ_2 2
+static int seq_mode = SEQ_1;
+
+DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+
+static int midi_opened[MAX_MIDI_DEV] =
+{0};
+static int midi_written[MAX_MIDI_DEV] =
+{0};
+
+unsigned long prev_input_time = 0;
+int prev_event_time;
+unsigned long seq_time = 0;
+
+#include "tuning.h"
+
+#define EV_SZ 8
+#define IEV_SZ 8
+static unsigned char *queue = NULL;
+static unsigned char *iqueue = NULL;
+
+static volatile int qhead = 0, qtail = 0, qlen = 0;
+static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
+static volatile int seq_playing = 0;
+static int sequencer_busy = 0;
+static int output_treshold;
+static int pre_event_timeout;
+static unsigned synth_open_mask;
+
+static int seq_queue (unsigned char *note);
+static void seq_startplay (void);
+static int seq_sync (void);
+static void seq_reset (void);
+static int pmgr_present[MAX_SYNTH_DEV] =
+{0};
+
+#if MAX_SYNTH_DEV > 15
+#error Too many synthesizer devices enabled.
+#endif
+
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c = count, p = 0;
+ int ev_len;
+ unsigned long flags;
+
+ dev = dev >> 4;
+
+ ev_len = seq_mode == SEQ_1 ? 4 : 8;
+
+ if (dev) /*
+ * Patch manager device
+ */
+ return pmgr_read (dev - 1, file, buf, count);
+
+ DISABLE_INTR (flags);
+ if (!iqlen)
+ {
+ DO_SLEEP (midi_sleeper, midi_sleep_flag, pre_event_timeout);
+
+ if (!iqlen)
+ {
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ }
+
+ while (iqlen && c >= ev_len)
+ {
+
+ COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], ev_len);
+ p += ev_len;
+ c -= ev_len;
+
+ iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+ iqlen--;
+ }
+ RESTORE_INTR (flags);
+
+ return count - c;
+}
+
+static void
+sequencer_midi_output (int dev)
+{
+ /*
+ * Currently NOP
+ */
+}
+
+void
+seq_copy_to_input (unsigned char *event, int len)
+{
+ unsigned long flags;
+
+ /*
+ * Verify that the len is valid for the current mode.
+ */
+
+ if (len != 4 && len != 8)
+ return;
+ if ((seq_mode == SEQ_1) != (len == 4))
+ return;
+
+ if (iqlen >= (SEQ_MAX_QUEUE - 1))
+ return; /* Overflow */
+
+ DISABLE_INTR (flags);
+ memcpy (&iqueue[iqtail * IEV_SZ], event, len);
+ iqlen++;
+ iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+
+ if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag))
+ {
+ WAKE_UP (midi_sleeper, midi_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+sequencer_midi_input (int dev, unsigned char data)
+{
+ unsigned int tstamp;
+ unsigned char event[4];
+
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
+
+ tstamp = GET_TIME () - seq_time;
+ if (tstamp != prev_input_time)
+ {
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+
+ seq_copy_to_input ((unsigned char *) &tstamp, 4);
+ prev_input_time = tstamp;
+ }
+
+ event[0] = SEQ_MIDIPUTC;
+ event[1] = data;
+ event[2] = dev;
+ event[3] = 0;
+
+ seq_copy_to_input (event, 4);
+}
+
+void
+seq_input_event (unsigned char *event, int len)
+{
+ unsigned long this_time;
+
+ if (seq_mode == SEQ_2)
+ this_time = tmr->get_time (tmr_no);
+ else
+ this_time = GET_TIME () - seq_time;
+
+ if (this_time != prev_input_time)
+ {
+ unsigned char tmp_event[8];
+
+ tmp_event[0] = EV_TIMING;
+ tmp_event[1] = TMR_WAIT_ABS;
+ tmp_event[2] = 0;
+ tmp_event[3] = 0;
+ *(unsigned long *) &tmp_event[4] = this_time;
+
+ seq_copy_to_input (tmp_event, 8);
+ prev_input_time = this_time;
+ }
+
+ seq_copy_to_input (event, len);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned char event[EV_SZ], ev_code;
+ int p = 0, c, ev_size;
+ int err;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count));
+
+ if (mode == OPEN_READ)
+ return RET_ERROR (EIO);
+
+ if (dev) /*
+ * Patch manager device
+ */
+ return pmgr_write (dev - 1, file, buf, count);
+
+ c = count;
+
+ while (c >= 4)
+ {
+ COPY_FROM_USER (event, buf, p, 4);
+ ev_code = event[0];
+
+ if (ev_code == SEQ_FULLSIZE)
+ {
+ int err;
+
+ dev = *(unsigned short *) &event[2];
+ if (dev < 0 || dev >= max_synthdev)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0);
+ if (err < 0)
+ return err;
+
+ return err;
+ }
+
+ if (ev_code >= 128)
+ {
+ if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
+ {
+ printk ("Sequencer: Invalid level 2 event %x\n", ev_code);
+ return RET_ERROR (EINVAL);
+ }
+
+ ev_size = 8;
+
+ if (c < ev_size)
+ {
+ if (!seq_playing)
+ seq_startplay ();
+ return count - c;
+ }
+
+ COPY_FROM_USER (&event[4], buf, p + 4, 4);
+
+ }
+ else
+ {
+ if (seq_mode == SEQ_2)
+ {
+ printk ("Sequencer: 4 byte event in level 2 mode\n");
+ return RET_ERROR (EINVAL);
+ }
+ ev_size = 4;
+ }
+
+ if (event[0] == SEQ_MIDIPUTC)
+ {
+
+ if (!midi_opened[event[2]])
+ {
+ int mode;
+ int dev = event[2];
+
+ if (dev >= max_mididev)
+ {
+ printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ mode = file->mode & O_ACCMODE;
+
+ if ((err = midi_devs[dev]->open (dev, mode,
+ sequencer_midi_input, sequencer_midi_output)) < 0)
+ {
+ seq_reset ();
+ printk ("Sequencer Error: Unable to open Midi #%d\n", dev);
+ return err;
+ }
+
+ midi_opened[dev] = 1;
+ }
+
+ }
+
+ if (!seq_queue (event))
+ {
+
+ if (!seq_playing)
+ seq_startplay ();
+ return count - c;
+ }
+
+ p += ev_size;
+ c -= ev_size;
+ }
+
+ if (!seq_playing)
+ seq_startplay ();
+
+ return count;
+}
+
+static int
+seq_queue (unsigned char *note)
+{
+
+ /*
+ * Test if there is space in the queue
+ */
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ if (!seq_playing)
+ seq_startplay (); /*
+ * Give chance to drain the queue
+ */
+
+ if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ /*
+ * Sleep until there is enough space on the queue
+ */
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+ }
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ return 0; /*
+ * To be sure
+ */
+
+ memcpy (&queue[qtail * EV_SZ], note, EV_SZ);
+
+ qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+ qlen++;
+
+ return 1;
+}
+
+static int
+extended_event (unsigned char *q)
+{
+ int dev = q[2];
+
+ if (dev < 0 || dev >= max_synthdev)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ switch (q[1])
+ {
+ case SEQ_NOTEOFF:
+ synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] > 127 && q[4] != 255)
+ return 0;
+
+ synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_PGMCHANGE:
+ synth_devs[dev]->set_instr (dev, q[3], q[4]);
+ break;
+
+ case SEQ_AFTERTOUCH:
+ synth_devs[dev]->aftertouch (dev, q[3], q[4]);
+ break;
+
+ case SEQ_BALANCE:
+ synth_devs[dev]->panning (dev, q[3], (char) q[4]);
+ break;
+
+ case SEQ_CONTROLLER:
+ synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]);
+ break;
+
+ case SEQ_VOLMODE:
+ if (synth_devs[dev]->volume_method != NULL)
+ synth_devs[dev]->volume_method (dev, q[3]);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static int
+find_voice (int dev, int chn, int note)
+{
+ unsigned short key;
+ int i;
+
+ key = (chn << 8) | (note + 1);
+
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if (synth_devs[dev]->alloc.map[i] == key)
+ return i;
+
+ return -1;
+}
+
+static int
+alloc_voice (int dev, int chn, int note)
+{
+ unsigned short key;
+ int voice;
+
+ key = (chn << 8) | (note + 1);
+
+ voice = synth_devs[dev]->alloc_voice (dev, chn, note,
+ &synth_devs[dev]->alloc);
+ synth_devs[dev]->alloc.map[voice] = key;
+ return voice;
+}
+
+static void
+seq_chn_voice_event (unsigned char *event)
+{
+ unsigned char dev = event[1];
+ unsigned char cmd = event[2];
+ unsigned char chn = event[3];
+ unsigned char note = event[4];
+ unsigned char parm = event[5];
+ int voice = -1;
+
+ if ((int) dev > max_synthdev)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
+
+ if (seq_mode == SEQ_2)
+ if (synth_devs[dev]->alloc_voice)
+ voice = find_voice (dev, chn, note);
+
+ if (cmd == MIDI_NOTEON && parm == 0)
+ {
+ cmd = MIDI_NOTEOFF;
+ parm = 64;
+ }
+
+ switch (cmd)
+ {
+ case MIDI_NOTEON:
+ if (note > 127)
+ return;
+
+ if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
+ {
+ voice = alloc_voice (dev, chn, note);
+ }
+
+ if (voice == -1)
+ voice = chn;
+
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->set_instr (dev, voice,
+ synth_devs[dev]->chn_info[chn].pgm_num);
+ }
+
+ synth_devs[dev]->start_note (dev, voice, note, parm);
+ break;
+
+ case MIDI_NOTEOFF:
+ if (voice == -1)
+ voice = chn;
+ synth_devs[dev]->kill_note (dev, voice, note, parm);
+ break;
+
+ case MIDI_KEY_PRESSURE:
+ /* To be implemented */
+ break;
+
+ default:;
+ }
+}
+
+static void
+seq_chn_common_event (unsigned char *event)
+{
+ unsigned char dev = event[1];
+ unsigned char cmd = event[2];
+ unsigned char chn = event[3];
+ unsigned char p1 = event[4];
+
+ /* unsigned char p2 = event[5]; */
+ unsigned short w14 = *(short *) &event[6];
+
+ if ((int) dev > max_synthdev)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
+
+ switch (cmd)
+ {
+ case MIDI_PGM_CHANGE:
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->chn_info[chn].pgm_num = p1;
+ }
+ else
+ synth_devs[dev]->set_instr (dev, chn, p1);
+ break;
+
+ case MIDI_CTL_CHANGE:
+ if (p1 == CTRL_MAIN_VOLUME)
+ {
+ w14 = (unsigned short) (((int) w14 * 16383) / 100);
+ p1 = CTL_MAIN_VOLUME;
+ }
+ if (p1 == CTRL_EXPRESSION)
+ {
+ w14 *= 128;
+ p1 = CTL_EXPRESSION;
+ }
+
+ if (seq_mode == SEQ_2)
+ {
+ if (chn > 15 || p1 > 127)
+ break;
+
+ synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0xff;
+
+ if (dev < num_synths)
+ {
+ int val = w14 & 0xff;
+
+ if (p1 < 64) /* Combine MSB and LSB */
+ {
+ val = ((synth_devs[dev]->
+ chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+ | (synth_devs[dev]->
+ chn_info[chn].controllers[p1 | 32] & 0x7f);
+ p1 &= ~32;
+ }
+ else
+ val = synth_devs[dev]->chn_info[chn].controllers[p1];
+
+ synth_devs[dev]->controller (dev, chn, p1, val);
+ }
+ else
+ synth_devs[dev]->controller (dev, chn, p1, w14);
+ }
+ else
+ synth_devs[dev]->controller (dev, chn, p1, w14);
+ break;
+
+ case MIDI_PITCH_BEND:
+ synth_devs[dev]->bender (dev, chn, w14);
+ break;
+
+ default:;
+ }
+}
+
+static int
+seq_timing_event (unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned int parm = *(int *) &event[4];
+
+ if (seq_mode == SEQ_2)
+ {
+ int ret;
+
+ if ((ret = tmr->event (tmr_no, event)) == TIMER_ARMED)
+ {
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+ }
+ }
+ return ret;
+ }
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+
+ /*
+ * NOTE! No break here. Execution of TMR_WAIT_REL continues in the
+ * next case (TMR_WAIT_ABS)
+ */
+
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ seq_playing = 1;
+ time = parm;
+ prev_event_time = time;
+
+ request_sound_timer (time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+ }
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+ break;
+
+ case TMR_STOP:
+ break;
+
+ case TMR_CONTINUE:
+ break;
+
+ case TMR_TEMPO:
+ break;
+
+ case TMR_ECHO:
+ if (seq_mode == SEQ_2)
+ seq_copy_to_input (event, 8);
+ else
+ {
+ parm = (parm << 8 | SEQ_ECHO);
+ seq_copy_to_input ((unsigned char *) &parm, 4);
+ }
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static void
+seq_local_event (unsigned char *event)
+{
+ /* unsigned char cmd = event[1]; */
+
+ printk ("seq_local_event() called. WHY????????\n");
+}
+
+static void
+seq_startplay (void)
+{
+ int this_one;
+ unsigned long *delay;
+ unsigned char *q;
+
+ while (qlen > 0)
+ {
+
+ seq_playing = 1;
+
+ qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+ qlen--;
+
+ q = &queue[this_one * EV_SZ];
+
+ switch (q[0])
+ {
+ case SEQ_NOTEOFF:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->kill_note (0, q[1], 255, q[3]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] < 128 || q[4] == 255)
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->start_note (0, q[1], q[2], q[3]);
+ break;
+
+ case SEQ_WAIT:
+ delay = (unsigned long *) q; /*
+ * Bytes 1 to 3 are containing the *
+ * delay in GET_TIME()
+ */
+ *delay = (*delay >> 8) & 0xffffff;
+
+ if (*delay > 0)
+ {
+ long time;
+
+ seq_playing = 1;
+ time = *delay;
+ prev_event_time = time;
+
+ request_sound_timer (time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+ }
+ /*
+ * The timer is now active and will reinvoke this function
+ * after the timer expires. Return to the caller now.
+ */
+ return;
+ }
+ break;
+
+ case SEQ_PGMCHANGE:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->set_instr (0, q[1], q[2]);
+ break;
+
+ case SEQ_SYNCTIMER: /*
+ * Reset timer
+ */
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+ break;
+
+ case SEQ_MIDIPUTC: /*
+ * Put a midi character
+ */
+ if (midi_opened[q[2]])
+ {
+ int dev;
+
+ dev = q[2];
+
+ if (!midi_devs[dev]->putc (dev, q[1]))
+ {
+ /*
+ * Output FIFO is full. Wait one timer cycle and try again.
+ */
+
+ qlen++;
+ qhead = this_one; /*
+ * Restore queue
+ */
+ seq_playing = 1;
+ request_sound_timer (-1);
+ return;
+ }
+ else
+ midi_written[dev] = 1;
+ }
+ break;
+
+ case SEQ_ECHO:
+ seq_copy_to_input (q, 4); /*
+ * Echo back to the process
+ */
+ break;
+
+ case SEQ_PRIVATE:
+ if ((int) q[1] < max_synthdev)
+ synth_devs[q[1]]->hw_control (q[1], q);
+ break;
+
+ case SEQ_EXTENDED:
+ extended_event (q);
+ break;
+
+ case EV_CHN_VOICE:
+ seq_chn_voice_event (q);
+ break;
+
+ case EV_CHN_COMMON:
+ seq_chn_common_event (q);
+ break;
+
+ case EV_TIMING:
+ if (seq_timing_event (q) == TIMER_ARMED)
+ {
+ return;
+ }
+ break;
+
+ case EV_SEQ_LOCAL:
+ seq_local_event (q);
+ break;
+
+ default:;
+ }
+
+ }
+
+ seq_playing = 0;
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+ }
+
+}
+
+static void
+reset_controllers (int dev, unsigned char *controller, int update_dev)
+{
+#include "midi_ctrl.h"
+
+ int i;
+
+ for (i = 0; i < 128; i++)
+ controller[i] = ctrl_def_values[i];
+}
+
+static void
+setup_mode2 (void)
+{
+ int dev;
+
+ max_synthdev = num_synths;
+
+ for (dev = 0; dev < num_midis; dev++)
+ if (midi_devs[dev]->converter != NULL)
+ {
+ synth_devs[max_synthdev++] =
+ midi_devs[dev]->converter;
+ }
+
+ for (dev = 0; dev < max_synthdev; dev++)
+ {
+ int chn;
+
+ for (chn = 0; chn < 16; chn++)
+ {
+ synth_devs[dev]->chn_info[chn].pgm_num = 0;
+ reset_controllers (dev,
+ synth_devs[dev]->chn_info[chn].controllers,
+ 0);
+ }
+ }
+
+ max_mididev = 0;
+ seq_mode = SEQ_2;
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+{
+ int retval, mode, i;
+ int level, tmp;
+
+ level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ DEB (printk ("sequencer_open(dev=%d)\n", dev));
+
+ if (!sequencer_ok)
+ {
+ printk ("Soundcard: Sequencer not initialized\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev) /*
+ * Patch manager device
+ */
+ {
+ int err;
+
+ dev--;
+
+ if (dev >= MAX_SYNTH_DEV)
+ return RET_ERROR (ENXIO);
+ if (pmgr_present[dev])
+ return RET_ERROR (EBUSY);
+ if ((err = pmgr_open (dev)) < 0)
+ return err; /*
+ * Failed
+ */
+
+ pmgr_present[dev] = 1;
+ return err;
+ }
+
+ if (sequencer_busy)
+ {
+ printk ("Sequencer busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ max_mididev = num_midis;
+ max_synthdev = num_synths;
+ pre_event_timeout = 0;
+ seq_mode = SEQ_1;
+
+ if (pending_timer != -1)
+ {
+ tmr_no = pending_timer;
+ pending_timer = -1;
+ }
+
+ if (tmr_no == -1) /* Not selected yet */
+ {
+ int i, best;
+
+ best = -1;
+ for (i = 0; i < num_sound_timers; i++)
+ if (sound_timer_devs[i]->priority > best)
+ {
+ tmr_no = i;
+ best = sound_timer_devs[i]->priority;
+ }
+
+ if (tmr_no == -1) /* Should not be */
+ tmr_no = 0;
+ }
+
+ tmr = sound_timer_devs[tmr_no];
+
+ if (level == 2)
+ {
+ printk ("Using timer #%d\n", tmr_no);
+ if (tmr == NULL)
+ {
+ printk ("sequencer: No timer for level 2\n");
+ return RET_ERROR (ENXIO);
+ }
+ setup_mode2 ();
+ }
+
+ if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+ if (!max_mididev)
+ {
+ printk ("Sequencer: No Midi devices. Input not possible\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!max_synthdev && !max_mididev)
+ return RET_ERROR (ENXIO);
+
+ synth_open_mask = 0;
+
+ for (i = 0; i < max_mididev; i++)
+ {
+ midi_opened[i] = 0;
+ midi_written[i] = 0;
+ }
+
+ /*
+ * if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ */
+ for (i = 0; i < max_synthdev; i++) /*
+ * Open synth devices
+ */
+ if ((tmp = synth_devs[i]->open (i, mode)) < 0)
+ {
+ printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+ if (synth_devs[i]->midi_dev)
+ printk ("(Maps to midi dev #%d\n", synth_devs[i]->midi_dev);
+ }
+ else
+ {
+ synth_open_mask |= (1 << i);
+ if (synth_devs[i]->midi_dev) /*
+ * Is a midi interface
+ */
+ midi_opened[synth_devs[i]->midi_dev] = 1;
+ }
+
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+
+ if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+ { /*
+ * Initialize midi input devices
+ */
+ for (i = 0; i < max_mididev; i++)
+ if (!midi_opened[i])
+ {
+ if ((retval = midi_devs[i]->open (i, mode,
+ sequencer_midi_input, sequencer_midi_output)) >= 0)
+ midi_opened[i] = 1;
+ }
+ }
+
+ if (seq_mode == SEQ_2)
+ {
+ tmr->open (tmr_no, seq_mode);
+ }
+
+ sequencer_busy = 1;
+ RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+ RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+ output_treshold = SEQ_MAX_QUEUE / 2;
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0);
+
+ return 0;
+}
+
+void
+seq_drain_midi_queues (void)
+{
+ int i, n;
+
+ /*
+ * Give the Midi drivers time to drain their output queues
+ */
+
+ n = 1;
+
+ while (!PROCESS_ABORTING (midi_sleeper, midi_sleep_flag) && n)
+ {
+ n = 0;
+
+ for (i = 0; i < max_mididev; i++)
+ if (midi_opened[i] && midi_written[i])
+ if (midi_devs[i]->buffer_status != NULL)
+ if (midi_devs[i]->buffer_status (i))
+ n++;
+
+ /*
+ * Let's have a delay
+ */
+ if (n)
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10);
+ }
+ }
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+{
+ int i;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ DEB (printk ("sequencer_release(dev=%d)\n", dev));
+
+ if (dev) /*
+ * Patch manager device
+ */
+ {
+ dev--;
+ pmgr_release (dev);
+ pmgr_present[dev] = 0;
+ return;
+ }
+
+ /*
+ * * Wait until the queue is empty
+ */
+
+ if (mode != OPEN_READ)
+ while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen)
+ {
+ seq_sync ();
+ }
+
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues (); /*
+ * Ensure the output queues are empty
+ */
+ seq_reset ();
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues (); /*
+ * Flush the all notes off messages
+ */
+
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i)) /*
+ * Actually opened
+ */
+ if (synth_devs[i])
+ {
+ synth_devs[i]->close (i);
+
+ if (synth_devs[i]->midi_dev)
+ midi_opened[synth_devs[i]->midi_dev] = 0;
+ }
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+
+ for (i = 0; i < max_mididev; i++)
+ if (midi_opened[i])
+ midi_devs[i]->close (i);
+
+ if (seq_mode == SEQ_2)
+ tmr->close (tmr_no);
+
+ sequencer_busy = 0;
+}
+
+static int
+seq_sync (void)
+{
+ if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+ seq_startplay ();
+
+ if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) /*
+ * Queue not
+ * empty
+ */
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+ }
+
+ return qlen;
+}
+
+static void
+midi_outc (int dev, unsigned char data)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int n;
+
+ /*
+ * This routine sends one byte to the Midi channel.
+ */
+ /*
+ * If the output Fifo is full, it waits until there
+ */
+ /*
+ * is space in the queue
+ */
+
+ n = 300; /*
+ * Timeout in jiffies
+ */
+
+ while (n && !midi_devs[dev]->putc (dev, data))
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 4);
+ n--;
+ }
+}
+
+static void
+seq_reset (void)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int i;
+
+ int chn;
+
+ sound_stop_timer ();
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+
+ qlen = qhead = qtail = 0;
+ iqlen = iqhead = iqtail = 0;
+
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ synth_devs[i]->reset (i);
+
+ if (seq_mode == SEQ_2)
+ {
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ for (chn = 0; chn < 16; chn++)
+ synth_devs[i]->controller (i, chn, 0xfe, 0); /* All notes off */
+ }
+ else
+ {
+ for (i = 0; i < max_mididev; i++)
+ if (midi_written[i]) /*
+ * Midi used. Some notes may still be playing
+ */
+ {
+ /*
+ * Sending just a ACTIVE SENSING message should be enough to stop all
+ * playing notes. Since there are devices not recognizing the
+ * active sensing, we have to send some all notes off messages also.
+ */
+ midi_outc (i, 0xfe);
+
+ for (chn = 0; chn < 16; chn++)
+ {
+ midi_outc (i,
+ (unsigned char) (0xb0 + (chn & 0xff))); /*
+ * Channel
+ * msg
+ */
+ midi_outc (i, 0x7b); /*
+ * All notes off
+ */
+ midi_outc (i, 0); /*
+ * Dummy parameter
+ */
+ }
+
+ midi_devs[i]->close (i);
+
+ midi_written[i] = 0;
+ midi_opened[i] = 0;
+ }
+ }
+
+ seq_playing = 0;
+
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ printk ("Sequencer Warning: Unexpected sleeping process\n");
+
+}
+
+static void
+seq_panic (void)
+{
+ /*
+ * This routine is called by the application in case the user
+ * wants to reset the system to the default state.
+ */
+
+ seq_reset ();
+
+ /*
+ * Since some of the devices don't recognize the active sensing and
+ * all notes off messages, we have to shut all notes manually.
+ *
+ * TO BE IMPLEMENTED LATER
+ */
+
+ /*
+ * Also return the controllers to their default states
+ */
+}
+
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ int midi_dev, orig_dev;
+ int mode = file->mode & O_ACCMODE;
+
+ orig_dev = dev = dev >> 4;
+
+ switch (cmd)
+ {
+ case SNDCTL_TMR_TIMEBASE:
+ case SNDCTL_TMR_TEMPO:
+ case SNDCTL_TMR_START:
+ case SNDCTL_TMR_STOP:
+ case SNDCTL_TMR_CONTINUE:
+ case SNDCTL_TMR_METRONOME:
+ case SNDCTL_TMR_SOURCE:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (seq_mode != SEQ_2)
+ return RET_ERROR (EINVAL);
+ return tmr->ioctl (tmr_no, cmd, arg);
+ break;
+
+ case SNDCTL_TMR_SELECT:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (seq_mode != SEQ_2)
+ return RET_ERROR (EINVAL);
+ pending_timer = IOCTL_IN (arg);
+
+ if (pending_timer < 0 || pending_timer >= num_sound_timers)
+ {
+ pending_timer = -1;
+ return RET_ERROR (EINVAL);
+ }
+
+ return IOCTL_OUT (arg, pending_timer);
+ break;
+
+ case SNDCTL_SEQ_PANIC:
+ seq_panic ();
+ break;
+
+ case SNDCTL_SEQ_SYNC:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return 0;
+ while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+ seq_sync ();
+ if (qlen)
+ return RET_ERROR (EINTR);
+ else
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESET:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ seq_reset ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_TESTMIDI:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ midi_dev = IOCTL_IN (arg);
+ if (midi_dev >= max_mididev)
+ return RET_ERROR (ENXIO);
+
+ if (!midi_opened[midi_dev])
+ {
+ int err, mode;
+
+ mode = file->mode & O_ACCMODE;
+ if ((err = midi_devs[midi_dev]->open (midi_dev, mode,
+ sequencer_midi_input,
+ sequencer_midi_output)) < 0)
+ return err;
+ }
+
+ midi_opened[midi_dev] = 1;
+
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_WRITE)
+ return 0;
+ return IOCTL_OUT (arg, iqlen);
+ break;
+
+ case SNDCTL_SEQ_GETOUTCOUNT:
+
+ if (mode == OPEN_READ)
+ return 0;
+ return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen);
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ /*
+ * If *arg == 0, just return the current rate
+ */
+ if (seq_mode == SEQ_2)
+ return tmr->ioctl (tmr_no, cmd, arg);
+
+ if (IOCTL_IN (arg) != 0)
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, HZ);
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ dev = IOCTL_IN (arg);
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ if (!orig_dev && pmgr_present[dev])
+ pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0);
+
+ return synth_devs[dev]->ioctl (dev, cmd, arg);
+ break;
+
+ case SNDCTL_SEQ_NRSYNTHS:
+ return IOCTL_OUT (arg, max_synthdev);
+ break;
+
+ case SNDCTL_SEQ_NRMIDIS:
+ return IOCTL_OUT (arg, max_mididev);
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ {
+ int dev = IOCTL_IN (arg);
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg));
+ }
+ break;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ {
+ int dev = IOCTL_IN (arg);
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ synth_devs[dev]->ioctl (dev, cmd, arg);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_SYNTH_INFO:
+ {
+ struct synth_info inf;
+ int dev;
+
+ IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+ dev = inf.device;
+
+ if (dev < 0 || dev >= max_synthdev)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ return synth_devs[dev]->ioctl (dev, cmd, arg);
+ }
+ break;
+
+ case SNDCTL_MIDI_INFO:
+ {
+ struct midi_info inf;
+ int dev;
+
+ IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+ dev = inf.device;
+
+ if (dev < 0 || dev >= max_mididev)
+ return RET_ERROR (ENXIO);
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf));
+ return 0;
+ }
+ break;
+
+ case SNDCTL_PMGR_IFACE:
+ {
+ struct patmgr_info *inf;
+ int dev, err;
+
+ inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf));
+
+ IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+ dev = inf->device;
+
+ if (dev < 0 || dev >= num_synths)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!synth_devs[dev]->pmgr_interface)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1)
+ {
+ KERNEL_FREE (inf);
+ return err;
+ }
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+ KERNEL_FREE (inf);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_PMGR_ACCESS:
+ {
+ struct patmgr_info *inf;
+ int dev, err;
+
+ inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf));
+
+ IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+ dev = inf->device;
+
+ if (dev < 0 || dev >= num_synths)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!pmgr_present[dev])
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ESRCH);
+ }
+
+ if ((err = pmgr_access (dev, inf)) < 0)
+ {
+ KERNEL_FREE (inf);
+ return err;
+ }
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+ KERNEL_FREE (inf);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_SEQ_TRESHOLD:
+ {
+ int tmp = IOCTL_IN (arg);
+
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (tmp < 1)
+ tmp = 1;
+ if (tmp >= SEQ_MAX_QUEUE)
+ tmp = SEQ_MAX_QUEUE - 1;
+ output_treshold = tmp;
+ return 0;
+ }
+ break;
+
+ case SNDCTL_MIDI_PRETIME:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val < 0)
+ val = 0;
+
+ val = (HZ * val) / 10;
+ pre_event_timeout = val;
+ return IOCTL_OUT (arg, val);
+ }
+ break;
+
+ default:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return RET_ERROR (EIO);
+
+ if (!synth_devs[0])
+ return RET_ERROR (ENXIO);
+ if (!(synth_open_mask & (1 << 0)))
+ return RET_ERROR (ENXIO);
+ return synth_devs[0]->ioctl (0, cmd, arg);
+ break;
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+#ifdef ALLOW_SELECT
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ unsigned long flags;
+
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!iqlen)
+ {
+ DISABLE_INTR (flags);
+ midi_sleep_flag.mode = WK_SLEEP;
+ select_wait (&midi_sleeper, wait);
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_OUT:
+ if (qlen >= SEQ_MAX_QUEUE)
+ {
+ DISABLE_INTR (flags);
+ seq_sleep_flag.mode = WK_SLEEP;
+ select_wait (&seq_sleeper, wait);
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif
+
+void
+sequencer_timer (void)
+{
+ seq_startplay ();
+}
+
+int
+note_to_freq (int note_num)
+{
+
+ /*
+ * This routine converts a midi note to a frequency (multiplied by 1000)
+ */
+
+ int note, octave, note_freq;
+ int notes[] =
+ {
+ 261632, 277189, 293671, 311132, 329632, 349232,
+ 369998, 391998, 415306, 440000, 466162, 493880
+ };
+
+#define BASE_OCTAVE 5
+
+ octave = note_num / 12;
+ note = note_num % 12;
+
+ note_freq = notes[note];
+
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+
+ /*
+ * note_freq >>= 1;
+ */
+
+ return note_freq;
+}
+
+unsigned long
+compute_finetune (unsigned long base_freq, int bend, int range)
+{
+ unsigned long amount;
+ int negative, semitones, cents, multiplier = 1;
+
+ if (!bend)
+ return base_freq;
+ if (!range)
+ return base_freq;
+
+ if (!base_freq)
+ return base_freq;
+
+ if (range >= 8192)
+ range = 8191;
+
+ bend = bend * range / 8192;
+ if (!bend)
+ return base_freq;
+
+ negative = bend < 0 ? 1 : 0;
+
+ if (bend < 0)
+ bend *= -1;
+ if (bend > range)
+ bend = range;
+
+ /*
+ if (bend > 2399)
+ bend = 2399;
+ */
+ while (bend > 2399)
+ {
+ multiplier *= 4;
+ bend -= 2400;
+ }
+
+ semitones = bend / 100;
+ cents = bend % 100;
+
+ amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents])
+ / 10000;
+
+ if (negative)
+ return (base_freq * 10000) / amount; /*
+ * Bend down
+ */
+ else
+ return (base_freq * amount) / 10000; /*
+ * Bend up
+ */
+}
+
+
+long
+sequencer_init (long mem_start)
+{
+
+ sequencer_ok = 1;
+ PERMANENT_MALLOC (unsigned char *, queue, SEQ_MAX_QUEUE * EV_SZ, mem_start);
+ PERMANENT_MALLOC (unsigned char *, iqueue, SEQ_MAX_QUEUE * IEV_SZ, mem_start);
+
+ return mem_start;
+}
+
+#else
+/*
+ * Stub version
+ */
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+{
+}
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+sequencer_init (long mem_start)
+{
+ return mem_start;
+}
+
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ return RET_ERROR (EIO);
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h
new file mode 100644
index 000000000000..eccd9b67336e
--- /dev/null
+++ b/sys/i386/isa/sound/sound_calls.h
@@ -0,0 +1,243 @@
+/*
+ * DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size);
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len);
+int DMAbuf_rmchars(int dev, int buff_no, int c);
+int DMAbuf_start_output(int dev, int buff_no, int l);
+int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local);
+long DMAbuf_init(long mem_start);
+int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+int DMAbuf_open_dma (int chan);
+void DMAbuf_close_dma (int chan);
+void DMAbuf_reset_dma (int chan);
+void DMAbuf_inputintr(int dev);
+void DMAbuf_outputintr(int dev, int underflow_flag);
+
+/*
+ * System calls for /dev/dsp and /dev/audio
+ */
+
+int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_open (int dev, struct fileinfo *file);
+void audio_release (int dev, struct fileinfo *file);
+int audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long audio_init (long mem_start);
+
+/*
+ * System calls for the /dev/sequencer
+ */
+
+int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_open (int dev, struct fileinfo *file);
+void sequencer_release (int dev, struct fileinfo *file);
+int sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long sequencer_init (long mem_start);
+void sequencer_timer(void);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+void seq_input_event(unsigned char *event, int len);
+void seq_copy_to_input (unsigned char *event, int len);
+
+#ifdef ALLOW_SELECT
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for the /dev/midi
+ */
+
+int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_open (int dev, struct fileinfo *file);
+void MIDIbuf_release (int dev, struct fileinfo *file);
+int MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
+long MIDIbuf_init(long mem_start);
+
+#ifdef ALLOW_SELECT
+int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for the generic midi interface.
+ *
+ */
+
+long CMIDI_init (long mem_start);
+int CMIDI_open (int dev, struct fileinfo *file);
+int CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int CMIDI_close (int dev, struct fileinfo *file);
+
+/*
+ *
+ * Misc calls from various sources
+ */
+
+/* From pro_midi.c */
+
+long pro_midi_attach(long mem_start);
+int pro_midi_open(int dev, int mode);
+void pro_midi_close(int dev);
+int pro_midi_write(int dev, snd_rw_buf *uio);
+int pro_midi_read(int dev, snd_rw_buf *uio);
+
+/* From soundcard.c */
+long soundcard_init(long mem_start);
+void tenmicrosec(void);
+void request_sound_timer (int count);
+void sound_stop_timer(void);
+int snd_ioctl_return(int *addr, int value);
+int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int));
+void snd_release_irq(int vect);
+void sound_dma_malloc(int dev);
+void sound_dma_free(int dev);
+
+/* From sound_switch.c */
+int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_open_sw (int dev, struct fileinfo *file);
+void sound_release_sw (int dev, struct fileinfo *file);
+int sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg);
+
+/* From sb_dsp.c */
+int sb_dsp_detect (struct address_info *hw_config);
+long sb_dsp_init (long mem_start, struct address_info *hw_config);
+void sb_dsp_disable_midi(void);
+int sb_get_irq(void);
+void sb_free_irq(void);
+int sb_dsp_command (unsigned char val);
+int sb_reset_dsp (void);
+
+/* From sb16_dsp.c */
+void sb16_dsp_interrupt (int unused);
+long sb16_dsp_init(long mem_start, struct address_info *hw_config);
+int sb16_dsp_detect(struct address_info *hw_config);
+
+/* From sb16_midi.c */
+void sb16midiintr (int unit);
+long attach_sb16midi(long mem_start, struct address_info * hw_config);
+int probe_sb16midi(struct address_info *hw_config);
+void sb_midi_interrupt(int dummy);
+
+/* From sb_midi.c */
+void sb_midi_init(int model);
+
+/* From sb_mixer.c */
+void sb_setmixer (unsigned int port, unsigned int value);
+int sb_getmixer (unsigned int port);
+void sb_mixer_set_stereo(int mode);
+int sb_mixer_init(int major_model);
+
+/* From opl3.c */
+int opl3_detect (int ioaddr);
+long opl3_init(long mem_start);
+
+/* From sb_card.c */
+long attach_sb_card(long mem_start, struct address_info *hw_config);
+int probe_sb(struct address_info *hw_config);
+
+/* From adlib_card.c */
+long attach_adlib_card(long mem_start, struct address_info *hw_config);
+int probe_adlib(struct address_info *hw_config);
+
+/* From pas_card.c */
+long attach_pas_card(long mem_start, struct address_info *hw_config);
+int probe_pas(struct address_info *hw_config);
+int pas_set_intr(int mask);
+int pas_remove_intr(int mask);
+unsigned char pas_read(int ioaddr);
+void pas_write(unsigned char data, int ioaddr);
+
+/* From pas_audio.c */
+void pas_pcm_interrupt(unsigned char status, int cause);
+long pas_pcm_init(long mem_start, struct address_info *hw_config);
+
+/* From pas_mixer.c */
+int pas_init_mixer(void);
+
+/* From pas_midi.c */
+long pas_midi_init(long mem_start);
+void pas_midi_interrupt(void);
+
+/* From gus_card.c */
+long attach_gus_card(long mem_start, struct address_info * hw_config);
+int probe_gus(struct address_info *hw_config);
+int gus_set_midi_irq(int num);
+void gusintr(int);
+long attach_gus_db16(long mem_start, struct address_info * hw_config);
+int probe_gus_db16(struct address_info *hw_config);
+
+/* From gus_wave.c */
+int gus_wave_detect(int baseaddr);
+long gus_wave_init(long mem_start, int irq, int dma);
+void gus_voice_irq(void);
+unsigned char gus_read8 (int reg);
+void gus_write8(int reg, unsigned int data);
+void guswave_dma_irq(void);
+void gus_delay(void);
+int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg);
+
+/* From gus_midi.c */
+long gus_midi_init(long mem_start);
+void gus_midi_interrupt(int dummy);
+
+/* From mpu401.c */
+long attach_mpu401(long mem_start, struct address_info * hw_config);
+int probe_mpu401(struct address_info *hw_config);
+
+/* From uart6850.c */
+long attach_uart6850(long mem_start, struct address_info * hw_config);
+int probe_uart6850(struct address_info *hw_config);
+
+/* From opl3.c */
+void enable_opl3_mode(int left, int right, int both);
+
+/* From patmgr.c */
+int pmgr_open(int dev);
+void pmgr_release(int dev);
+int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_access(int dev, struct patmgr_info *rec);
+int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2,
+ unsigned long parm3, unsigned long parm4);
+
+/* From ics2101.c */
+long ics2101_mixer_init(long mem_start);
+
+/* From sound_timer.c */
+void sound_timer_init(int io_base);
+void sound_timer_interrupt(void);
+
+/* From ad1848.c */
+void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture);
+int ad1848_detect (int io_base);
+void ad1848_interrupt (int dev);
+long attach_ms_sound(long mem_start, struct address_info * hw_config);
+int probe_ms_sound(struct address_info *hw_config);
+
+/* From pss.c */
+int probe_pss (struct address_info *hw_config);
+long attach_pss (long mem_start, struct address_info *hw_config);
+
+int pss_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int pss_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int pss_open (int dev, struct fileinfo *file);
+void pss_release (int dev, struct fileinfo *file);
+int pss_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int pss_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long pss_init(long mem_start);
diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h
new file mode 100644
index 000000000000..6c53c8cdd0cd
--- /dev/null
+++ b/sys/i386/isa/sound/sound_config.h
@@ -0,0 +1,258 @@
+/* sound_config.h
+ *
+ * A driver for Soundcards, misc configuration parameters.
+ *
+ *
+ * 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.
+ *
+ */
+
+#include "local.h"
+
+#if defined(ISC) || defined(SCO) || defined(SVR42)
+#define GENERIC_SYSV
+#endif
+
+/*
+ * Disable the AD1848 driver if there are no other drivers requiring it.
+ */
+
+#if defined(EXCLUDE_GUS16) && defined(EXCLUDE_MSS) && defined(EXCLUDE_PSS) && defined(EXCLUDE_GUSMAX)
+#define EXCLUDE_AD1848
+#endif
+
+#undef CONFIGURE_SOUNDCARD
+#undef DYNAMIC_BUFFER
+
+#ifdef KERNEL_SOUNDCARD
+#define CONFIGURE_SOUNDCARD
+#define DYNAMIC_BUFFER
+#undef LOADABLE_SOUNDCARD
+#endif
+
+#ifdef EXCLUDE_SEQUENCER
+#define EXCLUDE_MIDI
+#define EXCLUDE_YM3812
+#define EXCLUDE_OPL3
+#endif
+
+#ifndef SND_DEFAULT_ENABLE
+#define SND_DEFAULT_ENABLE 1
+#endif
+
+#ifdef CONFIGURE_SOUNDCARD
+
+/* ****** IO-address, DMA and IRQ settings ****
+
+If your card has nonstandard I/O address or IRQ number, change defines
+ for the following settings in your kernel Makefile */
+
+#ifndef SBC_BASE
+#define SBC_BASE 0x220 /* 0x220 is the factory default. */
+#endif
+
+#ifndef SBC_IRQ
+#define SBC_IRQ 7 /* IQR7 is the factory default. */
+#endif
+
+#ifndef SBC_DMA
+#define SBC_DMA 1
+#endif
+
+#ifndef SB16_DMA
+#define SB16_DMA 6
+#endif
+
+#ifndef SB16MIDI_BASE
+#define SB16MIDI_BASE 0x300
+#endif
+
+#ifndef PAS_BASE
+#define PAS_BASE 0x388
+#endif
+
+#ifndef PAS_IRQ
+#define PAS_IRQ 5
+#endif
+
+#ifndef PAS_DMA
+#define PAS_DMA 3
+#endif
+
+#ifndef GUS_BASE
+#define GUS_BASE 0x220
+#endif
+
+#ifndef GUS_IRQ
+#define GUS_IRQ 15
+#endif
+
+#ifndef GUS_MIDI_IRQ
+#define GUS_MIDI_IRQ GUS_IRQ
+#endif
+
+#ifndef GUS_DMA
+#define GUS_DMA 6
+#endif
+
+#ifndef MPU_BASE
+#define MPU_BASE 0x330
+#endif
+
+#ifndef MPU_IRQ
+#define MPU_IRQ 6
+#endif
+
+/* Echo Personal Sound System */
+#ifndef PSS_BASE
+#define PSS_BASE 0x220 /* 0x240 or */
+#endif
+
+#ifndef PSS_IRQ
+#define PSS_IRQ 7
+#endif
+
+#ifndef PSS_DMA
+#define PSS_DMA 1
+#endif
+
+#ifndef MAX_REALTIME_FACTOR
+#define MAX_REALTIME_FACTOR 4
+#endif
+
+/************* PCM DMA buffer sizes *******************/
+
+/* If you are using high playback or recording speeds, the default buffersize
+ is too small. DSP_BUFFSIZE must be 64k or less.
+
+ A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and
+ 4k for SB.
+
+ If you change the DSP_BUFFSIZE, don't modify this file.
+ Use the make config command instead. */
+
+#ifndef DSP_BUFFSIZE
+#define DSP_BUFFSIZE (4096)
+#endif
+
+#ifndef DSP_BUFFCOUNT
+#define DSP_BUFFCOUNT 2 /* 2 is recommended. */
+#endif
+
+#define DMA_AUTOINIT 0x10
+
+#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
+
+/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
+ driver. (There is no need to alter this) */
+#define SEQ_MAX_QUEUE 1024
+
+#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */
+/* 128 instruments for general MIDI setup and 16 unassigned */
+
+/*
+ * 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_NDEVS 256 /* Number of supported devices */
+#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/sequecer, 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 MAX_AUDIO_DEV 5
+#define MAX_MIXER_DEV 2
+#define MAX_SYNTH_DEV 3
+#define MAX_MIDI_DEV 6
+#define MAX_TIMER_DEV 3
+
+struct fileinfo {
+ int mode; /* Open mode */
+ };
+
+struct address_info {
+ int io_base;
+ int irq;
+ int dma;
+};
+
+#define SYNTH_MAX_VOICES 32
+
+struct voice_alloc_info {
+ int max_voice;
+ int used_voices;
+ int ptr; /* For device specific use */
+ unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+ };
+
+struct channel_info {
+ int pgm_num;
+ unsigned char controllers[128];
+ };
+
+/*
+ * Process wakeup reasons
+ */
+#define WK_NONE 0x00
+#define WK_WAKEUP 0x01
+#define WK_TIMEOUT 0x02
+#define WK_SIGNAL 0x04
+#define WK_SLEEP 0x08
+
+#define OPEN_READ 1
+#define OPEN_WRITE 2
+#define OPEN_READWRITE 3
+
+#include "os.h"
+#include "sound_calls.h"
+#include "dev_table.h"
+
+#ifndef DEB
+#define DEB(x)
+
+#define TIMER_ARMED 121234
+#define TIMER_NOT_ARMED 1
+
+#define FUTURE_VERSION
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c
new file mode 100644
index 000000000000..fe73aea93e07
--- /dev/null
+++ b/sys/i386/isa/sound/sound_switch.c
@@ -0,0 +1,528 @@
+/*
+ * sound/sound_switch.c
+ *
+ * The system call switch
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+struct sbc_device
+ {
+ int usecount;
+ };
+
+static struct sbc_device sbc_devices[SND_NDEVS] =
+{
+ {0}};
+
+static int in_use = 0; /*
+
+
+ * * * * Total # of open device files
+ * (excluding * * * minor 0) */
+
+/*
+ * /dev/sndstatus -device
+ */
+static char *status_buf = NULL;
+static int status_len, status_ptr;
+static int status_busy = 0;
+
+static int
+put_status (char *s)
+{
+ int l;
+
+ for (l = 0; l < 256, s[l]; l++); /*
+ * l=strlen(s);
+ */
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], s, l);
+ status_len += l;
+
+ return 1;
+}
+
+static int
+put_status_int (unsigned int val, int radix)
+{
+ int l, v;
+
+ static char hx[] = "0123456789abcdef";
+ char buf[11];
+
+ if (!val)
+ return put_status ("0");
+
+ l = 0;
+ buf[10] = 0;
+
+ while (val)
+ {
+ v = val % radix;
+ val = val / radix;
+
+ buf[9 - l] = hx[v];
+ l++;
+ }
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], &buf[10 - l], l);
+ status_len += l;
+
+ return 1;
+}
+
+static void
+init_status (void)
+{
+ /*
+ * Write the status information to the status_buf and update status_len.
+ * There is a limit of 4000 bytes for the data.
+ */
+
+ int i;
+
+ status_ptr = 0;
+
+ put_status ("Sound Driver:" SOUND_VERSION_STRING
+ " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
+ SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
+ "\n");
+
+ if (!put_status ("Config options: "))
+ return;
+ if (!put_status_int (SELECTED_SOUND_OPTIONS, 16))
+ return;
+
+ if (!put_status ("\n\nInstalled drivers: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_drivers - 1); i++)
+ {
+ if (!put_status ("Type "))
+ return;
+ if (!put_status_int (sound_drivers[i].card_type, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (sound_drivers[i].name))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\n\nCard config: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_cards - 1); i++)
+ {
+ int drv;
+
+ if (!snd_installed_cards[i].enabled)
+ if (!put_status ("("))
+ return;
+
+ /*
+ * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return;
+ * if (!put_status (": ")) return;
+ */
+
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
+ if (!put_status (sound_drivers[drv].name))
+ return;
+
+ if (!put_status (" at 0x"))
+ return;
+ if (!put_status_int (snd_installed_cards[i].config.io_base, 16))
+ return;
+ if (!put_status (" irq "))
+ return;
+ if (!put_status_int (snd_installed_cards[i].config.irq, 10))
+ return;
+ if (!put_status (" drq "))
+ return;
+ if (!put_status_int (snd_installed_cards[i].config.dma, 10))
+ return;
+
+ if (!snd_installed_cards[i].enabled)
+ if (!put_status (")"))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nPCM devices:\n"))
+ return;
+
+ for (i = 0; i < num_audiodevs; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (audio_devs[i]->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nSynth devices:\n"))
+ return;
+
+ for (i = 0; i < num_synths; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (synth_devs[i]->info->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nMidi devices:\n"))
+ return;
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (midi_devs[i]->info.name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nMIDI Timers:\n"))
+ return;
+
+ for (i = 0; i < num_sound_timers; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (sound_timer_devs[i]->info.name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\n"))
+ return;
+ if (!put_status_int (num_mixers, 10))
+ return;
+ if (!put_status (" mixer(s) installed\n"))
+ return;
+}
+
+static int
+read_status (snd_rw_buf * buf, int count)
+{
+ /*
+ * Return at most 'count' bytes from the status_buf.
+ */
+ int l, c;
+
+ l = count;
+ c = status_len - status_ptr;
+
+ if (l > c)
+ l = c;
+ if (l <= 0)
+ return 0;
+
+ COPY_TO_USER (buf, 0, &status_buf[status_ptr], l);
+ status_ptr += l;
+
+ return l;
+}
+
+int
+sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return read_status (buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_read (dev, file, buf, count);
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_read (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_read (dev, file, buf, count);
+#endif
+
+#ifndef EXCLUDE_PSS
+ case SND_DEV_PSS:
+ return pss_read (dev, file, buf, count);
+#endif
+
+ default:
+ printk ("Sound: Undefined minor device %d\n", dev);
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+int
+sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_write (dev, file, buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_write (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_write (dev, file, buf, count);
+#endif
+
+#ifndef EXCLUDE_PSS
+ case SND_DEV_PSS:
+ return pss_write (dev, file, buf, count);
+#endif
+
+ default:
+ return RET_ERROR (EPERM);
+ }
+
+ return count;
+}
+
+int
+sound_open_sw (int dev, struct fileinfo *file)
+{
+ int retval;
+
+ DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
+
+ if ((dev >= SND_NDEVS) || (dev < 0))
+ {
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_busy)
+ return RET_ERROR (EBUSY);
+ status_busy = 1;
+ if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL)
+ return RET_ERROR (EIO);
+ status_len = status_ptr = 0;
+ init_status ();
+ break;
+
+ case SND_DEV_CTL:
+ return 0;
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ if ((retval = sequencer_open (dev, file)) < 0)
+ return retval;
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ if ((retval = MIDIbuf_open (dev, file)) < 0)
+ return retval;
+ break;
+#endif
+
+#ifndef EXCLUDE_PSS
+ case SND_DEV_PSS:
+ if ((retval = pss_open (dev, file)) < 0)
+ return retval;
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ if ((retval = audio_open (dev, file)) < 0)
+ return retval;
+ break;
+
+ default:
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ sbc_devices[dev].usecount++;
+ in_use++;
+
+ return 0;
+}
+
+void
+sound_release_sw (int dev, struct fileinfo *file)
+{
+
+ DEB (printk ("sound_release_sw(dev=%d)\n", dev));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_buf)
+ KERNEL_FREE (status_buf);
+ status_buf = NULL;
+ status_busy = 0;
+ break;
+
+ case SND_DEV_CTL:
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ sequencer_release (dev, file);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ MIDIbuf_release (dev, file);
+ break;
+#endif
+
+#ifndef EXCLUDE_PSS
+ case SND_DEV_PSS:
+ pss_release (dev, file);
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ audio_release (dev, file);
+ break;
+
+ default:
+ printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
+ }
+
+ sbc_devices[dev].usecount--;
+ in_use--;
+}
+
+int
+sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg)
+{
+ DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ if ((dev & 0x0f) != SND_DEV_CTL && num_mixers > 0)
+ if ((cmd >> 8) & 0xff == 'M') /*
+ * Mixer ioctl
+ */
+ return mixer_devs[0]->ioctl (0, cmd, arg);
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_CTL:
+
+ if (!num_mixers)
+ return RET_ERROR (ENXIO);
+
+ dev = dev >> 4;
+
+ if (dev >= num_mixers)
+ return RET_ERROR (ENXIO);
+
+ return mixer_devs[dev]->ioctl (dev, cmd, arg);
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_ioctl (dev, file, cmd, arg);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_ioctl (dev, file, cmd, arg);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_ioctl (dev, file, cmd, arg);
+ break;
+#endif
+
+#ifndef EXCLUDE_PSS
+ case SND_DEV_PSS:
+ return pss_ioctl (dev, file, cmd, arg);
+ break;
+#endif
+
+ default:
+ return RET_ERROR (EPERM);
+ break;
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sound_timer.c b/sys/i386/isa/sound/sound_timer.c
new file mode 100644
index 000000000000..c6544c82a7ee
--- /dev/null
+++ b/sys/i386/isa/sound/sound_timer.c
@@ -0,0 +1,406 @@
+/*
+ * sound/sound_timer.c
+ *
+ * Timer for the level 2 interface of the /dev/sequencer. Uses the
+ * 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS.
+ *
+ * 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.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812)))
+
+static volatile int initialized = 0, opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+static volatile int select_addr, data_addr;
+static volatile int curr_timer = 0;
+static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
+
+
+static void
+timer_command (unsigned int addr, unsigned int val)
+{
+ int i;
+
+ OUTB ((unsigned char) (addr & 0xff), select_addr);
+
+ for (i = 0; i < 2; i++)
+ INB (select_addr);
+
+ OUTB ((unsigned char) (val & 0xff), data_addr);
+
+ for (i = 0; i < 2; i++)
+ INB (select_addr);
+}
+
+static void
+arm_timer (int timer, unsigned int interval)
+{
+
+ curr_timer = timer;
+
+ if (timer == 1)
+ {
+ gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */
+ gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */
+ timer_command (0x04, 0x01); /* Start timer 1 */
+ }
+ else
+ {
+ gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */
+ gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */
+ timer_command (0x04, 0x02); /* Start timer 2 */
+ }
+}
+
+static unsigned long
+tmr2ticks (int tmr_value)
+{
+ /*
+ * Convert timer ticks to MIDI ticks
+ */
+
+ unsigned long tmp;
+ unsigned long scale;
+
+ tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
+
+ scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
+
+ return (tmp + (scale / 2)) / scale;
+}
+
+static void
+reprogram_timer (void)
+{
+ unsigned long usecs_per_tick;
+ int timer_no, resolution;
+ int divisor;
+
+ usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+
+ /*
+ * Don't kill the system by setting too high timer rate
+ */
+ if (usecs_per_tick < 2000)
+ usecs_per_tick = 2000;
+
+ if (usecs_per_tick > (256 * 80))
+ {
+ timer_no = 2;
+ resolution = 320; /* usec */
+ }
+ else
+ {
+ timer_no = 1;
+ resolution = 80; /* usec */
+ }
+
+ divisor = (usecs_per_tick + (resolution / 2)) / resolution;
+ usecs_per_tmr = divisor * resolution;
+
+ arm_timer (timer_no, divisor);
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ RESTORE_INTR (flags);
+}
+
+static int
+timer_open (int dev, int mode)
+{
+ if (opened)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 60;
+ curr_timebase = HZ;
+ opened = 1;
+ reprogram_timer ();
+
+ return 0;
+}
+
+static void
+timer_close (int dev)
+{
+ opened = tmr_running = 0;
+ gus_write8 (0x45, 0); /* Disable both timers */
+}
+
+static int
+timer_event (int dev, unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ reprogram_timer ();
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ reprogram_timer ();
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ reprogram_timer ();
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+timer_get_time (int dev)
+{
+ if (!opened)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+timer_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return IOCTL_OUT (arg, TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ reprogram_timer ();
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+timer_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+static struct sound_timer_operations sound_timer =
+{
+ {"OPL-3/GUS Timer", 0},
+ 1, /* Priority */
+ 0, /* Local device link */
+ timer_open,
+ timer_close,
+ timer_event,
+ timer_get_time,
+ timer_ioctl,
+ timer_arm
+};
+
+void
+sound_timer_interrupt (void)
+{
+ gus_write8 (0x45, 0); /* Ack IRQ */
+ timer_command (4, 0x80); /* Reset IRQ flags */
+
+ if (!opened)
+ return;
+
+ if (curr_timer == 1)
+ gus_write8 (0x45, 0x04); /* Start timer 1 again */
+ else
+ gus_write8 (0x45, 0x08); /* Start timer 2 again */
+
+ if (!tmr_running)
+ return;
+
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+}
+
+void
+sound_timer_init (int io_base)
+{
+ int n;
+
+ if (initialized)
+ return; /* There is already a similar timer */
+
+ select_addr = io_base;
+ data_addr = io_base + 1;
+
+ initialized = 1;
+
+#if 1
+ if (num_sound_timers >= MAX_TIMER_DEV)
+ n = 0; /* Overwrite the system timer */
+ else
+ n = num_sound_timers++;
+#else
+ n = 0;
+#endif
+
+ sound_timer_devs[n] = &sound_timer;
+}
+
+#endif
+#endif
diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c
new file mode 100644
index 000000000000..b9e6ee5f1b70
--- /dev/null
+++ b/sys/i386/isa/sound/soundcard.c
@@ -0,0 +1,401 @@
+/*
+ * sound/386bsd/soundcard.c
+ *
+ * Soundcard driver for 386BSD.
+ *
+ * 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "dev_table.h"
+
+u_int snd1mask;
+u_int snd2mask;
+u_int snd3mask;
+u_int snd4mask;
+u_int snd5mask;
+u_int snd6mask;
+u_int snd7mask;
+u_int snd8mask;
+u_int snd9mask;
+
+#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
+
+static int timer_running = 0;
+
+static int soundcards_installed = 0; /* Number of installed
+ * soundcards */
+static int soundcard_configured = 0;
+
+static struct fileinfo files[SND_NDEVS];
+
+int sndprobe (struct isa_device *dev);
+int sndattach (struct isa_device *dev);
+int sndopen (dev_t dev, int flags);
+int sndclose (dev_t dev, int flags);
+int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode);
+int sndread (int dev, struct uio *uio);
+int sndwrite (int dev, struct uio *uio);
+int sndselect (int dev, int rw);
+static void sound_mem_init(void);
+
+unsigned long
+get_time(void)
+{
+ extern struct timeval time;
+ struct timeval timecopy;
+ int x;
+
+ x = splclock();
+ timecopy = time;
+ splx(x);
+ return timecopy.tv_usec/(1000000/HZ) +
+ (unsigned long)timecopy.tv_sec*HZ;
+}
+
+int
+sndread (int dev, struct uio *buf)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count));
+}
+
+int
+sndwrite (int dev, struct uio *buf)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count));
+}
+
+int
+sndopen (dev_t dev, int flags)
+{
+ int retval;
+
+ dev = minor (dev);
+
+ if (!soundcard_configured && dev)
+ {
+ printk ("SoundCard Error: The soundcard system has not been configured\n");
+ FIX_RETURN (-ENODEV);
+ }
+
+ files[dev].mode = 0;
+
+ if (flags & FREAD && flags & FWRITE)
+ files[dev].mode = OPEN_READWRITE;
+ else if (flags & FREAD)
+ files[dev].mode = OPEN_READ;
+ else if (flags & FWRITE)
+ files[dev].mode = OPEN_WRITE;
+
+ FIX_RETURN(sound_open_sw (dev, &files[dev]));
+}
+
+int
+sndclose (dev_t dev, int flags)
+{
+
+ dev = minor (dev);
+
+ sound_release_sw(dev, &files[dev]);
+ FIX_RETURN (0);
+}
+
+int
+sndioctl (dev_t dev, int cmd, caddr_t arg, int mode)
+{
+ dev = minor (dev);
+
+ FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg));
+}
+
+int
+sndselect (int dev, int rw)
+{
+ dev = minor (dev);
+
+ DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ FIX_RETURN (0);
+}
+
+static short
+ipri_to_irq (unsigned short ipri)
+{
+ /*
+ * Converts the ipri (bitmask) to the corresponding irq number
+ */
+ int irq;
+
+ for (irq = 0; irq < 16; irq++)
+ if (ipri == (1 << irq))
+ return irq;
+
+ return -1; /* Invalid argument */
+}
+
+int
+sndprobe (struct isa_device *dev)
+{
+ struct address_info hw_config;
+
+ hw_config.io_base = dev->id_iobase;
+ hw_config.irq = ipri_to_irq (dev->id_irq);
+ hw_config.dma = dev->id_drq;
+
+ return sndtable_probe (dev->id_unit, &hw_config);
+}
+
+int
+sndattach (struct isa_device *dev)
+{
+ int i;
+ static int midi_initialized = 0;
+ static int seq_initialized = 0;
+ static int generic_midi_initialized = 0;
+ unsigned long mem_start = 0xefffffff;
+ struct address_info hw_config;
+
+ hw_config.io_base = dev->id_iobase;
+ hw_config.irq = ipri_to_irq (dev->id_irq);
+ hw_config.dma = dev->id_drq;
+
+ if (dev->id_unit) /* Card init */
+ if (!sndtable_init_card (dev->id_unit, &hw_config))
+ {
+ printf (" <Driver not configured>");
+ return FALSE;
+ }
+
+ /*
+ * Init the high level sound driver
+ */
+
+ if (!(soundcards_installed = sndtable_get_cardcount ()))
+ {
+ printf (" <No such hardware>");
+ return FALSE; /* No cards detected */
+ }
+
+ printf("\n");
+
+#ifndef EXCLUDE_AUDIO
+ if (num_audiodevs) /* Audio devices present */
+ {
+ mem_start = DMAbuf_init (mem_start);
+ mem_start = audio_init (mem_start);
+ sound_mem_init ();
+ }
+
+ soundcard_configured = 1;
+#endif
+
+ if (num_midis && !midi_initialized)
+ {
+ midi_initialized = 1;
+ mem_start = MIDIbuf_init (mem_start);
+ }
+
+ if ((num_midis + num_synths) && !seq_initialized)
+ {
+ seq_initialized = 1;
+ mem_start = sequencer_init (mem_start);
+ }
+
+ return TRUE;
+}
+
+void
+tenmicrosec (void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ inb (0x80);
+}
+
+void
+request_sound_timer (int count)
+{
+ static int current = 0;
+ int tmp = count;
+
+ if (count < 0)
+ timeout (sequencer_timer, 0, -count);
+ else
+ {
+
+ if (count < current)
+ current = 0; /* Timer restarted */
+
+ count = count - current;
+
+ current = tmp;
+
+ if (!count)
+ count = 1;
+
+ timeout (sequencer_timer, 0, count);
+ }
+ timer_running = 1;
+}
+
+void
+sound_stop_timer (void)
+{
+ if (timer_running)
+ untimeout (sequencer_timer, 0);
+ timer_running = 0;
+}
+
+#ifndef EXCLUDE_AUDIO
+static void
+sound_mem_init (void)
+{
+ int i, dev;
+ unsigned long dma_pagesize;
+ static unsigned long dsp_init_mask = 0;
+
+ for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */
+ if (!(dsp_init_mask & (1 << dev))) /* Not already done */
+ if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0)
+ {
+ dsp_init_mask |= (1 << dev);
+
+ if (sound_dma_automode[dev])
+ {
+ sound_dma_automode[dev] = 0; /* Not possible with 386BSD */
+ }
+
+#if 0
+ if (sound_buffcounts[dev] == 1)
+ {
+ sound_buffcounts[dev] = 2;
+ sound_buffsizes[dev] /= 2;
+ }
+
+ if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */
+ sound_buffsizes[dev] = 65536;
+
+ if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
+ dma_pagesize = 131072; /* 128k */
+ else
+ dma_pagesize = 65536;
+
+ /* More sanity checks */
+
+ if (sound_buffsizes[dev] > dma_pagesize)
+ sound_buffsizes[dev] = dma_pagesize;
+ sound_buffsizes[dev] &= 0xfffff000; /* Truncate to n*4k */
+ if (sound_buffsizes[dev] < 4096)
+ sound_buffsizes[dev] = 4096;
+#else
+ dma_pagesize = 4096;
+ sound_buffsizes[dev] = 4096;
+ sound_buffcounts[dev] = 16; /* 16*4k -> 64k */
+#endif
+
+ /* Now allocate the buffers */
+
+ for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
+ {
+ /*
+ * The DMA buffer allocation algorithm hogs memory. We allocate
+ * a memory area which is two times the requires size. This
+ * guarantees that it contains at least one valid DMA buffer.
+ *
+ * This really needs some kind of finetuning.
+ */
+ char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT);
+ unsigned long addr, rounded, start, end;
+
+ if (tmpbuf == NULL)
+ {
+ printk ("snd: Unable to allocate %d bytes of buffer\n",
+ 2 * sound_buffsizes[dev]);
+ return;
+ }
+
+ addr = kvtop (tmpbuf);
+ /*
+ * Align the start address if required
+ */
+ start = (addr & ~(dma_pagesize - 1));
+ end = ((addr+sound_buffsizes[dev]-1) & ~(dma_pagesize - 1));
+
+ if (start != end)
+ rounded = end;
+ else
+ rounded = addr; /* Fits to the same DMA page */
+
+ snd_raw_buf[dev][snd_raw_count[dev]] =
+ &tmpbuf[rounded - addr]; /* Compute offset */
+ /*
+ * Use virtual address as the physical address, since
+ * isa_dmastart performs the phys address computation.
+ */
+ snd_raw_buf_phys[dev][snd_raw_count[dev]] =
+ (unsigned long) snd_raw_buf[dev][snd_raw_count[dev]];
+ }
+ } /* for dev */
+
+}
+
+#endif
+
+struct isa_driver snddriver =
+{sndprobe, sndattach, "snd"};
+
+int
+snd_ioctl_return (int *addr, int value)
+{
+ if (value < 0)
+ return value; /* Error */
+ suword (addr, value);
+ return 0;
+}
+
+int
+snd_set_irq_handler (int interrupt_level, void(*hndlr)(int))
+{
+ return 1;
+}
+
+void
+snd_release_irq(int vect)
+{
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sys_timer.c b/sys/i386/isa/sound/sys_timer.c
new file mode 100644
index 000000000000..1000045a42d5
--- /dev/null
+++ b/sys/i386/isa/sound/sys_timer.c
@@ -0,0 +1,304 @@
+/*
+ * sound/sys_timer.c
+ *
+ * The default timer for the Level 2 sequencer interface
+ * Uses the (100HZ) timer of kernel.
+ *
+ * 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.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static volatile int opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+
+static void poll_def_tmr (unsigned long dummy);
+
+DEFINE_TIMER (def_tmr, poll_def_tmr);
+
+static unsigned long
+tmr2ticks (int tmr_value)
+{
+ /*
+ * Convert system timer ticks (HZ) to MIDI ticks
+ */
+
+ unsigned long tmp;
+ unsigned long scale;
+
+ tmp = (tmr_value * 1000) / HZ;/* Convert to msecs */
+
+ scale = (60 * 1000) / (curr_tempo * curr_timebase); /* msecs per MIDI tick */
+
+ return (tmp + (scale / 2)) / scale;
+}
+
+static void
+poll_def_tmr (unsigned long dummy)
+{
+
+ if (opened)
+ {
+ ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
+
+ if (tmr_running)
+ {
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+ }
+ }
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ RESTORE_INTR (flags);
+}
+
+static int
+def_tmr_open (int dev, int mode)
+{
+ if (opened)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 60;
+ curr_timebase = HZ;
+ opened = 1;
+
+ ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
+
+ return 0;
+}
+
+static void
+def_tmr_close (int dev)
+{
+ opened = tmr_running = 0;
+}
+
+static int
+def_tmr_event (int dev, unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+def_tmr_get_time (int dev)
+{
+ if (!opened)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+def_tmr_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return IOCTL_OUT (arg, TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:;
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+def_tmr_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+struct sound_timer_operations default_sound_timer =
+{
+ {"System Timer", 0},
+ 0, /* Priority */
+ 0, /* Local device link */
+ def_tmr_open,
+ def_tmr_close,
+ def_tmr_event,
+ def_tmr_get_time,
+ def_tmr_ioctl,
+ def_tmr_arm
+};
+
+#endif
+#endif
diff --git a/sys/i386/isa/sound/tuning.h b/sys/i386/isa/sound/tuning.h
new file mode 100644
index 000000000000..858e1fe6c618
--- /dev/null
+++ b/sys/i386/isa/sound/tuning.h
@@ -0,0 +1,29 @@
+#ifdef SEQUENCER_C
+
+unsigned short semitone_tuning[24] =
+{
+/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
+/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
+/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+unsigned short cent_tuning[100] =
+{
+/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
+/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
+/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
+/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
+/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
+/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
+/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
+/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
+/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
+/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
+/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
+/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
+/* 96 */ 10570, 10576, 10582, 10589
+};
+#else
+extern unsigned short semitone_tuning[24];
+extern unsigned short cent_tuning[100];
+#endif
diff --git a/sys/i386/isa/sound/uart6850.c b/sys/i386/isa/sound/uart6850.c
new file mode 100644
index 000000000000..ca6313521270
--- /dev/null
+++ b/sys/i386/isa/sound/uart6850.c
@@ -0,0 +1,323 @@
+/*
+ * sound/uart6850.c
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
+ * added 6850 support, used with COVOX SoundMaster II and custom cards.
+ *
+ * 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 "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (uart6850_base) /*
+ * * * Midi6850 Data I/O Port on IBM
+ * */
+#define COMDPORT (uart6850_base+1) /*
+ * * * Midi6850 Command Port on IBM */
+#define STATPORT (uart6850_base+1) /*
+ * * * Midi6850 Status Port on IBM */
+
+#define uart6850_status() INB(STATPORT)
+#define input_avail() ((uart6850_status()&INPUT_AVAIL))
+#define output_ready() ((uart6850_status()&OUTPUT_READY))
+#define uart6850_cmd(cmd) OUTB(cmd, COMDPORT)
+#define uart6850_read() INB(DATAPORT)
+#define uart6850_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x02 /*
+ * * * Mask for Data Read Ready Bit */
+#define INPUT_AVAIL 0x01 /*
+ * * * Mask for Data Send Ready Bit */
+
+#define UART_RESET 0x95 /*
+ * * * 6850 Total Reset Command */
+#define UART_MODE_ON 0x03 /*
+ * * * 6850 Send/Receive UART Mode */
+
+static int uart6850_opened = 0;
+static int uart6850_base = 0x330;
+static int uart6850_irq;
+static int uart6850_detected = 0;
+static int my_dev;
+
+static int reset_uart6850 (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+uart6850_input_loop (void)
+{
+ int count;
+
+ count = 10;
+
+ while (count) /*
+ * Not timed out
+ */
+ if (input_avail ())
+ {
+ unsigned char c = uart6850_read ();
+
+ count = 100;
+
+ if (uart6850_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+ else
+ while (!input_avail () && count)
+ count--;
+}
+
+void
+m6850intr (int unit)
+{
+ printk ("M");
+ if (input_avail ())
+ uart6850_input_loop ();
+}
+
+/*
+ * It looks like there is no input interrupts in the UART mode. Let's try
+ * polling.
+ */
+
+static void
+poll_uart6850 (unsigned long dummy)
+{
+ unsigned long flags;
+
+ DEFINE_TIMER (uart6850_timer, poll_uart6850);
+
+ if (!(uart6850_opened & OPEN_READ))
+ return; /*
+ * No longer required
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ uart6850_input_loop ();
+
+ ACTIVATE_TIMER (uart6850_timer, poll_uart6850, 1); /*
+ * Come back later
+ */
+
+ RESTORE_INTR (flags);
+}
+
+static int
+uart6850_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (uart6850_opened)
+ {
+ printk ("Midi6850: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ uart6850_cmd (UART_RESET);
+
+ uart6850_input_loop ();
+
+ midi_input_intr = input;
+ uart6850_opened = mode;
+ poll_uart6850 (0); /*
+ * Enable input polling
+ */
+
+ return 0;
+}
+
+static void
+uart6850_close (int dev)
+{
+ uart6850_cmd (UART_MODE_ON);
+
+ uart6850_opened = 0;
+}
+
+static int
+uart6850_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ uart6850_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+
+ if (!output_ready ())
+ {
+ printk ("Midi6850: Timeout\n");
+ return 0;
+ }
+
+ uart6850_write (midi_byte);
+ return 1;
+}
+
+static int
+uart6850_command (int dev, unsigned char *midi_byte)
+{
+ return 1;
+}
+
+static int
+uart6850_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+uart6850_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+uart6850_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+uart6850_kick (int dev)
+{
+}
+
+static int
+uart6850_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+#define MIDI_SYNTH_NAME "6850 UART Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations uart6850_operations =
+{
+ {"6850 UART", 0, 0, SNDCARD_UART6850},
+ &std_midi_synth,
+ uart6850_open,
+ uart6850_close,
+ uart6850_ioctl,
+ uart6850_out,
+ uart6850_start_read,
+ uart6850_end_read,
+ uart6850_kick,
+ uart6850_command,
+ uart6850_buffer_status
+};
+
+
+long
+attach_uart6850 (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
+
+ if (!uart6850_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ uart6850_cmd (UART_MODE_ON);
+
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+ printk (" <6850 Midi Interface>");
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &uart6850_operations;
+ return mem_start;
+}
+
+static int
+reset_uart6850 (void)
+{
+ uart6850_read ();
+ return 1; /*
+ * OK
+ */
+}
+
+
+int
+probe_uart6850 (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
+
+ if (snd_set_irq_handler (uart6850_irq, m6850intr) < 0)
+ return 0;
+
+ ok = reset_uart6850 ();
+
+ uart6850_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/ulaw.h b/sys/i386/isa/sound/ulaw.h
new file mode 100644
index 000000000000..be9f92d9984a
--- /dev/null
+++ b/sys/i386/isa/sound/ulaw.h
@@ -0,0 +1,69 @@
+static unsigned char ulaw_dsp[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 5, 9, 13, 17, 21, 25, 29, 33,
+ 37, 41, 45, 49, 53, 57, 61, 65,
+ 68, 70, 72, 74, 76, 78, 80, 82,
+ 84, 86, 88, 90, 92, 94, 96, 98,
+ 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115,
+ 115, 116, 116, 117, 117, 118, 118, 119,
+ 119, 120, 120, 121, 121, 122, 122, 123,
+ 123, 123, 124, 124, 124, 124, 125, 125,
+ 125, 125, 126, 126, 126, 126, 127, 127,
+ 127, 127, 127, 127, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 252, 248, 244, 240, 236, 232, 228, 224,
+ 220, 216, 212, 208, 204, 200, 196, 192,
+ 189, 187, 185, 183, 181, 179, 177, 175,
+ 173, 171, 169, 167, 165, 163, 161, 159,
+ 157, 156, 155, 154, 153, 152, 151, 150,
+ 149, 148, 147, 146, 145, 144, 143, 142,
+ 142, 141, 141, 140, 140, 139, 139, 138,
+ 138, 137, 137, 136, 136, 135, 135, 134,
+ 134, 134, 133, 133, 133, 133, 132, 132,
+ 132, 132, 131, 131, 131, 131, 130, 130,
+ 130, 130, 130, 130, 129, 129, 129, 129,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+
+static unsigned char dsp_ulaw[] = {
+ 31, 31, 31, 32, 32, 32, 32, 33,
+ 33, 33, 33, 34, 34, 34, 34, 35,
+ 35, 35, 35, 36, 36, 36, 36, 37,
+ 37, 37, 37, 38, 38, 38, 38, 39,
+ 39, 39, 39, 40, 40, 40, 40, 41,
+ 41, 41, 41, 42, 42, 42, 42, 43,
+ 43, 43, 43, 44, 44, 44, 44, 45,
+ 45, 45, 45, 46, 46, 46, 46, 47,
+ 47, 47, 47, 48, 48, 49, 49, 50,
+ 50, 51, 51, 52, 52, 53, 53, 54,
+ 54, 55, 55, 56, 56, 57, 57, 58,
+ 58, 59, 59, 60, 60, 61, 61, 62,
+ 62, 63, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 81, 83, 85, 87, 89,
+ 91, 93, 95, 99, 103, 107, 111, 119,
+ 255, 247, 239, 235, 231, 227, 223, 221,
+ 219, 217, 215, 213, 211, 209, 207, 206,
+ 205, 204, 203, 202, 201, 200, 199, 198,
+ 197, 196, 195, 194, 193, 192, 191, 191,
+ 190, 190, 189, 189, 188, 188, 187, 187,
+ 186, 186, 185, 185, 184, 184, 183, 183,
+ 182, 182, 181, 181, 180, 180, 179, 179,
+ 178, 178, 177, 177, 176, 176, 175, 175,
+ 175, 175, 174, 174, 174, 174, 173, 173,
+ 173, 173, 172, 172, 172, 172, 171, 171,
+ 171, 171, 170, 170, 170, 170, 169, 169,
+ 169, 169, 168, 168, 168, 168, 167, 167,
+ 167, 167, 166, 166, 166, 166, 165, 165,
+ 165, 165, 164, 164, 164, 164, 163, 163,
+ 163, 163, 162, 162, 162, 162, 161, 161,
+ 161, 161, 160, 160, 160, 160, 159, 159,
+};