summaryrefslogtreecommitdiffstats
path: root/sys/i386
diff options
context:
space:
mode:
authorjkh <jkh@FreeBSD.org>1993-10-23 10:55:52 +0000
committerjkh <jkh@FreeBSD.org>1993-10-23 10:55:52 +0000
commit6fff94e5f07f0d167d9ddbd386e366596f36081a (patch)
tree02792126c665934c13fb381bfaf87335b0633267 /sys/i386
parent47553b3ec63c85480e289f6f8157885799107ee6 (diff)
downloadFreeBSD-src-6fff94e5f07f0d167d9ddbd386e366596f36081a.zip
FreeBSD-src-6fff94e5f07f0d167d9ddbd386e366596f36081a.tar.gz
This is the Linux generic soundcard driver, version 1.0c. Supports
SBlaster/Adlib/ProAudio Spectrum/Gravis/etc cards. This is a BETA test driver, please test it and get back to me!
Diffstat (limited to 'sys/i386')
-rw-r--r--sys/i386/isa/sound/COPYING27
-rw-r--r--sys/i386/isa/sound/HOWTO_MIDI51
-rw-r--r--sys/i386/isa/sound/README17
-rw-r--r--sys/i386/isa/sound/RELNOTES38
-rw-r--r--sys/i386/isa/sound/RELNOTES.Linux191
-rw-r--r--sys/i386/isa/sound/adlib_card.c32
-rw-r--r--sys/i386/isa/sound/audio.c278
-rw-r--r--sys/i386/isa/sound/dev_table.c83
-rw-r--r--sys/i386/isa/sound/dev_table.h229
-rw-r--r--sys/i386/isa/sound/dmabuf.c773
-rw-r--r--sys/i386/isa/sound/finetune.h28
-rw-r--r--sys/i386/isa/sound/gus_card.c183
-rw-r--r--sys/i386/isa/sound/gus_hw.h35
-rw-r--r--sys/i386/isa/sound/gus_midi.c257
-rw-r--r--sys/i386/isa/sound/gus_vol.c101
-rw-r--r--sys/i386/isa/sound/gus_wave.c2523
-rw-r--r--sys/i386/isa/sound/gustest/Makefile16
-rw-r--r--sys/i386/isa/sound/gustest/Readme67
-rw-r--r--sys/i386/isa/sound/gustest/gmidi.h131
-rw-r--r--sys/i386/isa/sound/gustest/gmod.c1589
-rw-r--r--sys/i386/isa/sound/gustest/gpatinfo.c176
-rw-r--r--sys/i386/isa/sound/gustest/gusload.c350
-rw-r--r--sys/i386/isa/sound/gustest/midithru.c325
-rw-r--r--sys/i386/isa/sound/gustest/pmtest.c411
-rw-r--r--sys/i386/isa/sound/local.h17
-rw-r--r--sys/i386/isa/sound/midi.c176
-rw-r--r--sys/i386/isa/sound/midibuf.c105
-rw-r--r--sys/i386/isa/sound/mpu401.c332
-rw-r--r--sys/i386/isa/sound/opl3.c913
-rw-r--r--sys/i386/isa/sound/opl3.h263
-rw-r--r--sys/i386/isa/sound/os.h273
-rw-r--r--sys/i386/isa/sound/pas.h249
-rw-r--r--sys/i386/isa/sound/pas2_card.c343
-rw-r--r--sys/i386/isa/sound/pas2_midi.c269
-rw-r--r--sys/i386/isa/sound/pas2_mixer.c481
-rw-r--r--sys/i386/isa/sound/pas2_pcm.c412
-rw-r--r--sys/i386/isa/sound/patmgr.c239
-rw-r--r--sys/i386/isa/sound/pro_midi.c155
-rw-r--r--sys/i386/isa/sound/sb_card.c33
-rw-r--r--sys/i386/isa/sound/sb_dsp.c1303
-rw-r--r--sys/i386/isa/sound/sequencer.c1137
-rw-r--r--sys/i386/isa/sound/sound_calls.h183
-rw-r--r--sys/i386/isa/sound/sound_config.h182
-rw-r--r--sys/i386/isa/sound/soundcard.c593
-rw-r--r--sys/i386/isa/sound/tuning.h29
-rw-r--r--sys/i386/isa/sound/ulaw.h69
46 files changed, 15667 insertions, 0 deletions
diff --git a/sys/i386/isa/sound/COPYING b/sys/i386/isa/sound/COPYING
new file mode 100644
index 0000000..be7703a
--- /dev/null
+++ b/sys/i386/isa/sound/COPYING
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ * 3. Unmodified version of this file (COPYING) must be provided with
+ * any source or binary distributions.
+ *
+ * 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/HOWTO_MIDI b/sys/i386/isa/sound/HOWTO_MIDI
new file mode 100644
index 0000000..f0601e5
--- /dev/null
+++ b/sys/i386/isa/sound/HOWTO_MIDI
@@ -0,0 +1,51 @@
+The following file describes the procedure for adding modules to MIDI
+Please READ the main documentation files for the driver first!!!
+
+
+ Example: We have a sound card with a MIDI chip & port on it
+ and, we call it the 'MYBLASTER' card:
+
+ **************************************************************************
+
+ 0: Run 'configure'. Select the MIDI on CHIP support option.
+
+ 1: Write a midi driver module; 'blast_midi.c'
+ (with functions for open,close,read,write,attach.)
+
+ 1a: Write all functions except the 'attach' the way you want.
+
+ For the 'attach' function, look at a model in the 'pro_midi.c'
+ file. Just dup it in the same fashion. For the 'generic_midi_operations'
+ structure which is required, see file 'dev_table.h'.
+
+ 2: We called the 'attach' function: 'blast_attach'.
+
+ Go to the file 'dev_table.h' and add your driver name and the function
+ pointer ( which is 'blast_attach' ) to the 'midi_supported' table.
+
+ 3: You are almost set. Go and make a reference
+ to an 'exclude constant'; say EXLCUDE_BLAST_MIDI in your module
+ (refer to the 'pro_midi.c' file for model). Also make sure to
+ add the constant to the file 'sound_config.h' (for example, look
+ where the constant EXCLUDE_PRO_MIDI is in the file.)
+
+ 4: Add the line
+
+ #define ALL_EXTERNAL_TO_ME
+
+ as the 1st line of your 'blast_midi.c' file. This happily, makes
+ you ignorant of everything else specific to the driver! :).
+
+ 4a: And of course, don't forget to make a device :). Note that your
+ minor number should be = ( 15 + position of your driver in the
+ 'midi_supported' table in the 'dev_table.h' file ).
+
+ Eg: Your driver is the second one in the table. viz midi_supported[1].
+ Your device minor number should be ( 15 + 1 = 16 ). Then, make the
+ reference to your device as, say CMIDI_DEV_BLAST in the file
+ 'sound_config.h'. Also add this in 'soundcard.c' for the open, read,
+ write and close routines. See files for example using CMIDI_DEV_PRO
+ (which is the ProAudioSpectrum on chip MIDI).
+
+ 5: You are all set. If ever you have problems, follow the same model
+ as the file 'pro_midi.c', except for substituting your own functions!
diff --git a/sys/i386/isa/sound/README b/sys/i386/isa/sound/README
new file mode 100644
index 0000000..efb0b11
--- /dev/null
+++ b/sys/i386/isa/sound/README
@@ -0,0 +1,17 @@
+CAUTION!
+
+This is a prototype version of the Linux Sound Driver for FreeBSD.
+
+The official and supported version is 1.0c.
+
+This version 'should work' but there may be some bugs and the programmers
+API may change before the final version.
+
+There are some additional programs for GUS owners in the
+gustest subdirectory of this directory, namely a module
+(.MOD, .STM and .669) player and a patch file loader.
+Additionally, there is a midithru program which allows
+you to play the synth on the soundcard with a midi keyboard
+(also usable for OPL-3 owners).
+
+Hannu & FreeBSD team.
diff --git a/sys/i386/isa/sound/RELNOTES b/sys/i386/isa/sound/RELNOTES
new file mode 100644
index 0000000..03d492d
--- /dev/null
+++ b/sys/i386/isa/sound/RELNOTES
@@ -0,0 +1,38 @@
+Welcome to use the Linux sound driver for FreeBSD. This
+driver supports the SoundBlaster, SB Pro, Pro Audio Spectrum 16,
+AdLib and Gravis UltraSound sound cards.
+
+In addition there is rather limited support for MPU-401
+(and compatible) midi cards. Also, the OPL-3 synthesizer
+of the SB Pro and PAS16 cards is now supported in the 4 OP
+modes.
+
+Most of the features of the /dev/sequencer device file are
+available just for GUS owners.
+
+The SoundBlaster 16 and SB 16 ASP cards are not supported,
+though they may work in mono mode with speeds < 22 kHz.
+The OPL-3 chicp of the SB 16 should work (without problems?).
+Is there anybody willing to implement the SB 16 support
+(have the SB 16 and the SDK for it)?
+
+Since this driver is a sound driver, it does not contain support
+for SCSI/CD-ROM/Joystick -devices.
+
+Known bugs
+----------
+
+- It's not possible to open /dev/dsp (or /dev/audio) while the
+ /dev/sequencer is open for output and GUS is the only soundcard
+ installed. It's possible if /dev/dsp is opened before /dev/sequencer
+ but at this time the GUS is not available for access via /dev/sequencer.
+ This is a limitation of the driver.
+- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
+ It uses by default the I/O port 0x330, which is also used by the
+ Adaptec 1542 SCSI adapter.
+- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
+ ^C and playing again should solve this problem. This is probably caused by
+ incompatibilities between the GUS and certain VLB motherboards. Try to avoid
+ switching between VTs while patches are being loaded to the GUS.
+- There is a skeleton of the patch manager support. It doesn't work in
+ this version.
diff --git a/sys/i386/isa/sound/RELNOTES.Linux b/sys/i386/isa/sound/RELNOTES.Linux
new file mode 100644
index 0000000..4082f4e
--- /dev/null
+++ b/sys/i386/isa/sound/RELNOTES.Linux
@@ -0,0 +1,191 @@
+Release notes for the Linux Sound Driver 1.99.9
+-----------------------------------------------
+
+******** THIS IS A BETA TEST RELEASE ********
+which means that there can be some untested things. In theory
+there is a risk that this driver causes some trouble to your system.
+You should not use this driver before backing up your disks.
+
+
+
+
+Welcome to use the Gravis UltraSound driver for Linux. This
+driver still supports the same cards than version 1.0c
+(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib).
+In addition there is rather limited support for MPU-401
+(and compatible) midi cards. Also the OPL-3 synthesizer
+of the SB Pro and PAS16 cards is now supported in the 4 OP
+modes.
+Most of the features of the /dev/sequencer device file are
+available just for GUS owners.
+
+The SoundBlaster 16 and SB 16 ASP cards are not supported.
+They could work in mono mode with speeds < 22 kHz.
+The OPL-3 chicp of the SB 16 should work (without problems?).
+Is there anybody willing to implement the SB 16 support
+(have the SB 16 and the SDK for it)?
+
+
+This is the first version of the driver which has almost
+all of the features which I have planned to include into
+version 2.0. Some features are still missing and some ones
+doesn't work.
+
+NOTE! There are separate driver for CD-ROMS supported by
+ some soundcards. The driver for CDU31A (Fusion 16) is
+ called cdu31a-0.6.diff.z. It will be contained in the
+ Linux version 0.99.12. The driver for the CD-ROM of SB Pro
+ is sbpcd0.4.tar.gz (these were the latest versions when I wrote
+ this). These files should be at least at sunsite.unc.edu.
+ As far as I know, there is no driver for the SCSI interface of PAS16
+ (yet).
+
+ There is also a driver for joystick. Look for file joystick-0.5.tar.gz
+ (sunsite).
+
+ Since this driver is a sound driver, it will not contain support
+ for SCSI/CD-ROM/Joystick -devices.
+
+Compatibility with the earlier versions
+---------------------------------------
+
+This is just like the version 1.99.7/1.99.8. There is just some minor
+enhancements. Most of them are portability fixes. If you are porting
+this driver to any OS, please look at the 386bsd/os.h. There is some
+new macros and some macros have more parameters. In addition this file
+contains some usefull comments.
+
+**** There is some ISC and 386bsd stuff in this driver. Please stay away ****
+This stuff is here just because I want to be in sync with the porters. These
+ports don't work yet.
+
+The ioctl() interface has changed completely since version 1.0c. All
+programs using this driver must be at least recompiled.
+The snd-util-1.99.6 package contains some utilities for this version.
+
+The version 1.0c and earlier used a 'nonportable' ioctl calling scheme
+where the input argument was passed by value and the output value was
+returned as the functional return. For example setting the speed of
+/dev/dsp were done as the following:
+
+ int actual_speed;
+ actual_speed = ioctl(fd, SOUND_PCM_WRITE_RATE, 44100);
+
+After version 1.99.0 this must be done as the following:
+
+ int actual_speed = 44100;
+ ioctl(fd, SOUND_PCM_WRITE_RATE, &actual_speed);
+
+If you have an application written for the version 1.0, you should search
+for the strings SNDCTL_ and SOUND_ and to check the parameters.
+
+Since the this version will support more than one synthesizer devices
+at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition
+there is some new fields which must be initialized. Look at the sbiset.c in
+the snd-util-1.99.6 package for further info.
+
+The GUS patch format has changed since the version 1.99.3. You have to
+use latest versions of the programs in the sound/gustest directory. In
+addition the version 0.4g of the Adagio package supports this format.
+
+New features
+------------
+
+There is also some changes which make this version more usable than
+the version 1.0c.
+
+- /dev/dsp and /dev/audio
+
+The DMA buffering is now little bit more intelligent than earlier. The
+buffer size is selected run-time so that a buffer holds data for 0.5 to
+1.0 seconds of recording or playback. This makes recording more comfortable
+than with version 1.0. With the previous version there was sometimes more
+than 10 seconds of delay before the driver returned the first input byte.
+
+There is also support for more than one digitized voice devices. The device
+files /dev/dsp1 and /dev/audio1 (minor 19 and 20) are available with PAS16.
+The /dev/dsp (/dev/audio) is connected to the PCM circuit of the PAS16 itself
+and the /dev/dsp1 (/dev/audio1) to the SB emulation of PAS16 card. Two
+dsp/audio devices are available also if you have combination of SB and GUS.
+With GUS and PAS16 you will have even three dsp/audio devices. These devices
+can be used independently and can be active at the same time (3 channels
+at the same time propably don't work).
+
+The dsp/audio support of PAS16 should be much cleaner now since the
+constant clicking sound between the DMA blocks (about once per second) has
+been eliminated.
+
+The stereo playback of GUS doesn't work perfectly. There is lot of
+clicking in the output.
+
+- /dev/mixer
+
+No changes.
+
+There is no mixer for the GUS yet.
+
+- /dev/sequencer
+
+This part has the most changes. Mostly to support the rich
+features of the Gravis UltraSound. There is also the support
+for the OPL-3 synthesizer chip.
+
+- /dev/sndstat
+
+This is a new devicefile for debugging purposes. A better place for
+it is in the /proc -directory but I was just too lazy to implement it
+properly. The /dev/sndstat (major 14, minor 6) is a file which returns
+info about the current configuration (see the example below). If you
+send me a error/problem report, please include a printout from this
+device to your message (cat /dev/sndstat).
+
+------ cut here --- cat /dev/sndstat example --------
+Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi)
+Config options: 0x00000d4b
+
+Major number: 14
+HW config:
+Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6
+Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3
+Type 2: SoundBlaster at 0x220 irq 7 drq 1
+Type 1: AdLib at 0x388 irq 0 drq 0
+
+PCM devices:
+00: Gravis UltraSound
+01: Pro Audio Spectrum
+02: SoundBlaster 2.0
+
+Synth devices:
+00: Gravis UltraSound
+01: Yamaha OPL-3
+
+Midi devices:
+00: Gravis UltraSound
+01: Pro Audio Spectrum
+
+Mixer(s) installed
+------ cut here ---- End of Example -----------
+
+
+Known bugs
+----------
+
+- There was clicking during stereo playback to /dev/dsp with GUS.
+ * Fixed in 1.99.9 *
+- It's not possible to open /dev/dsp (or /dev/audio) while the
+ /dev/sequencer is open for output and GUS is the only soundcard
+ installed. It's possible if /dev/dsp is opened before /dev/sequencer
+ but at this time the GUS is not available for access via /dev/sequencer.
+ This is a limitation of the driver.
+- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
+ It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI
+ adapter.
+- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
+ ^C and playing again should solve this problem. This is propably caused by
+ incompatibilities between GUS and certain VLB motherboards. Try to avoid
+ switching between VTs while patches are being loaded to the GUS.
+- There was some problems with GUS and Mitsumi CD in version 1.99.8. Fixed
+ in 1.99.9.
+- /dev/audio sounded like stereo with GUS. Fixed in 1.99.9.
+- There is a skeleton of the patch manager support. It don't work in
+ this version.
diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c
new file mode 100644
index 0000000..a9cf568
--- /dev/null
+++ b/sys/i386/isa/sound/adlib_card.c
@@ -0,0 +1,32 @@
+
+/*
+ * linux/kernel/chr_drv/sound/adlib_card.c
+ *
+ * Detection routine for the AdLib card.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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 0000000..e35366e
--- /dev/null
+++ b/sys/i386/isa/sound/audio.c
@@ -0,0 +1,278 @@
+/*
+ * linux/kernel/chr_drv/sound/audio.c
+ *
+ * Device file manager for /dev/audio
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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_DSP_DEV]; /* != -1, if there is a
+ * incomplete output block */
+static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV];
+static char *wr_dma_buf[MAX_DSP_DEV];
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ int mode;
+ int ret;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if ((ret = DMAbuf_open (dev, mode)) < 0)
+ return ret;
+
+ wr_buff_no[dev] = -1;
+ 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 (!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]);
+
+ COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
+
+ /* Insert local processing here */
+
+#ifdef linux
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
+#endif
+ translate_bytes (ulaw_dsp, &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;
+
+ while (c)
+ {
+ if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
+ return buff_no;
+
+ if (l > c)
+ l = c;
+
+ /* Insert any local processing here. */
+#ifdef linux
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
+#endif
+
+ translate_bytes (dsp_ulaw, 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;
+
+ default:
+#if 1
+ return RET_ERROR (EIO);
+#else
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+#endif
+ }
+}
+
+long
+audio_init (long mem_start)
+{
+ 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/dev_table.c b/sys/i386/isa/sound/dev_table.c
new file mode 100644
index 0000000..96765cd8d4
--- /dev/null
+++ b/sys/i386/isa/sound/dev_table.c
@@ -0,0 +1,83 @@
+/*
+ * linux/kernel/chr_drv/sound/dev_table.c
+ *
+ * Device call tables.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#define _DEV_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+long
+sndtable_init (long mem_start)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ for (i = 0; i < (n - 1); i++)
+ if (supported_drivers[i].probe (&supported_drivers[i].config))
+ {
+#ifndef SHORT_BANNERS
+ printk ("snd%d",
+ supported_drivers[i].card_type);
+#endif
+
+ mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
+#ifndef SHORT_BANNERS
+ printk (" at 0x%03x irq %d drq %d\n",
+ supported_drivers[i].config.io_base,
+ supported_drivers[i].config.irq,
+ supported_drivers[i].config.dma);
+#endif
+ }
+ return mem_start;
+}
+
+int
+sndtable_probe (int unit, struct address_info *hw_config)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ if (!unit)
+ return TRUE;
+
+ for (i = 0; i < (n - 1); i++)
+ if (supported_drivers[i].card_type == unit)
+ return supported_drivers[i].probe (hw_config);
+
+ return FALSE;
+}
+
+int
+sndtable_init_card (int unit, struct address_info *hw_config)
+{
+ int i, n = sizeof (supported_drivers) / 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 (supported_drivers[i].card_type == unit)
+ {
+ if (supported_drivers[i].attach (0, hw_config) != 0)
+ panic ("snd#: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int
+sndtable_get_cardcount (void)
+{
+ return num_dspdevs + num_mixers + num_synths + num_midis;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h
new file mode 100644
index 0000000..64482ce
--- /dev/null
+++ b/sys/i386/isa/sound/dev_table.h
@@ -0,0 +1,229 @@
+/*
+ linux/kernel/chr_drv/sound/dev_table.h
+
+ Global definitions for device call tables
+
+ (C) Hannu Savolainen 1992
+ See COPYING for further details. Should be distributed with this file.
+
+*/
+
+#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 card_info {
+ int card_type; /* From soundcard.c */
+ char *name;
+ long (*attach) (long mem_start, struct address_info *hw_config);
+ int (*probe) (struct address_info *hw_config);
+ struct address_info config;
+};
+
+/** UWM -- new MIDI structure here.. **/
+
+struct generic_midi_info{
+ char *name; /* Name of the MIDI device.. */
+ long (*attach) (long mem_start);
+};
+
+struct audio_operations {
+ char name[32];
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ void (*output_block) (int dev, unsigned long buf, int count, int intrflag);
+ void (*start_input) (int dev, unsigned long buf, int count, int intrflag);
+ 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 (*has_output_drained)(int dev);
+ void (*copy_from_user)(int dev, char *localbuf, int localoffs,
+ snd_rw_buf *userbuf, int useroffs, int len);
+};
+
+struct mixer_operations {
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+};
+
+struct synth_operations {
+ struct synth_info *info;
+ 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 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);
+ int (*pmgr_interface) (int dev, struct patmgr_info *info);
+};
+
+struct midi_operations {
+ struct midi_info info;
+ int (*open) (int dev, int mode);
+ 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);
+};
+
+/** UWM -- new structure for MIDI **/
+
+struct generic_midi_operations {
+ struct midi_info info;
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ int (*write) (int dev, struct uio *data);
+ int (*read) (int dev, struct uio *data);
+};
+
+#ifndef ALL_EXTERNAL_TO_ME
+
+#ifdef _MIDI_TABLE_C_
+
+/** UWM **/
+ struct generic_midi_operations * generic_midi_devs[MAX_MIDI_DEV] = {NULL};
+ int num_generic_midis = 0, pro_midi_dev = 0;
+
+ struct generic_midi_info midi_supported[] = {
+
+#ifndef EXCLUDE_PRO_MIDI
+ {"ProAudioSpectrum MV101",pro_midi_attach}
+#endif
+ };
+
+ int num_midi_drivers =
+ sizeof (midi_supported) / sizeof(struct generic_midi_info);
+
+#endif
+
+
+#ifdef _DEV_TABLE_C_
+ struct audio_operations * dsp_devs[MAX_DSP_DEV] = {NULL}; int num_dspdevs = 0;
+ struct mixer_operations * mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
+ struct synth_operations * synth_devs[MAX_SYNTH_DEV] = {NULL}; int num_synths = 0;
+ struct midi_operations * midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
+
+
+# ifndef EXCLUDE_MPU401
+ int mpu401_dev = 0;
+# endif
+
+/*
+ * Note! The detection order is significant. Don't change it.
+ */
+
+ struct card_info supported_drivers[] = {
+#ifndef EXCLUDE_MPU401
+ {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401,
+ {MPU_BASE, MPU_IRQ, 0}},
+#endif
+
+#ifndef EXCLUDE_GUS
+ {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus,
+ {GUS_BASE, GUS_IRQ, GUS_DMA}},
+#endif
+
+#ifndef EXCLUDE_PAS
+ {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas,
+ {PAS_BASE, PAS_IRQ, PAS_DMA}},
+#endif
+
+#ifndef EXCLUDE_SB
+ {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb,
+ {SBC_BASE, SBC_IRQ, SBC_DMA}},
+#endif
+
+#ifndef EXCLUDE_YM3812
+ {SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib,
+ {FM_MONO, 0, 0}},
+#endif
+ {0, "*?*", NULL}
+ };
+
+ int num_sound_drivers =
+ sizeof(supported_drivers) / sizeof (struct card_info);
+
+
+# ifndef EXCLUDE_AUDIO
+ int sound_buffcounts[MAX_DSP_DEV] = {0};
+ long sound_buffsizes[MAX_DSP_DEV] = {0};
+ int sound_dsp_dmachan[MAX_DSP_DEV] = {0};
+ int sound_dma_automode[MAX_DSP_DEV] = {0};
+# endif
+#else
+ extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
+ extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+ extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
+ extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+# ifndef EXCLUDE_MPU401
+ extern int mpu401_dev;
+# endif
+
+ extern struct card_info supported_drivers[];
+ extern int num_sound_drivers;
+
+# ifndef EXCLUDE_AUDIO
+ extern int sound_buffcounts[MAX_DSP_DEV];
+ extern long sound_buffsizes[MAX_DSP_DEV];
+ extern int sound_dsp_dmachan[MAX_DSP_DEV];
+ extern int sound_dma_automode[MAX_DSP_DEV];
+# endif
+
+#endif
+
+long sndtable_init(long mem_start);
+int sndtable_get_cardcount (void);
+long CMIDI_init(long mem_start); /* */
+#endif
+
+#endif
+
+/* If external to me.... :) */
+
+#ifdef ALL_EXTERNAL_TO_ME
+
+ extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
+ extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+ extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
+ extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+ extern struct generic_midi_operations *generic_midi_devs[];
+ extern int num_generic_midis, pro_midi_dev;
+
+#ifndef EXCLUDE_MPU401
+ extern int mpu401_dev;
+#endif
+
+ extern struct generic_midi_info midi_supported[];
+ extern struct card_info supported_drivers[];
+ extern int num_sound_drivers;
+ extern int num_midi_drivers;
+#ifndef EXCLUDE_AUDIO
+ extern int sound_buffcounts[MAX_DSP_DEV];
+ extern long sound_buffsizes[MAX_DSP_DEV];
+ extern int sound_dsp_dmachan[MAX_DSP_DEV];
+ extern int sound_dma_automode[MAX_DSP_DEV];
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c
new file mode 100644
index 0000000..5123022
--- /dev/null
+++ b/sys/i386/isa/sound/dmabuf.c
@@ -0,0 +1,773 @@
+/*
+ * linux/kernel/chr_drv/sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "sound_calls.h"
+
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
+
+#define MAX_SUB_BUFFERS 16
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'dma_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.
+ */
+
+#define DMODE_NONE 0
+#define DMODE_OUTPUT 1
+#define DMODE_INPUT 2
+#define DMODE_INIT 3
+
+DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]);
+
+static int dma_mode[MAX_DSP_DEV] =
+{0}; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+
+static volatile int dmabuf_interrupted[MAX_DSP_DEV] =
+{0};
+
+#ifdef ISC
+/* I don't like this. */
+#undef INTERRUPTIBLE_SLEEP_ON
+#define INTERRUPTIBLE_SLEEP_ON(A,F) { \
+ A = F = 1; \
+ if (sleep(&(A), (PZERO + 5) | PCATCH)) { \
+ A = F = 0; \
+ dmabuf_interrupted[dev] = 1; \
+ dev_busy[dev] = 0; \
+ dma_reset(dev); \
+ dmabuf_interrupted[dev] = 0; \
+ /* longjmp(u.u_qsav, 1); Where it goes??? */ \
+ } \
+ }
+#endif
+
+/*
+ * Pointers to raw buffers
+ */
+
+char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+int snd_raw_count[MAX_DSP_DEV];
+
+/*
+ * Device state tables
+ */
+
+static int dev_busy[MAX_DSP_DEV];
+static int dev_active[MAX_DSP_DEV];
+static int dev_qlen[MAX_DSP_DEV];
+static int dev_qhead[MAX_DSP_DEV];
+static int dev_qtail[MAX_DSP_DEV];
+static int dev_underrun[MAX_DSP_DEV];
+static int bufferalloc_done[MAX_DSP_DEV] =
+{0};
+
+/*
+ * Logical buffers for each devices
+ */
+
+static int dev_nbufs[MAX_DSP_DEV]; /* # of logical buffers ( >=
+ * sound_buffcounts[dev] */
+static int dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static char *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static int dev_buffsize[MAX_DSP_DEV];
+
+static void
+reorganize_buffers (int dev)
+{
+ /*
+ * This routine breaks the physical device buffers to logical ones.
+ */
+
+ unsigned long i, p, n;
+ unsigned long sr, nc, sz, bsz;
+
+ sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
+ nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
+ sz = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
+
+ if (sr < 1 || nc < 1 || sz < 1)
+ {
+ printk ("SOUND: 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; /* Convert # of bits -> # of bytes */
+
+ sz = sr * nc * sz;
+
+ /*
+ * Compute a buffer size not exeeding 1 second.
+ */
+
+ bsz = sound_buffsizes[dev];
+
+ while (bsz > sz)
+ bsz >>= 1; /* Divide by 2 */
+
+ if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev])
+ bsz >>= 1; /* Need at least 2 buffers */
+
+ dev_buffsize[dev] = bsz;
+ n = 0;
+
+ /*
+ * Now computing addresses for the logical buffers
+ */
+
+ for (i = 0; i < snd_raw_count[dev]; i++)
+ {
+ p = 0;
+
+ while ((p + bsz) <= sound_buffsizes[dev])
+ {
+ dev_buf[dev][n] = snd_raw_buf[dev][i] + p;
+ dev_buf_phys[dev][n] = snd_raw_buf_phys[dev][i] + p;
+ p += bsz;
+ n++;
+ }
+ }
+
+ dev_nbufs[dev] = n;
+
+ for (i = 0; i < dev_nbufs[dev]; i++)
+ {
+ dev_counts[dev][i] = 0;
+ }
+
+ bufferalloc_done[dev] = 1;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ int retval;
+
+ if (dev >= num_dspdevs)
+ {
+ printk ("PCM device %d not installed.\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev_busy[dev])
+ return RET_ERROR (EBUSY);
+
+ if (!dsp_devs[dev])
+ {
+ printk ("DSP device %d not initialized\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (sound_buffcounts[dev] <= 0)
+ return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
+
+ if ((retval = dsp_devs[dev]->open (dev, mode)) < 0)
+ return retval;
+
+ dev_underrun[dev] = 0;
+
+ dev_busy[dev] = 1;
+
+ reorganize_buffers (dev);
+ bufferalloc_done[dev] = 0;
+
+ dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
+
+ return 0;
+}
+
+static void
+dma_reset (int dev)
+{
+ dsp_devs[dev]->reset (dev);
+
+ dev_qlen[dev] = 0;
+ dev_qhead[dev] = 0;
+ dev_qtail[dev] = 0;
+ dev_active[dev] = 0;
+}
+
+static int
+dma_sync (int dev)
+{
+ unsigned long flags;
+ unsigned long time;
+ int timed_out;
+
+ if (dma_mode[dev] == DMODE_OUTPUT)
+ {
+ DISABLE_INTR (flags);
+
+ timed_out = 0;
+ time = GET_TIME ();
+
+ while ((!(PROCESS_ABORTING || dmabuf_interrupted[dev]) && !timed_out)
+ && dev_qlen[dev])
+ {
+ REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]);
+ INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ if ((GET_TIME () - time) > (10 * HZ))
+ timed_out = 1;
+ }
+ 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 (dsp_devs[dev]->has_output_drained) /* Device has hidden buffers */
+ {
+ while (!(PROCESS_ABORTING || dmabuf_interrupted[dev])
+ && !dsp_devs[dev]->has_output_drained (dev))
+ {
+ REQUEST_TIMEOUT (HZ / 4, dev_sleeper[dev]);
+ INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ }
+ RESTORE_INTR (flags);
+ }
+ return dev_qlen[dev];
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+
+ if (!(PROCESS_ABORTING || dmabuf_interrupted[dev])
+ && (dma_mode[dev] == DMODE_OUTPUT))
+ {
+ dma_sync (dev);
+ }
+
+ dma_reset (dev);
+
+ if (!dev_active[dev])
+ dsp_devs[dev]->close (dev);
+
+ dma_mode[dev] = DMODE_NONE;
+ dev_busy[dev] = 0;
+
+ return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ unsigned long flags;
+
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ if (!dma_mode[dev])
+ {
+ int err;
+
+ if ((err = dsp_devs[dev]->prepare_for_input (dev,
+ dev_buffsize[dev], dev_nbufs[dev])) < 0)
+ return err;
+ dma_mode[dev] = DMODE_INPUT;
+ }
+
+ if (dma_mode[dev] != DMODE_INPUT)
+ return RET_ERROR (EBUSY); /* Can't change mode on fly */
+
+ DISABLE_INTR (flags);
+ if (!dev_qlen[dev])
+ {
+ if (!dev_active[dev])
+ {
+ dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 0);
+ dev_active[dev] = 1;
+ }
+
+ /* Wait for the next block */
+#ifdef CRYPTO
+ REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]);
+#else
+ REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]);
+#endif
+ INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+
+ if (!dev_qlen[dev])
+ return RET_ERROR (EINTR);
+
+ *buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]];
+ *len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]];
+
+ return dev_qhead[dev];
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ int p = dev_counts[dev][dev_qhead[dev]] + c;
+
+ if (p >= dev_buffsize[dev])
+ { /* This buffer is now empty */
+ dev_counts[dev][dev_qhead[dev]] = 0;
+ dev_qlen[dev]--;
+ dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+ }
+ else
+ dev_counts[dev][dev_qhead[dev]] = p;
+
+ return 0;
+}
+
+int
+DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
+{
+ char *dmabuf;
+ int buff_no, c, err;
+
+ /*
+ * This routine returns at most 'count' bytes from the dsp input buffers.
+ * Returns negative value if there is an error.
+ */
+
+ if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &c)) < 0)
+ return buff_no;
+
+ if (c > count)
+ c = count;
+
+ COPY_TO_USER (user_buf, 0, dmabuf, c);
+
+ if ((err = DMAbuf_rmchars (dev, buff_no, c)) < 0)
+ return err;
+ return c;
+
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SNDCTL_DSP_RESET:
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ dma_sync (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ return IOCTL_OUT (arg, dev_buffsize[dev]);
+ break;
+
+ default:
+ return dsp_devs[dev]->ioctl (dev, cmd, arg, local);
+ }
+
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+ unsigned long flags;
+
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ if (!dma_mode[dev])
+ {
+ int err;
+
+ dma_mode[dev] = DMODE_OUTPUT;
+ if ((err = dsp_devs[dev]->prepare_for_output (dev,
+ dev_buffsize[dev], dev_nbufs[dev])) < 0)
+ return err;
+ }
+
+ if (dma_mode[dev] != DMODE_OUTPUT)
+ return RET_ERROR (EBUSY); /* Can't change mode on fly */
+
+ DISABLE_INTR (flags);
+ if (dev_qlen[dev] == dev_nbufs[dev])
+ {
+ if (!dev_active[dev])
+ {
+ printk ("Soundcard warning: DMA not activated %d/%d\n",
+ dev_qlen[dev], dev_nbufs[dev]);
+ return RET_ERROR (EIO);
+ }
+
+ /* Wait for free space */
+ REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]); /* Overestimated timeout */
+ INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+
+ if (dev_qlen[dev] == dev_nbufs[dev])
+ return RET_ERROR (EIO); /* We have got signal (?) */
+
+ *buf = dev_buf[dev][dev_qtail[dev]];
+ *size = dev_buffsize[dev];
+ dev_counts[dev][dev_qtail[dev]] = 0;
+
+ return dev_qtail[dev];
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ if (buff_no != dev_qtail[dev])
+ printk ("Soundcard warning: DMA buffers out of sync %d != %d\n", buff_no, dev_qtail[dev]);
+
+ dev_qlen[dev]++;
+
+ dev_counts[dev][dev_qtail[dev]] = l;
+
+ dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
+
+ if (!dev_active[dev])
+ {
+ dev_active[dev] = 1;
+ dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 0);
+ }
+
+ return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ int chan = sound_dsp_dmachan[dev];
+ 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 (sound_dma_automode[dev])
+ { /* 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, snd_raw_buf_phys[dev][0]);
+ set_dma_count (chan, sound_buffsizes[dev]);
+ 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,
+ snd_raw_buf_phys[dev][0],
+ sound_buffsizes[dev],
+ chan);
+#else
+#ifdef ISC
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) | DMAMODE_AUTO,
+ snd_raw_buf_phys[dev][0], count - 1);
+ 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
+
+#ifdef ISC
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
+ physaddr, count - 1);
+ dma_enable (chan);
+#else
+# error This routine is not valid for this OS.
+#endif /* !ISC */
+#endif
+
+#endif
+ }
+
+ return count;
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ int i;
+
+ /*
+ * In this version the DMA buffer allocation is done by sound_mem_init()
+ * which is called by init/main.c
+ */
+
+ for (i = 0; i < MAX_DSP_DEV; i++)
+ {
+ dev_qlen[i] = 0;
+ dev_qhead[i] = 0;
+ dev_qtail[i] = 0;
+ dev_active[i] = 0;
+ dev_busy[i] = 0;
+ bufferalloc_done[i] = 0;
+ }
+
+ return mem_start;
+}
+
+void
+DMAbuf_outputintr (int dev)
+{
+ unsigned long flags;
+
+ dev_active[dev] = 0;
+ dev_qlen[dev]--;
+ dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+
+ if (dev_qlen[dev])
+ {
+ dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 1);
+ dev_active[dev] = 1;
+ }
+ else
+ {
+ if (dev_busy[dev])
+ {
+ dev_underrun[dev]++;
+ dsp_devs[dev]->halt_xfer (dev);
+ }
+ else
+ { /* Device has been closed */
+ dsp_devs[dev]->close (dev);
+ }
+ }
+
+ DISABLE_INTR (flags);
+ if (dev_sleep_flag[dev])
+ {
+ dev_sleep_flag[dev] = 0;
+ WAKE_UP (dev_sleeper[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ unsigned long flags;
+
+ dev_active[dev] = 0;
+ if (!dev_busy[dev])
+ {
+ dsp_devs[dev]->close (dev);
+ }
+ else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
+ {
+ dev_underrun[dev]++;
+ dsp_devs[dev]->halt_xfer (dev);
+ }
+ else
+ {
+ dev_qlen[dev]++;
+ dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
+
+ dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 1);
+ dev_active[dev] = 1;
+ }
+
+ DISABLE_INTR (flags);
+ if (dev_sleep_flag[dev])
+ {
+ dev_sleep_flag[dev] = 0;
+ WAKE_UP (dev_sleeper[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+ unsigned long flags;
+ int chan = sound_dsp_dmachan[dev];
+
+ if (ALLOC_DMA_CHN (chan))
+ {
+ printk ("Unable to grab DMA%d for the audio driver\n", chan);
+ return 0;
+ }
+
+ DISABLE_INTR (flags);
+#ifdef linux
+ disable_dma (chan);
+ clear_dma_ff (chan);
+#endif
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+ int chan = sound_dsp_dmachan[dev];
+
+ 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_read (int dev, snd_rw_buf * user_buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+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)
+{
+ return;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/finetune.h b/sys/i386/isa/sound/finetune.h
new file mode 100644
index 0000000..07fd3e0
--- /dev/null
+++ b/sys/i386/isa/sound/finetune.h
@@ -0,0 +1,28 @@
+#ifdef SEQUENCER_C
+/*
+ * Copyright Hannu Savolainen 1993
+ * See COPYING for further details. Should be distributed with this file.
+ */
+
+ 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 0000000..39aca78
--- /dev/null
+++ b/sys/i386/isa/sound/gus_card.c
@@ -0,0 +1,183 @@
+
+/*
+ * linux/kernel/chr_drv/sound/gus_card.c
+ *
+ * Detection routine for the Gravis Ultrasound.
+ *
+ * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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;
+
+static int
+set_gus_irq (int interrupt_level)
+{
+ int retcode;
+
+#ifdef linux
+ struct sigaction sa;
+
+ sa.sa_handler = gusintr;
+
+#ifdef SND_SA_INTERRUPT
+ sa.sa_flags = SA_INTERRUPT;
+#else
+ sa.sa_flags = 0;
+#endif
+
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ retcode = irqaction (interrupt_level, &sa);
+
+ if (retcode < 0)
+ {
+ printk ("GUS: IRQ%d already in use\n", interrupt_level);
+ }
+
+#else
+ /* # error Unimplemented for this OS */
+#endif
+ return retcode;
+}
+
+int
+gus_set_midi_irq (int interrupt_level)
+{
+ int retcode;
+
+#ifdef linux
+ struct sigaction sa;
+
+ sa.sa_handler = gus_midi_interrupt;
+
+#ifdef SND_SA_INTERRUPT
+ sa.sa_flags = SA_INTERRUPT;
+#else
+ sa.sa_flags = 0;
+#endif
+
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ retcode = irqaction (interrupt_level, &sa);
+
+ if (retcode < 0)
+ {
+ printk ("GUS: IRQ%d already in use\n", interrupt_level);
+ }
+
+#else
+ /* # error Unimplemented for this OS */
+#endif
+ return retcode;
+}
+
+long
+attach_gus_card (long mem_start, struct address_info *hw_config)
+{
+ int io_addr;
+
+ set_gus_irq (hw_config->irq);
+
+ 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
+ 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 %03x, config was %03x ", 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
+ 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 unit)
+{
+ unsigned char src;
+
+ 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))
+ {
+ printk ("T");
+ gus_write8 (0x45, 0); /* Timer control */
+ }
+
+ if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+ {
+ gus_voice_irq ();
+ }
+ }
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h
new file mode 100644
index 0000000..48233e7
--- /dev/null
+++ b/sys/i386/isa/sound/gus_hw.h
@@ -0,0 +1,35 @@
+
+/*
+ * 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_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 u_DRAMIO (gus_base + 0x107)
diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c
new file mode 100644
index 0000000..6978eed
--- /dev/null
+++ b/sys/i386/isa/sound/gus_midi.c
@@ -0,0 +1,257 @@
+/*
+ * linux/kernel/chr_drv/sound/gus2_midi.c
+ *
+ * The low level driver for the GUS Midi Interface.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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 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)
+{
+
+ 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;
+
+ 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);
+}
+
+static struct midi_operations gus_midi_operations =
+{
+ {"Gravis UltraSound", 0},
+ 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
+};
+
+long
+gus_midi_init (long mem_start)
+{
+ OUTB (MIDI_RESET, u_MidiControl);
+
+ 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)
+ sequencer_midi_input (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 0000000..b3f1e84
--- /dev/null
+++ b/sys/i386/isa/sound/gus_vol.c
@@ -0,0 +1,101 @@
+/*
+ * gus_vol.c - Compute volume for GUS.
+ *
+ * Greg Lee 1993.
+ */
+#include "sound_config.h"
+#ifndef EXCLUDE_GUS
+
+#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 50).
+ */
+ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+#endif
+
+ if (x < (1 << 11))
+ return (11 << 8);
+ 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;
+ }
+
+ /* low volumes give occasional sour notes */
+ if (i < 11)
+ return (11 << 8);
+
+ return ((i << 8) + m);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c
new file mode 100644
index 0000000..bc00af9
--- /dev/null
+++ b/sys/i386/isa/sound/gus_wave.c
@@ -0,0 +1,2523 @@
+
+/*
+ * linux/kernel/chr_drv/sound/gus_wave.c
+ *
+ * Driver for the Gravis UltraSound wave table synth.
+ *
+ * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+/* #define GUS_LINEAR_VOLUME */
+
+#include "sound_config.h"
+#include "ultrasound.h"
+#include "gus_hw.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#define MAX_SAMPLE 256
+#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
+
+ 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;
+
+ };
+
+extern int gus_base;
+extern int gus_irq, gus_dma;
+extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern int snd_raw_count[MAX_DSP_DEV];
+static long gus_mem_size = 0;
+static long free_mem_ptr = 0;
+static int gus_busy = 0;
+static int nr_voices = 0; /* Number of currently allowed voices */
+static int gus_devnum = 0;
+static int volume_base, volume_scale, volume_method;
+
+#define VOL_METHOD_ADAGIO 1
+int gus_wave_volume = 60; /* Master wolume for wave (0 to 100) */
+static unsigned char mix_image = 0x00;
+
+/*
+ * Current version of this_one 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 the second
+ * chn */
+
+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 /* Don't change */
+static int pcm_bsize, /* Current blocksize */
+ pcm_nblk, /* Current # of blocks */
+ pcm_banksize; /* # bytes allocated for channels */
+static int pcm_datasize[MAX_PCM_BUFFERS]; /* Actual # of bytes in blk */
+static volatile int pcm_head, pcm_tail, pcm_qlen; /* DRAM queue */
+static volatile int pcm_active;
+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[MAX_SAMPLE + 1];
+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);
+static void compute_volume (int voice, int volume);
+
+#define INSTANT_RAMP -1 /* Dont use 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 silence here */
+ 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)
+{
+ 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)
+{
+ 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 char data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+ OUTB (data, u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+unsigned char
+gus_read8 (int reg)
+{
+ 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)
+{
+ 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 short data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+
+ OUTB (data & 0xff, u_DataLo);
+ OUTB ((data >> 8) & 0xff, u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+unsigned short
+gus_read16 (int reg)
+{
+ 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)
+{
+ unsigned long hold_address;
+
+ 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));
+}
+
+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;
+
+ nr_voices = nvoices;
+
+ gus_write8 (0x0e, (nvoices - 1) | 0xc0);
+}
+
+static void
+gus_voice_on (unsigned char mode)
+{
+ gus_write8 (0x00, mode & 0xfc);
+ gus_delay ();
+ gus_write8 (0x00, mode & 0xfc);
+}
+
+static void
+gus_voice_off (void)
+{
+ gus_write8 (0x00, gus_read8 (0x00) | 0x03);
+}
+
+static void
+gus_voice_mode (unsigned char mode)
+{
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* Don't start or stop
+ * voice */
+ 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 short vol)
+{
+ gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */
+ gus_write16 (0x09, vol << 4);
+}
+
+static void
+gus_voice_balance (unsigned char balance)
+{
+ gus_write8 (0x0c, balance);
+}
+
+static void
+gus_ramp_range (unsigned short low, unsigned short high)
+{
+ gus_write8 (0x07, (low >> 4) & 0xff);
+ gus_write8 (0x08, (high >> 4) & 0xff);
+}
+
+static void
+gus_ramp_rate (unsigned char scale, unsigned char rate)
+{
+ gus_write8 (0x06, ((scale & 0x03) << 6) | (rate & 0x3f));
+}
+
+static void
+gus_rampon (unsigned char mode)
+{
+ gus_write8 (0x0d, mode & 0xfc);
+ gus_delay ();
+ gus_write8 (0x0d, mode & 0xfc);
+}
+
+static void
+gus_ramp_mode (unsigned char mode)
+{
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* Don't start or stop
+ * ramping */
+ gus_delay ();
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_rampoff (void)
+{
+ gus_write8 (0x0d, 0x03);
+}
+
+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 */
+ RESTORE_INTR (flags);
+
+ 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;
+}
+
+static void
+step_envelope (int voice)
+{
+ unsigned vol, prev_vol, phase;
+ unsigned char rate;
+
+ if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+ {
+ gus_rampoff ();
+ return; /* Sustain */
+ }
+
+ if (voices[voice].env_phase >= 5)
+ {
+ /*
+ * Shoot the voice off
+ */
+
+ gus_voice_init (voice);
+ return;
+ }
+
+ prev_vol = voices[voice].current_volume;
+ gus_voice_volume (prev_vol);
+ 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];
+ gus_write8 (0x06, rate); /* Ramping rate */
+
+ voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+ if (((vol - prev_vol) / 64) == 0) /* No significant volume change */
+ {
+ step_envelope (voice); /* Continue with the next phase */
+ return;
+ }
+
+ if (vol > prev_vol)
+ {
+ if (vol >= (4096 - 64))
+ vol = 4096 - 65;
+ gus_ramp_range (0, vol);
+ gus_rampon (0x20); /* Increasing, irq */
+ }
+ else
+ {
+ if (vol <= 64)
+ vol = 65;
+ gus_ramp_range (vol, 4095);
+ gus_rampon (0x60); /* Decreasing, irq */
+ }
+ voices[voice].current_volume = vol;
+}
+
+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)
+{
+ 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 ();
+ step_envelope (voice);
+}
+
+static void
+gus_voice_fade (int voice)
+{
+ int instr_no = sample_map[voice], is16bits;
+
+ if (instr_no < 0 || instr_no > MAX_SAMPLE)
+ {
+ gus_write8 (0x00, 0x03); /* Hard stop */
+ return;
+ }
+
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ start_release (voice);
+ return;
+ }
+
+ /*
+ * Ramp the volume down but not too quickly.
+ */
+ if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ return;
+ }
+
+ gus_ramp_range (65, 4095);
+ gus_ramp_rate (2, 4);
+ gus_rampon (0x40 | 0x20); /* Down, once, irq */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+}
+
+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 */
+ }
+
+ 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.
+ *
+ * If GUS_MIDI_IRQ is defined and if it's != GUS_IRQ, separate Midi IRQ is set
+ * up. Otherwise the same IRQ is shared by the both devices.
+ *
+ * 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;
+
+ if (GUS_MIDI_IRQ != gus_irq)
+ { /* The midi irq was defined and != wave irq */
+ tmp = gus_irq_map[GUS_MIDI_IRQ];
+ tmp <<= 3;
+
+ if (!tmp)
+ printk ("Warning! GUS Midi IRQ not selected\n");
+ else
+ gus_set_midi_irq (GUS_MIDI_IRQ);
+
+ irq_image |= tmp;
+ }
+ else
+ 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, 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 */
+
+ 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)
+{
+ gus_base = baseaddr;
+
+ gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /* Release Reset */
+ gus_delay ();
+ gus_delay ();
+
+ gus_poke (0x000, 0xaa);
+ gus_poke (0x100, 0x55);
+
+ if (gus_peek (0x000) != 0xaa)
+ return 0;
+ if (gus_peek (0x100) != 0x55)
+ return 0;
+
+ gus_mem_size = 0x40000; /* 256k */
+ gus_poke (0x40000, 0xaa);
+ if (gus_peek (0x40000) != 0xaa)
+ return 1;
+
+ gus_mem_size = 0x80000; /* 512k */
+ gus_poke (0x80000, 0xaa);
+ if (gus_peek (0x80000) != 0xaa)
+ return 1;
+
+ gus_mem_size = 0xc0000; /* 768k */
+ gus_poke (0xc0000, 0xaa);
+ if (gus_peek (0xc0000) != 0xaa)
+ return 1;
+
+ gus_mem_size = 0x100000; /* 1M */
+
+ 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);
+
+ 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 velocity)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_fade (voice);
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+static void
+guswave_aftertouch (int dev, int voice, int pressure)
+{
+ short lo_limit, hi_limit;
+ unsigned long flags;
+
+ return; /* 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, Down, 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
+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 (volume, voices[voice].main_vol,
+ voices[voice].expression_vol,
+ voices[voice].patch_vol);
+ break;
+
+ default:
+ voices[voice].initial_volume = volume_base + (volume * volume_scale);
+ }
+ }
+
+ if (voices[voice].initial_volume > 4095)
+ voices[voice].initial_volume = 4095;
+}
+
+static void
+compute_and_set_volume (int voice, int volume, int ramp_time)
+{
+ int current, target, rate;
+
+ compute_volume (voice, volume);
+ voices[voice].current_volume = voices[voice].initial_volume;
+
+ current = gus_read16 (0x09) >> 4;
+ target = voices[voice].initial_volume;
+
+ if (ramp_time == INSTANT_RAMP)
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ return;
+ }
+
+ if (ramp_time == FAST_RAMP)
+ rate = 63;
+ else
+ rate = 16;
+ gus_ramp_rate (0, rate);
+
+ if ((target - current) / 64 == 0) /* Too close */
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ 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 */
+ }
+}
+
+static void
+dynamic_volume_change (int voice)
+{
+ unsigned char status;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x00); /* Voice status */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03)
+ return; /* Voice not started */
+
+ 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);
+
+#if 0 /* Is this really required */
+ voices[voice].current_volume =
+ gus_read16 (0x09) >> 4; /* Get current volume */
+
+ voices[voice].env_phase--;
+ step_envelope (voice);
+#endif
+}
+
+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;
+ 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 CTRL_EXPRESSION:
+ volume_method = VOL_METHOD_ADAGIO;
+ voices[voice].expression_vol = value;
+ dynamic_volume_change (voice);
+ break;
+
+ case CTRL_MAIN_VOLUME:
+ volume_method = VOL_METHOD_ADAGIO;
+ voices[voice].main_vol = value;
+ dynamic_volume_change (voice);
+ break;
+
+ default: /* Ignore */
+ break;
+ }
+}
+
+static int
+guswave_start_note (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; /* 8 or 16 bit samples */
+ 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; /* 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 (); /* It may still be running */
+ gus_rampoff ();
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ compute_volume (voice, volume);
+ init_envelope (voice);
+ }
+ else
+ compute_and_set_volume (voice, volume, 0);
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len, is16bits); /* Sample start=end */
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample], is16bits); /* Sample start=begin */
+
+ if (samples[sample].mode & WAVE_LOOPING)
+ {
+ mode |= 0x08; /* Looping on */
+
+ if (samples[sample].mode & WAVE_BIDIR_LOOP)
+ mode |= 0x10; /* Bidirectional looping on */
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ {
+ gus_write_addr (0x0a, /* Put the current location = loop_end */
+ sample_ptrs[sample] + samples[sample].loop_end, is16bits);
+ mode |= 0x40; /* Loop backwards */
+ }
+
+ 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 it 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, is16bits); /* Loop end location */
+ }
+ gus_voice_freq (freq);
+ gus_voice_balance (pan);
+ gus_voice_on (mode);
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+static void
+guswave_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < 32; i++)
+ gus_voice_init (i);
+}
+
+static int
+guswave_open (int dev, int mode)
+{
+ int err;
+
+ if (gus_busy)
+ return RET_ERROR (EBUSY);
+
+ if ((err = DMAbuf_open_dma (gus_devnum)))
+ return err;
+
+ 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;
+
+ unsigned long blk_size, blk_end, left, src_offs, target;
+
+ if (format != GUS_PATCH)
+ {
+ printk ("GUS Error: Invalid patch format (key) 0x%04x\n", format);
+#ifndef CPU_I486
+ if (format == OBSOLETE_GUS_PATCH)
+ printk ("GUS Error: obsolete patch format\n");
+#endif
+ 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, patch.len);
+ patch.len = count;
+ }
+
+ if (patch.len <= 0 || patch.len > gus_mem_size)
+ {
+ printk ("GUS: Invalid sample length %d\n", 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; /* Alignment 32 bytes */
+
+#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", 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 = /* Align to 256K*N */
+ ((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 all moved */
+ {
+ blk_size = sound_buffsizes[gus_devnum];
+ 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))
+ { /* Have to split the block */
+
+ blk_end &= ~(256 * 1024 - 1);
+ blk_size = blk_end - target;
+ }
+
+#ifdef GUS_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);
+ gus_poke (target + i, data);
+ }
+ }
+#else /* GUS_NO_DMA */
+ {
+ unsigned long address, hold_address;
+ unsigned char dma_command;
+
+ /*
+ * OK, move now. First in and then out.
+ */
+
+ COPY_FROM_USER (snd_raw_buf[gus_devnum][0],
+ addr, sizeof (patch) + src_offs,
+ blk_size);
+
+ gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0],
+ blk_size, DMA_MODE_WRITE);
+
+ /*
+ * Set the DRAM address for the wave data
+ */
+
+ address = target;
+
+ if (sound_dsp_dmachan[gus_devnum] > 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 (sound_dsp_dmachan[gus_devnum] > 3)
+ dma_command |= 0x04; /* 16 bit DMA channel */
+
+ gus_write8 (0x41, dma_command); /* Let's go luteet (=bugs) */
+
+ /*
+ * Sleep here until the DRAM DMA done interrupt is served
+ */
+ active_device = GUS_DEV_WAVE;
+
+ INTERRUPTIBLE_SLEEP_ON (dram_sleeper, dram_sleep_flag);
+ }
+#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];
+
+ 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; /* Disable intr */
+ 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:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_fade (voice);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEMODE:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Disable intr */
+ 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 voice value */
+ 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; /* NO-NO */
+ 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; /* Disable intr */
+ gus_ramp_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPON:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Disable intr */
+ gus_rampon (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPOFF:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOLUME_SCALE:
+ volume_base = p1;
+ volume_scale = p2;
+ 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:
+ 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_SAMPLESIZE:
+ 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 YET IMPLEMENTED */
+ 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_busy = 1;
+ active_device = 0;
+
+ gus_reset ();
+ reset_sample_memory ();
+ gus_select_max_voices (14);
+
+ gus_sampling_set_bits (8);
+ gus_sampling_set_channels (1);
+ gus_sampling_set_speed (DSP_DEFAULT_SPEED);
+ pcm_active = 0;
+
+ return 0;
+}
+
+static void
+gus_sampling_close (int dev)
+{
+ gus_reset ();
+ gus_busy = 0;
+ active_device = 0;
+}
+
+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 of the DRAM buffers */
+ {
+ mode[chn] |= 0x08; /* Enable loop */
+ ramp_mode[chn] = 0x03;/* Disable rollover */
+ }
+ 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) /* Voice not started yet */
+ {
+ /*
+ * 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 (); /* It could already be running */
+ gus_rampoff ();
+ gus_voice_volume (4000);
+ gus_ramp_range (65, 4030);
+
+ gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */
+ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start location */
+
+ 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 loop */
+
+ if (pcm_datasize[this_one] != pcm_bsize)
+ {
+ /* Incomplete block. Possibly the last one. */
+ if (chn == 0)
+ {
+ mode[chn] &= ~0x08; /* Disable loop */
+ mode[chn] |= 0x20;/* Enable loop IRQ */
+ 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 loop */
+ }
+ }
+
+ 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 (sound_dsp_dmachan[dev] > 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 (sound_dsp_dmachan[dev] > 3)
+ dma_command |= 0x04; /* 16 bit DMA channel */
+
+ gus_write8 (0x41, dma_command); /* Kick on */
+
+ if (chn == (gus_sampling_channels - 1)) /* Last channel */
+ {
+ /* Last (right or mono) channel data */
+ active_device = GUS_DEV_PCM_DONE;
+ if (!pcm_active && (pcm_qlen > 2 || 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)
+{
+ 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)
+{
+ unsigned long flags;
+ unsigned char mode;
+
+ DISABLE_INTR (flags);
+
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ mode = 0xa0; /* DMA IRQ enable, invert MSB */
+
+ if (sound_dsp_dmachan[dev] > 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 frequency */
+
+ 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_has_output_drained (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",
+ 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, /* halt_xfer */
+ gus_has_output_drained,
+ gus_copy_from_user
+};
+
+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 address */
+ 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; /* 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++)
+ 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 struct synth_operations guswave_operations =
+{
+ &gus_info,
+ 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_patchmgr
+};
+
+long
+gus_wave_init (long mem_start, int irq, int dma)
+{
+ printk (" <Gravis UltraSound %dk>", gus_mem_size / 1024);
+
+ 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
+ synth_devs[num_synths++] = &guswave_operations;
+
+ reset_sample_memory ();
+
+ gus_initialize ();
+
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations;
+ sound_dsp_dmachan[gus_devnum] = dma;
+ sound_buffcounts[gus_devnum] = 1;
+ sound_buffsizes[gus_devnum] = DSP_BUFFSIZE;
+ sound_dma_automode[gus_devnum] = 0;
+ }
+ else
+ printk ("GUS: Too many PCM devices available\n");
+
+ 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 ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ return;
+ }
+ 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; /* Requires extensive processing */
+ case LMODE_PCM:
+ {
+ int orig_qlen = pcm_qlen;
+
+ pcm_qlen--;
+ pcm_head = (pcm_head + 1) % pcm_nblk;
+ if (pcm_qlen)
+ {
+ play_next_pcm_block ();
+ }
+ else
+ { /* Out of data. Just stop the voice */
+ gus_voice_off ();
+ gus_rampoff ();
+ pcm_active = 0;
+ }
+
+ if (orig_qlen == pcm_nblk)
+ {
+ DMAbuf_outputintr (gus_devnum);
+ }
+ }
+ 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 */
+ gus_voice_init (voice);
+ break;
+
+ case VMODE_ENVELOPE:
+ gus_rampoff ();
+ step_envelope (voice);
+ break;
+
+ default:;
+ }
+
+ RESTORE_INTR (flags);
+}
+
+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) && 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) && 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 Irq pending */
+ switch (active_device)
+ {
+ case GUS_DEV_WAVE:
+ if (dram_sleep_flag)
+ WAKE_UP (dram_sleeper);
+ break;
+
+ case GUS_DEV_PCM_CONTINUE:
+ gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
+ pcm_current_count,
+ pcm_current_intrflag, 1);
+ break;
+
+ case GUS_DEV_PCM_DONE:
+ if (pcm_qlen < pcm_nblk)
+ {
+ DMAbuf_outputintr (gus_devnum);
+ }
+ 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/gustest/Makefile b/sys/i386/isa/sound/gustest/Makefile
new file mode 100644
index 0000000..d161e5b
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/Makefile
@@ -0,0 +1,16 @@
+all: gustest gusload gmod midithru
+
+gustest: gustest.c
+ $(CC) -o gustest gustest.c -lm
+
+gusload: gusload.c
+ $(CC) -o gusload gusload.c
+
+gmod: gmod.c
+ $(CC) -o gmod gmod.c
+
+midithru: midithru.c
+ $(CC) -o midithru midithru.c
+
+clean:
+ rm -f gusload gustest gmod midithru *.o
diff --git a/sys/i386/isa/sound/gustest/Readme b/sys/i386/isa/sound/gustest/Readme
new file mode 100644
index 0000000..7640bf8
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/Readme
@@ -0,0 +1,67 @@
+The programs in this directory are more or less incompletely implemented.
+I have used them for debugging purposes while developing the driver.
+
+Files in this directory:
+
+
+../ultrasound.h (sys/ultrasound.h)
+ This file contains some macros which are similar than
+ the procedures provided by GUSUNIT.PAS. See gustest.c
+ for more information.
+ INSTALL THIS FILE TO YOUR /usr/include/sys !!!!!!!!!!!!
+
+gusload.c This program can be used to load patches (samples) to
+ the DRAM of GUS. It understands the format used in the
+ .pat files shipped with GUS.
+
+ Usage: gusload pgm# patchfile.
+ or gusload reset #Removes all patches from memory.
+
+ You should load just the patches you will need to play
+ a Midi file, since the memory capacity of GUS is rather
+ limited (256k-1M).
+
+ Example:
+
+ gusload 0 acpiano.pat
+ gusload 1 britepno.pat
+ gusload 19 church.pat
+
+ This program is not required if the adagio package is
+ used. It can do the patch uploading itself.
+
+gmod.c This is a simple module player which demonstrates
+ programming with GUS. It doesn't try to interpret
+ most of the effect commands. In fact this program
+ may interpret the modules incorrectly since I am
+ not a module player expert.
+ This version plays .MOD, .STM and .669 modules.
+
+midithru.c This program reads messages from the Midi interface
+ and plays the notes with an internal synthesizer
+ (FM or GUS). The program accepts one argument, the
+ synthesizer device number. In addition to the note on
+ and note off messages it interprets also program changes
+ and channel pressure messages.
+ If you need an example on programming the /dev/sequencer,
+ this is a good one. The voice allocation algorithm is
+ not good so don't look at it.
+
+ NOTE! This program is useful with gmod. Jus load
+ a module with gmod. Wait until the module has
+ finished or hit ^C. Now you can play the samples
+ with the midithru program.
+
+ NOTE2! You need a Midi keyboard to use this program. In
+ addition the Midi interface of GUS is not supported
+ yet which means you need also PAS16 or MPU-401.
+
+pmtest.c
+gpatinfo.c ******* For information only *******
+ These programs demonstrate the patch manager interface
+ which will be included to some later driver version.
+ This interface is not complete in version 1.99.9.
+ Using pmtest will hang your system sooner or later.
+
+Hannu Savolainen
+hsavolai@cs.helsinki.fi
diff --git a/sys/i386/isa/sound/gustest/gmidi.h b/sys/i386/isa/sound/gustest/gmidi.h
new file mode 100644
index 0000000..ab951e7
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gmidi.h
@@ -0,0 +1,131 @@
+ char patch_names[][9] =
+ {
+ /* 0 */ "acpiano",
+ /* 1 */ "britepno",
+ /* 2 */ "synpiano",
+ /* 3 */ "honktonk",
+ /* 4 */ "epiano1",
+ /* 5 */ "epiano2",
+ /* 6 */ "hrpschrd",
+ /* 7 */ "clavinet",
+ /* 8 */ "celeste",
+ /* 9 */ "glocken",
+ /* 10 */ "musicbox",
+ /* 11 */ "vibes",
+ /* 12 */ "marimba",
+ /* 13 */ "xylophon",
+ /* 14 */ "tubebell",
+ /* 15 */ "santur",
+ /* 16 */ "homeorg",
+ /* 17 */ "percorg",
+ /* 18 */ "rockorg",
+ /* 19 */ "church",
+ /* 20 */ "reedorg",
+ /* 21 */ "accordn",
+ /* 22 */ "harmonca",
+ /* 23 */ "concrtna",
+ /* 24 */ "nyguitar",
+ /* 25 */ "acguitar",
+ /* 26 */ "jazzgtr",
+ /* 27 */ "cleangtr",
+ /* 28 */ "mutegtr",
+ /* 29 */ "odguitar",
+ /* 30 */ "distgtr",
+ /* 31 */ "gtrharm",
+ /* 32 */ "acbass",
+ /* 33 */ "fngrbass",
+ /* 34 */ "pickbass",
+ /* 35 */ "fretless",
+ /* 36 */ "slapbas1",
+ /* 37 */ "slapbas2",
+ /* 38 */ "synbass1",
+ /* 39 */ "synbass2",
+ /* 40 */ "violin",
+ /* 41 */ "viola",
+ /* 42 */ "cello",
+ /* 43 */ "contraba",
+ /* 44 */ "marcato",
+ /* 45 */ "pizzcato",
+ /* 46 */ "harp",
+ /* 47 */ "timpani",
+ /* 48 */ "marcato",
+ /* 49 */ "slowstr",
+ /* 50 */ "synstr1",
+ /* 51 */ "synstr2",
+ /* 52 */ "choir",
+ /* 53 */ "doo",
+ /* 54 */ "voices",
+ /* 55 */ "orchhit",
+ /* 56 */ "trumpet",
+ /* 57 */ "trombone",
+ /* 58 */ "tuba",
+ /* 59 */ "mutetrum",
+ /* 60 */ "frenchrn",
+ /* 61 */ "hitbrass",
+ /* 62 */ "synbras1",
+ /* 63 */ "synbras2",
+ /* 64 */ "sprnosax",
+ /* 65 */ "altosax",
+ /* 66 */ "tenorsax",
+ /* 67 */ "barisax",
+ /* 68 */ "oboe",
+ /* 69 */ "englhorn",
+ /* 70 */ "bassoon",
+ /* 71 */ "clarinet",
+ /* 72 */ "piccolo",
+ /* 73 */ "flute",
+ /* 74 */ "recorder",
+ /* 75 */ "woodflut",
+ /* 76 */ "bottle",
+ /* 77 */ "shakazul",
+ /* 78 */ "whistle",
+ /* 79 */ "ocarina",
+ /* 80 */ "sqrwave",
+ /* 81 */ "sawwave",
+ /* 82 */ "calliope",
+ /* 83 */ "chiflead",
+ /* 84 */ "voxlead",
+ /* 85 */ "voxlead",
+ /* 86 */ "lead5th",
+ /* 87 */ "basslead",
+ /* 88 */ "fantasia",
+ /* 89 */ "warmpad",
+ /* 90 */ "polysyn",
+ /* 91 */ "ghostie",
+ /* 92 */ "bowglass",
+ /* 93 */ "metalpad",
+ /* 94 */ "halopad",
+ /* 95 */ "sweeper",
+ /* 96 */ "aurora",
+ /* 97 */ "soundtrk",
+ /* 98 */ "crystal",
+ /* 99 */ "atmosphr",
+ /* 100 */ "freshair",
+ /* 101 */ "unicorn",
+ /* 102 */ "sweeper",
+ /* 103 */ "startrak",
+ /* 104 */ "sitar",
+ /* 105 */ "banjo",
+ /* 106 */ "shamisen",
+ /* 107 */ "koto",
+ /* 108 */ "kalimba",
+ /* 109 */ "bagpipes",
+ /* 110 */ "fiddle",
+ /* 111 */ "Shannai",
+ /* 112 */ "carillon",
+ /* 113 */ "agogo",
+ /* 114 */ "steeldrm",
+ /* 115 */ "woodblk",
+ /* 116 */ "taiko",
+ /* 117 */ "toms",
+ /* 118 */ "syntom",
+ /* 119 */ "revcym",
+ /* 120 */ "fx-fret",
+ /* 121 */ "fx-blow",
+ /* 122 */ "seashore",
+ /* 123 */ "jungle",
+ /* 124 */ "telephon",
+ /* 125 */ "helicptr",
+ /* 126 */ "applause",
+ /* 127 */ "ringwhsl"
+ };
diff --git a/sys/i386/isa/sound/gustest/gmod.c b/sys/i386/isa/sound/gustest/gmod.c
new file mode 100644
index 0000000..d730e26
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gmod.c
@@ -0,0 +1,1589 @@
+/*
+ * gmod.c - Module player for GUS and Linux.
+ * (C) Hannu Savolainen, 1993
+ *
+ * NOTE! This program doesn't try to be a complete module player.
+ * It's just a too I used while developing the driver. In
+ * addition it can be used as an example on programming
+ * the LInux Sound Driver with GUS.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ultrasound.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <math.h>
+#include <string.h>
+
+#define CMD_ARPEG 0x00
+#define CMD_SLIDEUP 0x01
+#define CMD_SLIDEDOWN 0x02
+#define CMD_SLIDETO 0x03
+#define SLIDE_SIZE 8
+#define CMD_VOLSLIDE 0x0a
+#define CMD_JUMP 0x0b
+#define CMD_VOLUME 0x0c
+#define CMD_BREAK 0x0d
+#define CMD_SPEED 0x0f
+#define CMD_NOP 0xfe
+#define CMD_NONOTE 0xff
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define MAX_TRACK 8
+#define MAX_PATTERN 128
+#define MAX_POSITION 128
+
+struct note_info
+ {
+ unsigned char note;
+ unsigned char vol;
+ unsigned char sample;
+ unsigned char command;
+ short parm1, parm2;
+ };
+
+struct voice_info
+ {
+ int sample;
+ int note;
+ int volume;
+ int pitchbender;
+
+ /* Pitch sliding */
+
+ int slide_pitch;
+ int slide_goal;
+ int slide_rate;
+
+ int volslide;
+ };
+
+typedef struct note_info pattern[MAX_TRACK][64];
+int pattern_len[MAX_POSITION];
+int pattern_tempo[MAX_POSITION];
+pattern *pattern_table[MAX_PATTERN];
+
+struct voice_info voices[MAX_TRACK];
+
+int nr_channels, nr_patterns, songlength;
+int tune[MAX_POSITION];
+double tick_duration;
+
+int period_table[] =
+{
+ 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
+ 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
+ 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
+};
+
+SEQ_DEFINEBUF (2048);
+
+int seqfd;
+int sample_ok[128], sample_vol[128];
+int tmp, gus_dev;
+double this_time, next_time;
+int ticks_per_division;
+double clock_rate; /* HZ */
+
+/*
+ * The function seqbuf_dump() must always be provided
+ */
+
+void play_module (char *name);
+int load_module (char *name);
+int play_note (int channel, struct note_info *pat);
+void lets_play_voice (int channel, struct voice_info *v);
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+void
+init_voices ()
+{
+ int i;
+
+ for (i = 0; i < MAX_TRACK; i++)
+ {
+ voices[i].sample = 0;
+ voices[i].note = 0;
+ voices[i].volume = 64;
+
+ voices[i].slide_pitch = 0;
+ voices[i].slide_goal = 0;
+ voices[i].slide_rate = 0;
+ voices[i].pitchbender = 0;
+
+ voices[i].volslide = 0;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, n, j;
+ struct synth_info info;
+
+ if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ GUS_NUMVOICES (gus_dev, 14);
+
+ for (i = 1; i < argc; i++)
+ {
+ for (j = 0; j < MAX_PATTERN; j++)
+ pattern_table[j] = NULL;
+
+ if (load_module (argv[i]))
+ {
+ tick_duration = 100.0 / clock_rate;
+ play_module (argv[i]);
+ }
+
+ }
+
+ SEQ_DUMPBUF ();
+ close (seqfd);
+
+ exit (0);
+}
+
+unsigned short
+intelize (unsigned short v)
+{
+ return ((v & 0xff) << 8) | ((v >> 8) & 0xff);
+}
+
+unsigned long
+intelize4 (unsigned long v)
+{
+ return
+ (((v >> 16) & 0xff) << 8) | (((v >> 16) >> 8) & 0xff) |
+ (((v & 0xff) << 8) | ((v >> 8) & 0xff) << 16);
+}
+
+int
+load_stm_module (int mod_fd, char *name)
+{
+
+ struct sample_header
+ {
+ char name[12];
+ unsigned char instr_disk;
+ unsigned short reserved1;
+ unsigned short length; /* In bytes */
+ unsigned short loop_start;
+ unsigned short loop_end;
+ unsigned char volume;
+ unsigned char reserved2;
+ unsigned short C2_speed;
+ unsigned short reserved3;
+
+ };
+
+ int i, total_mem;
+ int sample_ptr;
+
+ int position;
+
+ unsigned char *tune_ptr; /* array 0-127 */
+
+ char header[1105], sname[21];
+
+ int nr_samples; /* 16 or 32 samples (or 64 or ???) */
+ int slen, npat;
+
+ fprintf (stderr, "Loading .STM module: %s\n", name);
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ strncpy (sname, header, 20);
+
+ fprintf (stderr, "\nModule: %s - ", sname);
+
+ if (header[28] != 0x1a)
+ {
+ fprintf (stderr, "Not a STM module\n");
+ close (mod_fd);
+ return 0;
+ }
+
+ npat = header[33];
+ slen = 0;
+ tune_ptr = &header[48 + (31 * 32)];
+
+ for (i = 0; i < 64; i++)
+ {
+ tune[i] = tune_ptr[i];
+ if (tune[i] < npat)
+ slen = i;
+ }
+
+ fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
+
+ nr_samples = 31;
+
+ sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the
+ * first sample is
+ * stored */
+ total_mem = 0;
+
+ for (i = 0; i < 32; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end, base_freq;
+ unsigned short loop_flags = 0;
+
+ struct sample_header *sample;
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[48 + (i * 32)];
+
+ len = sample->length;
+ loop_start = sample->loop_start;
+ loop_end = sample->loop_end;
+ base_freq = sample->C2_speed;
+
+ if (strlen (sample->name) > 21)
+ {
+ fprintf (stderr, "\nInvalid name for sample #%d\n", i);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (len > 0)
+ {
+ int x;
+
+ if (loop_end > len)
+ loop_end = 1;
+ else if (loop_end < loop_start)
+ {
+ loop_start = 0;
+ loop_end = 0;
+ }
+ else
+ loop_flags = WAVE_LOOPING;
+
+ total_mem += len;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_freq = base_freq;
+ patch->base_note = 261630; /* Mid C */
+ patch->low_note = 0;
+ patch->high_note = 0x7fffffff;
+ patch->volume = 120;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if ((x = read (mod_fd, patch->data, len)) != len)
+ {
+ fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n",
+ i,
+ len,
+ loop_start,
+ loop_end,
+ base_freq,
+ sample->name);
+
+ if (write (seqfd, patch, sizeof (*patch) + len) == -1)
+ {
+ perror ("ioctl /dev/sequencer");
+ exit (-1);
+ }
+ else
+ sample_ok[i] = 1;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = slen;
+ songlength = slen;
+ nr_channels = 4;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[64][4][4];
+ int pat, channel, x;
+
+ int pp = 1104 + (position * 1024);
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((x = read (mod_fd, patterns, 1024)) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
+ close (mod_fd);
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+
+ for (channel = 0; channel < 4; channel++)
+ {
+ unsigned char *p;
+
+ unsigned vol, note, octave, sample, effect, params;
+
+ p = &patterns[pat][channel][0];
+
+ if (p[0] < 251)
+ {
+ note = p[0] & 15;
+ octave = p[0] / 16;
+
+ note = 48 + octave * 12 + note;
+
+ sample = p[1] / 8;
+ vol = (p[1] & 7) + (p[2] / 2);
+ effect = p[2] & 0xF;
+ params = p[3];
+ }
+ else
+ {
+ note = 0;
+ octave = 0;
+
+ sample = 0;
+ vol = 0;
+ effect = CMD_NONOTE;
+ params = 0;
+ }
+
+ (*pattern_table[position])[channel][pat].note = note;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+
+ }
+
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+load_669_module (int mod_fd, char *name)
+{
+ struct sample_header
+ {
+ char name[13];
+ unsigned long length; /* In bytes */
+ unsigned long loop_start;
+ unsigned long loop_end;
+ };
+
+ int i, total_mem;
+ int sample_ptr;
+
+ int position;
+
+ unsigned char *tune_ptr, *len_ptr, *tempo_ptr; /* array 0-127 */
+
+ char header[1084];
+ char msg[110];
+
+ int nr_samples; /* 16 or 32 samples */
+ int slen, npat;
+
+ clock_rate = 25.0;
+
+ fprintf (stderr, "Loading .669 module: %s\n", name);
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (*(unsigned short *) &header[0] != 0x6669)
+ {
+ fprintf (stderr, "Not a 669 file\n");
+ close (mod_fd);
+ return 0;
+ }
+
+ strncpy (msg, &header[2], 108);
+
+ for (i = 0; i < strlen (msg); i++)
+ if ((msg[i] >= ' ' && msg[i] <= 'z') || msg[i] == '\n')
+ printf ("%c", msg[i]);
+ printf ("\n");
+
+ npat = header[0x6f];
+
+ tune_ptr = &header[0x71];
+
+ for (slen = 0; slen < 128 && tune_ptr[slen] != 0xff; slen++);
+ slen--;
+
+ for (i = 0; i < slen; i++)
+ tune[i] = tune_ptr[i];
+
+ len_ptr = &header[0x171];
+ for (i = 0; i < slen; i++)
+ pattern_len[i] = len_ptr[i] - 1;
+
+ tempo_ptr = &header[0xf1];
+ for (i = 0; i < slen; i++)
+ pattern_tempo[i] = tempo_ptr[i];
+
+ nr_samples = header[0x6e];
+
+ fprintf (stderr, "Song lenght %d, %d patterns, %d samples.\n", slen, npat, nr_samples);
+
+ sample_ptr = 0x1f1 + (nr_samples * 0x19) + (npat * 0x600); /* Location where the
+ * first sample is
+ * stored */
+ total_mem = 0;
+
+ for (i = 0; i < 64; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end;
+ unsigned short loop_flags = 0;
+
+ struct sample_header *sample;
+ char sname[14];
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[0x1f1 + (i * 0x19)];
+
+ len = *(unsigned long *) &sample->name[13];
+ loop_start = *(unsigned long *) &sample->name[17];
+ loop_end = *(unsigned long *) &sample->name[21];
+ if (loop_end > len)
+ loop_end = 1;
+ else if (loop_end == len)
+ loop_end--;
+
+ if (loop_end < loop_start)
+ {
+ loop_start = 0;
+ loop_end = 0;
+ }
+
+ strncpy (sname, sample->name, 13);
+
+ if (len > 0 && len < 200000)
+ {
+ total_mem += len;
+
+ fprintf (stderr, "Sample %02d: %05d, %05d, %05d %s\n",
+ i,
+ len,
+ loop_start,
+ loop_end,
+ sname);
+
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ if (loop_end == 0)
+ loop_end = 1;
+ if (loop_end >= len)
+ loop_end = 1;
+
+ if (loop_end > 1) loop_flags = WAVE_LOOPING;
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = WAVE_UNSIGNED | loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_freq = 8448;
+ patch->base_note = 261630;
+ patch->low_note = 1000;
+ patch->high_note = 0x7fffffff;
+ patch->volume = 120;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ fprintf (stderr, "Seek failed\n");
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if (read (mod_fd, patch->data, len) != len)
+ {
+ fprintf (stderr, "Short file (sample at %d)\n", sample_ptr);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ if (write (seqfd, patch, sizeof (*patch) + len) == -1)
+ {
+ perror ("ioctl /dev/sequencer");
+ /* exit (-1); */
+ }
+ else
+ sample_ok[i] = 1;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = slen;
+ songlength = slen;
+ nr_channels = 8;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[0x600];
+ int pat, channel, x;
+
+ int pp = 0x1f1 + (nr_samples * 0x19) + (position * 0x600);
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((x = read (mod_fd, patterns, 1024)) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern at %d) %d!=1024\n", pp, x);
+ close (mod_fd);
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+
+ for (channel = 0; channel < 8; channel++)
+ {
+ unsigned char *p;
+
+ unsigned vol, period, sample, effect, params;
+
+ p = &patterns[pat * 24 + channel * 3];
+
+ if (p[0] >= 0xfe ||
+ (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff) ||
+ (p[0] == 0 && p[1] == 0 && p[2] == 0) ||
+ *(int *) p == -1)
+ {
+ period = 0;
+ effect = CMD_NONOTE;
+ sample = 0;
+ vol = 0;
+ params = 0;
+
+ if (p[0] == 0)
+ {
+ effect = CMD_BREAK;
+ params = -2;
+ }
+ }
+ else
+ {
+ period = (p[0] >> 2) + 48;
+ effect = (p[2] >> 4);
+ params = p[2] & 0x0f;
+ vol = p[1] & 0x0f;
+
+ if (p[2] == 0xfe)
+ {
+ effect = CMD_VOLUME;
+ params = vol;
+ }
+ else if (p[2] == 0xff)
+ {
+ effect = CMD_NOP;
+ }
+ else
+ switch (effect)
+ {
+ case 0: /* a - Portamento up */
+ effect = CMD_SLIDEUP;
+ break;
+
+ case 1: /* b - Portamento down */
+ effect = CMD_SLIDEDOWN;
+ break;
+
+ case 2: /* c - Port to note */
+ effect = CMD_SLIDETO;
+ break;
+
+ case 3: /* d - Frequency adjust */
+ effect = CMD_NOP; /* To be implemented */
+ break;
+
+ case 4: /* e - Frequency vibrato */
+ effect = CMD_NOP; /* To be implemented */
+ break;
+
+ case 5: /* f - Set tempo */
+ effect = CMD_SPEED;
+ break;
+
+ default:
+ effect = CMD_NOP;
+ }
+
+ sample = (((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)) + 1;
+ }
+
+ (*pattern_table[position])[channel][pat].note = period;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+
+ }
+
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+load_mmd0_module (int mod_fd, char *name)
+{
+
+ struct sample_header
+ {
+ unsigned short loop_start;
+ unsigned short loop_end;
+ unsigned char midich;
+ unsigned char midipreset;
+ unsigned char volume;
+ unsigned char strans;
+ };
+
+ int i, total_mem;
+ int sample_ptr;
+
+ int position;
+
+ unsigned char *tune_ptr; /* array 0-127 */
+
+ char header[1105];
+
+ int nr_samples; /* 16 or 32 samples (or 64 or ???) */
+ int slen, npat;
+
+ fprintf (stderr, "Loading .MED module: %s\n", name);
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (strncmp (header, "MMD0", 4))
+ {
+ fprintf (stderr, "Not a MED module\n");
+ close (mod_fd);
+ return 0;
+ }
+
+ printf ("Module len %d\n", intelize4 (*(long *) &header[4]));
+ printf ("Song info %d\n", intelize4 (*(long *) &header[8]));
+ printf ("Song len %d\n", intelize4 (*(long *) &header[12]));
+ printf ("Blockarr %x\n", intelize4 (*(long *) &header[16]));
+ printf ("Blockarr len %d\n", intelize4 (*(long *) &header[20]));
+ printf ("Sample array %x\n", intelize4 (*(long *) &header[24]));
+ printf ("Sample array len %d\n", intelize4 (*(long *) &header[28]));
+ printf ("Exp data %x\n", intelize4 (*(long *) &header[32]));
+ printf ("Exp size %d\n", intelize4 (*(long *) &header[36]));
+ printf ("Pstate %d\n", intelize (*(long *) &header[40]));
+ printf ("Pblock %d\n", intelize (*(long *) &header[42]));
+
+ return 0;
+
+ npat = header[33];
+ slen = 0;
+ tune_ptr = &header[48 + (31 * 32)];
+
+ for (i = 0; i < 64; i++)
+ {
+ tune[i] = tune_ptr[i];
+ if (tune[i] < npat)
+ slen = i;
+ }
+
+ fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
+
+ nr_samples = 31;
+
+ sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the
+ * first sample is
+ * stored */
+ total_mem = 0;
+
+ for (i = 0; i < 32; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end, base_freq;
+ unsigned short loop_flags = 0;
+
+ struct sample_header *sample;
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[48 + (i * 32)];
+
+ /*
+ * len = sample->length; loop_start = sample->loop_start; loop_end =
+ * sample->loop_end; base_freq = sample->C2_speed;
+ *
+ * if (strlen (sample->name) > 21) { fprintf (stderr, "\nInvalid name for
+ * sample #%d\n", i); close (mod_fd); return 0; }
+ */
+ if (len > 0)
+ {
+ int x;
+
+ if (loop_end > len)
+ loop_end = 1;
+
+ if (loop_end < loop_start)
+ {
+ loop_start = 0;
+ loop_end = 0;
+ }
+
+ if (loop_end > 2) loop_flags = WAVE_LOOPING;
+
+ total_mem += len;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_freq = base_freq;
+ patch->base_note = 261630; /* Mid C */
+ patch->low_note = 0;
+ patch->high_note = 0x7fffffff;
+ patch->volume = 120;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if ((x = read (mod_fd, patch->data, len)) != len)
+ {
+ fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+ /*
+ * fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n", i,
+ * len, loop_start, loop_end, base_freq, sample->name);
+ */
+ if (write (seqfd, patch, sizeof (*patch) + len) == -1)
+ {
+ perror ("ioctl /dev/sequencer");
+ exit (-1);
+ }
+ else
+ sample_ok[i] = 1;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = slen;
+ songlength = slen;
+ nr_channels = 4;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[64][4][4];
+ int pat, channel, x;
+
+ int pp = 1104 + (position * 1024);
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((x = read (mod_fd, patterns, 1024)) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
+ close (mod_fd);
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+
+ for (channel = 0; channel < 4; channel++)
+ {
+ unsigned char *p;
+
+ unsigned vol, note, octave, sample, effect, params;
+
+ p = &patterns[pat][channel][0];
+
+ if (p[0] < 251)
+ {
+ note = p[0] & 15;
+ octave = p[0] / 16;
+
+ note = 48 + octave * 12 + note;
+
+ sample = p[1] / 8;
+ vol = (p[1] & 7) + (p[2] / 2);
+ effect = p[2] & 0xF;
+ params = p[3];
+ }
+ else
+ {
+ note = 0;
+ octave = 0;
+
+ sample = 0;
+ vol = 0;
+ effect = CMD_NONOTE;
+ params = 0;
+ }
+
+ (*pattern_table[position])[channel][pat].note = note;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+
+ }
+
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+load_module (char *name)
+{
+
+ struct sample_header
+ {
+ char name[22];
+ unsigned short length; /* In words */
+
+ unsigned char finetune;
+ unsigned char volume;
+
+ unsigned short repeat_point; /* In words */
+ unsigned short repeat_length; /* In words */
+ };
+
+ int i, mod_fd, total_mem;
+ int sample_ptr, pattern_loc;
+
+ int position;
+
+ unsigned char *tune_ptr; /* array 0-127 */
+
+ char header[1084];
+
+ int nr_samples; /* 16 or 32 samples */
+ int slen, npat;
+ char mname[23];
+
+ ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);
+ ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);
+
+ clock_rate = 50.0;
+
+ for (i = 0; i < MAX_POSITION; i++)
+ pattern_len[i] = 64;
+
+ for (i = 0; i < MAX_POSITION; i++)
+ pattern_tempo[i] = 0;
+
+ if ((mod_fd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ return 0;
+ }
+
+ if (read (mod_fd, header, sizeof (header)) != sizeof (header))
+ {
+ fprintf (stderr, "%s: Short file (header)\n", name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (lseek (mod_fd, 0, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (header[28] == 0x1a)
+ return load_stm_module (mod_fd, name);
+
+ if (*(unsigned short *) &header[0] == 0x6669)
+ return load_669_module (mod_fd, name);
+
+ if (!strncmp (header, "MMD0", 4))
+ return load_mmd0_module (mod_fd, name);
+
+ fprintf (stderr, "Loading .MOD module: %s\n", name);
+
+ strncpy (mname, header, 22);
+ fprintf (stderr, "\nModule: %s - ", mname);
+
+ if (!strncmp (&header[1080], "M.K.", 4) || !strncmp (&header[1080], "FLT8", 4))
+ {
+ fprintf (stderr, "31 samples\n");
+ nr_samples = 31;
+ }
+ else
+ {
+ fprintf (stderr, "15 samples\n");
+ nr_samples = 15;
+ }
+
+ if (nr_samples == 31)
+ {
+ sample_ptr = pattern_loc = 1084;
+ slen = header[950];
+ tune_ptr = (unsigned char *) &header[952];
+ }
+ else
+ {
+ sample_ptr = pattern_loc = 600;
+ slen = header[470];
+ tune_ptr = (unsigned char *) &header[472];
+ }
+
+ npat = 0;
+ for (i = 0; i < 128; i++)
+ {
+ tune[i] = tune_ptr[i];
+
+ if (tune_ptr[i] > npat)
+ npat = tune_ptr[i];
+ }
+ npat++;
+
+ fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);
+
+ sample_ptr += (npat * 1024); /* Location where the first sample is stored */
+ total_mem = 0;
+
+ for (i = 0; i < 32; i++)
+ sample_ok[i] = 0;
+
+ for (i = 0; i < nr_samples; i++)
+ {
+ int len, loop_start, loop_end;
+ unsigned short loop_flags = 0;
+ char pname[22];
+
+ struct sample_header *sample;
+
+ struct patch_info *patch;
+
+ sample = (struct sample_header *) &header[20 + (i * 30)];
+
+ len = intelize (sample->length) * 2;
+ loop_start = intelize (sample->repeat_point) * 2;
+ loop_end = loop_start + (intelize (sample->repeat_length) * 2);
+
+ if (loop_start > len)
+ loop_start = 0;
+ if (loop_end > len)
+ loop_end = len;
+
+ if (loop_end <= loop_start)
+ loop_end = loop_start + 1;
+
+ if (loop_end > 2 && loop_end > loop_start)
+ loop_flags = WAVE_LOOPING;
+
+ strncpy (pname, sample->name, 20);
+
+ if (len > 0)
+ {
+ fprintf (stderr, "Sample %02d: L%05d, S%05d, E%05d V%02d %s\n",
+ i,
+ len,
+ loop_start,
+ loop_end,
+ sample->volume,
+ pname);
+
+ total_mem += len;
+
+ patch = (struct patch_info *) malloc (sizeof (*patch) + len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = i;
+ patch->mode = loop_flags;
+ patch->len = len;
+ patch->loop_start = loop_start;
+ patch->loop_end = loop_end;
+ patch->base_note = 261630; /* Middle C */
+ patch->base_freq = 8448;
+ patch->low_note = 0;
+ patch->high_note = 20000000;
+ patch->volume = 120;
+ patch->panning = 0;
+
+ if (lseek (mod_fd, sample_ptr, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ sample_ptr += len;
+
+ if (read (mod_fd, patch->data, len) != len)
+ {
+ fprintf (stderr, "Short file (sample) %d\n", sample_ptr);
+ close (mod_fd);
+ free (patch);
+ return 0;
+ }
+
+ SEQ_WRPATCH (patch, sizeof (*patch) + len);
+
+ sample_ok[i] = 1;
+ if (sample->volume == 0) sample->volume = 64;
+ sample_vol[i] = sample->volume;
+
+ free (patch);
+ }
+ }
+
+ nr_patterns = npat;
+ songlength = slen;
+ nr_channels = 4;
+
+ for (position = 0; position < npat; position++)
+ {
+ unsigned char patterns[64][4][4];
+ int pat, channel;
+
+ int pp = pattern_loc + (position * 1024);
+
+ if (lseek (mod_fd, pp, 0) == -1)
+ {
+ perror (name);
+ close (mod_fd);
+ return 0;
+ }
+
+ if (read (mod_fd, patterns, 1024) != 1024)
+ {
+ fprintf (stderr, "Short file (pattern %d) %d\n", tune[position], pp);
+ close (mod_fd);
+ return 0;
+ }
+
+ if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for a pattern\n");
+ return 0;
+ }
+
+ for (pat = 0; pat < 64; pat++)
+ {
+ for (channel = 0; channel < 4; channel++)
+ {
+ unsigned short tmp;
+ unsigned char *p;
+
+ unsigned period, sample, effect, params, note, vol;
+
+ p = &patterns[pat][channel][0];
+
+ tmp = (p[0] << 8) | p[1];
+ sample = (tmp >> 8) & 0x10;
+ period =
+ MIN (tmp & 0xFFF, 1023);
+ tmp = (p[2] << 8) | p[3];
+ sample |= tmp >> 12;
+ effect = (tmp >> 8) & 0xF;
+ params = tmp & 0xFF;
+
+ note = 0;
+
+ if (period)
+ {
+ /*
+ * Convert period to a Midi note number
+ */
+
+ for (note = 0; note < 37 && period != period_table[note]; note++);
+ if (note >= 37)
+ note = 0;
+
+ note += 48;
+ }
+
+ vol = 64;
+
+ if (sample)
+ if (effect == 0xc)
+ {
+ vol = params;
+ }
+ else
+ vol = sample_vol[sample - 1];
+
+ vol *= 2;
+ if (vol>64)vol--;
+
+ (*pattern_table[position])[channel][pat].note = note;
+ (*pattern_table[position])[channel][pat].sample = sample;
+ (*pattern_table[position])[channel][pat].command = effect;
+ (*pattern_table[position])[channel][pat].parm1 = params;
+ (*pattern_table[position])[channel][pat].parm2 = 0;
+ (*pattern_table[position])[channel][pat].vol = vol;
+ }
+ }
+ }
+
+ close (mod_fd);
+ return 1;
+}
+
+int
+panning (int ch)
+{
+ static int panning_tab[] =
+ {-110, 110, 110, -110};
+
+ return panning_tab[ch % 4];
+}
+
+void
+set_speed (int parm)
+{
+ if (!parm)
+ parm = 1;
+
+ if (parm < 32)
+ {
+ ticks_per_division = parm;
+ }
+ else
+ {
+ tick_duration = (60.0 / parm) * 10.0;
+ }
+
+}
+
+void
+play_module (char *name)
+{
+ int i, position, jump_to_pos;
+
+ init_voices ();
+
+ SEQ_START_TIMER ();
+#if 1
+ for (i=0;i<32;i++)
+ {
+ SEQ_EXPRESSION(gus_dev, i, 127);
+ SEQ_MAIN_VOLUME(gus_dev, i, 100);
+ }
+#endif
+ next_time = 0.0;
+
+ set_speed (6);
+
+ for (position = 0; position < songlength; position++)
+ {
+ int tick, pattern, channel, pos, go_to;
+
+ pos = tune[position];
+ if (pattern_tempo[position])
+ set_speed (pattern_tempo[position]);
+
+ jump_to_pos = -1;
+ for (pattern = 0; pattern < pattern_len[position] && jump_to_pos == -1; pattern++)
+ {
+ this_time = 0.0;
+
+ for (channel = 0; channel < nr_channels; channel++)
+ {
+ if ((go_to = play_note (channel, &(*pattern_table[pos])[channel][pattern])) != -1)
+ jump_to_pos = go_to;
+
+ }
+
+ next_time += tick_duration;
+
+ for (tick = 1; tick < ticks_per_division; tick++)
+ {
+ for (channel = 0; channel < nr_channels; channel++)
+ lets_play_voice (channel, &voices[channel]);
+ next_time += tick_duration;
+ }
+
+ }
+
+ if (jump_to_pos >= 0)
+ position = jump_to_pos;
+ }
+
+ SEQ_WAIT_TIME ((int) next_time + 200); /* Wait extra 2 secs */
+
+ for (i = 0; i < nr_channels; i++)
+ SEQ_STOP_NOTE (gus_dev, i, 0, 127);
+ SEQ_DUMPBUF ();
+
+ for (i = 0; i < nr_patterns; i++)
+ free (pattern_table[i]);
+}
+
+void
+sync_time ()
+{
+ if (next_time > this_time)
+ {
+ SEQ_WAIT_TIME ((long) next_time);
+ this_time = next_time;
+ }
+}
+
+void
+set_volslide (int channel, struct note_info *pat)
+{
+ int n;
+
+ voices[channel].volslide = 0;
+
+ if ((n = (pat->parm1 & 0xf0) >> 4))
+ voices[channel].volslide = n;
+ else
+ voices[channel].volslide = pat->parm1 & 0xf;
+}
+
+void
+set_slideto (int channel, struct note_info *pat)
+{
+ int size, rate, dir, range = 200;
+
+ rate = pat->parm1;
+ size = voices[channel].note - pat->note;
+ if (!size)
+ return;
+
+ if (size < 0)
+ {
+ size *= -1;
+ dir = -1;
+ }
+ else
+ dir = 1;
+
+ if (size > 2)
+ {
+ range = size * 100;
+ rate = rate * size / 200;
+ }
+
+ rate = pat->parm1 * dir / 30;
+ if (!rate)
+ rate = 1;
+
+ voices[channel].slide_pitch = 1;
+ voices[channel].slide_goal = (dir * 8192 * 200 * 2 / size) / range;
+ voices[channel].pitchbender = 0;
+ voices[channel].slide_rate = rate;
+ SEQ_BENDER_RANGE (gus_dev, channel, range);
+}
+
+int
+play_note (int channel, struct note_info *pat)
+{
+ int jump = -1;
+ int sample;
+
+ if (pat->sample == 0x3f)
+ pat->sample = 0;
+
+ if (pat->command == CMD_NONOTE)
+ return -1; /* Undefined */
+
+ sample = pat->sample;
+
+ if (sample && !pat->note)
+ {
+ pat->note = voices[channel].note;
+ }
+
+ if (sample)
+ voices[channel].sample = sample;
+ else
+ sample = voices[channel].sample;
+
+ sample--;
+
+ if (pat->note && pat->command != 3) /* Have a note -> play */
+ {
+ if (sample < 0)
+ sample = voices[channel].sample - 1;
+
+ if (!sample_ok[sample])
+ sample = voices[channel].sample - 1;
+
+ if (sample < 0)
+ sample = 0;
+
+ if (sample_ok[sample])
+ {
+ sync_time ();
+
+ if (pat->vol > 127) pat->vol=127;
+ SEQ_SET_PATCH (gus_dev, channel, sample);
+ SEQ_PANNING (gus_dev, channel, panning (channel));
+ SEQ_PITCHBEND (gus_dev, channel, 0);
+ SEQ_START_NOTE (gus_dev, channel, pat->note, pat->vol);
+
+ voices[channel].volume = pat->vol;
+ voices[channel].note = pat->note;
+ voices[channel].slide_pitch = 0;
+ }
+ else
+ SEQ_STOP_NOTE (gus_dev, channel, pat->note, pat->vol);
+ }
+
+ switch (pat->command)
+ {
+
+ case CMD_NOP:;
+ break;
+
+ case CMD_JUMP:
+ jump = pat->parm1;
+ break;
+
+ case CMD_BREAK:
+ jump = -2;
+ break;
+
+ case CMD_SPEED:
+ set_speed (pat->parm1);
+ break;
+
+ case CMD_SLIDEUP:
+ voices[channel].slide_pitch = 1;
+ voices[channel].slide_goal = 8191;
+ voices[channel].pitchbender = 0;
+ voices[channel].slide_rate = pat->parm1 * SLIDE_SIZE;
+ SEQ_BENDER_RANGE (gus_dev, channel, 200);
+ break;
+
+ case CMD_SLIDEDOWN:
+ voices[channel].slide_pitch = 1;
+ voices[channel].slide_goal = -8192;
+ voices[channel].pitchbender = 0;
+ voices[channel].slide_rate = -pat->parm1 * SLIDE_SIZE;
+ SEQ_BENDER_RANGE (gus_dev, channel, 200);
+ break;
+
+ case CMD_SLIDETO:
+ set_slideto (channel, pat);
+ break;
+
+ case CMD_VOLUME:
+ {
+ int vol = pat->parm1*2;
+ if (vol>127) vol=127;
+ if (pat->note && pat->command != 3)
+ break;
+ SEQ_START_NOTE (gus_dev, channel, 255, vol);
+ }
+ break;
+
+ case CMD_ARPEG:
+ break;
+
+ case 0x0e:
+ /* printf ("Cmd 0xE%02x\n", pat->parm1); */
+ break;
+
+ case CMD_VOLSLIDE:
+ set_slideto (channel, pat);
+ break;
+
+ default:
+ /* printf ("Command %x %02x\n", pat->command, pat->parm1); */
+ }
+
+ return jump;
+}
+
+void
+lets_play_voice (int channel, struct voice_info *v)
+{
+ if (v->slide_pitch)
+ {
+ v->pitchbender += v->slide_rate;
+ if (v->slide_goal < 0)
+ {
+ if (v->pitchbender <= v->slide_goal)
+ {
+ v->pitchbender = v->slide_goal;
+ v->slide_pitch = 0; /* Stop */
+ }
+ }
+ else
+ {
+ if (v->pitchbender >= v->slide_goal)
+ {
+ v->pitchbender = v->slide_goal;
+ v->slide_pitch = 0; /* Stop */
+ }
+ }
+
+ sync_time ();
+ SEQ_PITCHBEND (gus_dev, channel, v->pitchbender);
+ }
+
+ if (v->volslide)
+ {
+ v->volume += v->volslide;
+ sync_time ();
+
+ if (v->volume > 127) v->volume = 127;
+ SEQ_START_NOTE (gus_dev, channel, 255, v->volume);
+ }
+}
diff --git a/sys/i386/isa/sound/gustest/gpatinfo.c b/sys/i386/isa/sound/gustest/gpatinfo.c
new file mode 100644
index 0000000..3e21fc5
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gpatinfo.c
@@ -0,0 +1,176 @@
+/*
+ * gpatinfo.c: This program demonstrates the patch management
+ * interface of the GUS driver.
+ *
+ * NOTE! The patch manager interface is highly device dependent,
+ * currently incompletely implemented prototype and
+ * will change before final implementation.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/ultrasound.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "gmidi.h"
+
+#define GUS_DEV gus_dev
+
+#define patch_access(cmd, rec) \
+ rec.command = cmd;\
+ rec.device = gus_dev;\
+ if (ioctl(seqfd, SNDCTL_PMGR_IFACE, &rec)==-1)\
+ {\
+ perror("/dev/sequencer(SNDCTL_PMGR_IFACE/" #cmd ")");\
+ exit(-1);\
+ }
+
+SEQ_DEFINEBUF (2048);
+
+int seqfd;
+
+int gus_dev = -1;
+
+/*
+ * The function seqbuf_dump() must always be provided
+ */
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, j, n;
+ struct synth_info info;
+ struct patch_info *patch;
+ struct patmgr_info mgr, mgr2, mgr3;
+
+ if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+/*
+ * First locate the GUS device
+ */
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ printf("Gravis UltraSound device = %d\n", gus_dev);
+
+ /*
+ * Get type of the Patch Manager interface of the GUS device
+ */
+
+ patch_access(PM_GET_DEVTYPE, mgr);
+ printf("Patch manager type: %d\n", mgr.parm1);
+
+ if (mgr.parm1 != PMTYPE_WAVE)
+ {
+ fprintf(stderr, "Hups, this program seems to be obsolete\n");
+ exit(-1);
+ }
+
+ /*
+ * The GUS driver supports up to 256 different midi program numbers but
+ * this limit can be changed before compiling the driver. The following
+ * call returns the value compiled to the driver.
+ */
+
+ patch_access(PM_GET_PGMMAP, mgr);
+ printf("Device supports %d midi programs.\n", mgr.parm1);
+
+ /*
+ * Each program can be undefined or it may have one or more patches.
+ * A patch consists of header and the waveform data. If there is more
+ * than one patch in a program, the right one is selected by checking the
+ * note number when the program is played.
+ *
+ * The following call reads an array indexed by program number. Each
+ * element defines the number of patches defined for the corresponding
+ * program.
+ */
+ printf("Loaded programs:\n");
+
+ for (i=0;i<mgr.parm1;i++)
+ if (mgr.data.data8[i])
+ {
+ printf("%03d: %2d patches\n", i, mgr.data.data8[i]);
+
+ /*
+ * Next get the magic keys of the patches associated with this program.
+ * This key can be used to access the patc data.
+ */
+ mgr2.parm1=i;
+ patch_access(PM_GET_PGM_PATCHES, mgr2);
+ for (j = 0;j<mgr2.parm1;j++)
+ {
+ printf("\tPatch %d: %3d ", j, mgr2.data.data32[j]);
+
+ /*
+ * The last step is to read the patch header (without wave data).
+ * The header is returned in the mgr3.data. The field parm1 returns
+ * address of the wave data in tge GUS DRAM. Parm2 returns
+ * size of the struct patch_info in the kernel.
+ *
+ * There is also the PM_SET_PATCH call which allows modification of the
+ * header data. The only limitation is that the sample len cannot be
+ * increased.
+ */
+ mgr3.parm1 = mgr2.data.data32[j];
+ patch_access(PM_GET_PATCH, mgr3);
+ patch = (struct patch_info *)&mgr3.data; /* Pointer to the patch hdr */
+
+ printf("DRAM ptr = %7d, sample len =%6d bytes.\n",
+ mgr3.parm1, patch->len);
+
+ }
+ }
+
+ i = gus_dev;
+
+ if (ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &i)==-1) exit(-1);
+ printf("%d bytes of DRAM available for wave data\n", i);
+
+
+ exit(0);
+}
diff --git a/sys/i386/isa/sound/gustest/gusload.c b/sys/i386/isa/sound/gustest/gusload.c
new file mode 100644
index 0000000..216150a
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/gusload.c
@@ -0,0 +1,350 @@
+/*
+ * patutil.c - A sample program which loads patches to the Gravis
+ * Ultrasound
+ *
+ */
+
+#ifndef PATCH_PATH
+#define PATCH_PATH "/D/ultrasnd/midi"
+#endif
+
+#include <stdio.h>
+#include <sys/ultrasound.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "gmidi.h"
+
+struct pat_header
+ {
+ char magic[12];
+ char version[10];
+ char description[60];
+ unsigned char instruments;
+ char voices;
+ char channels;
+ unsigned short nr_waveforms;
+ unsigned short master_volume;
+ unsigned long data_size;
+ };
+
+struct sample_header
+ {
+ char name[7];
+ unsigned char fractions;
+ long len;
+ long loop_start;
+ long loop_end;
+ unsigned short base_freq;
+ long low_note;
+ long high_note;
+ long base_note;
+ short detune;
+ unsigned char panning;
+
+ unsigned char envelope_rate[6];
+ unsigned char envelope_offset[6];
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ char modes;
+
+ short scale_frequency;
+ unsigned short scale_factor;
+ };
+
+#define GUS_DEV gus_dev
+
+SEQ_DEFINEBUF (2048);
+
+int seqfd;
+
+int gus_dev = -1;
+
+struct patch_info *patch;
+
+/*
+ * The function seqbuf_dump() must always be provided
+ */
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, n, patfd, pgm, print_only = 0;
+ struct synth_info info;
+ struct pat_header header;
+ struct sample_header sample;
+ char buf[256];
+ char name[256];
+ long offset;
+
+ if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("/dev/sequencer");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ if (argc == 2)
+ {
+ if (!strcmp (argv[1], "reset"))
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ perror ("Sample reset");
+ exit (0);
+ }
+
+ if (argc != 3)
+ {
+ fprintf (stderr, "Usage: %s pgm# patchfile\n", argv[0]);
+ fprintf (stderr, " or : %s pgm# GM\n", argv[0]);
+ fprintf (stderr, " or : %s pgm# -l\n", argv[0]);
+ fprintf (stderr, " or : %s reset\n", argv[0]);
+ fprintf (stderr, " or : %s -l patchfile\n", argv[0]);
+ exit (-1);
+ }
+
+ pgm = atoi (argv[1]);
+ strcpy (name, argv[2]);
+
+ if (strcmp (name, "GM") == 0 || strcmp(name, "-l")==0)
+ {
+ if (strcmp (name, "-l") == 0) print_only = 1;
+ if (pgm < 0 || pgm > 127)
+ {
+ fprintf (stderr, "pgm# must be between 0 and 127\n");
+ exit (-1);
+ }
+
+ sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]);
+
+ if (!print_only)
+ fprintf (stderr, "Loading program %d from %s\n", pgm, name);
+ }
+ else if (strcmp (argv[1], "-l") == 0)
+ print_only = 1;
+
+ if ((patfd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ if (read (patfd, buf, 0xef) != 0xef)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ exit (-1);
+ }
+
+ memcpy ((char *) &header, buf, sizeof (header));
+
+ if (strncmp (header.magic, "GF1PATCH110", 12))
+ {
+ fprintf (stderr, "%s: Not a patch file\n", name);
+ exit (-1);
+ }
+
+ if (strncmp (header.version, "ID#000002", 10))
+ {
+ fprintf (stderr, "%s: Incompatible patch file version\n", name);
+ exit (-1);
+ }
+
+ header.nr_waveforms = *(unsigned short *) &buf[85];
+ header.master_volume = *(unsigned short *) &buf[87];
+
+ if (print_only)
+ {
+ printf ("Patch file: %s contains %d samples\n\n", name, header.nr_waveforms);
+ printf ("Master volume: %d\n", header.master_volume);
+ }
+
+ offset = 0xef;
+
+ for (i = 0; i < header.nr_waveforms; i++)
+ {
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ if (read (patfd, &buf, sizeof (sample)) != sizeof (sample))
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ exit (-1);
+ }
+
+ memcpy ((char *) &sample, buf, sizeof (sample));
+
+ /*
+ * Since some fields of the patch record are not 32bit aligned, we must
+ * handle them specially.
+ */
+ sample.low_note = *(long *) &buf[22];
+ sample.high_note = *(long *) &buf[26];
+ sample.base_note = *(long *) &buf[30];
+ sample.detune = *(short *) &buf[34];
+ sample.panning = (unsigned char) buf[36];
+
+ memcpy (sample.envelope_rate, &buf[37], 6);
+ memcpy (sample.envelope_offset, &buf[43], 6);
+
+ sample.tremolo_sweep = (unsigned char) buf[49];
+ sample.tremolo_rate = (unsigned char) buf[50];
+ sample.tremolo_depth = (unsigned char) buf[51];
+
+ sample.vibrato_sweep = (unsigned char) buf[52];
+ sample.vibrato_rate = (unsigned char) buf[53];
+ sample.vibrato_depth = (unsigned char) buf[54];
+ sample.modes = (unsigned char) buf[55];
+ sample.scale_frequency = *(short *) &buf[56];
+ sample.scale_factor = *(unsigned short *) &buf[58];
+
+ if (print_only)
+ {
+ printf("\nSample: %03d / %s\n", i, sample.name);
+ printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end);
+ printf ("Flags: ");
+ if (sample.modes & WAVE_16_BITS)
+ printf ("16 bit ");
+ if (sample.modes & WAVE_UNSIGNED)
+ printf ("unsigned ");
+ if (sample.modes & WAVE_LOOP_BACK)
+ printf("reverse ");
+ if (sample.modes & WAVE_BIDIR_LOOP)
+ printf("bidir ");
+ if (sample.modes & WAVE_LOOPING)
+ printf ("looping "); else printf("one_shot" );
+ if (sample.modes & WAVE_SUSTAIN_ON)
+ printf ("sustain ");
+ if (sample.modes & WAVE_ENVELOPES)
+ printf ("enveloped ");
+ printf ("\n");
+
+ if (sample.modes & WAVE_ENVELOPES)
+ {
+ int i;
+
+ printf ("Envelope info: ");
+ for (i = 0; i < 6; i++)
+ {
+ printf ("%d/%d ", sample.envelope_rate[i],
+ sample.envelope_offset[i]);
+ }
+ printf ("\n");
+ }
+
+ printf("Tremolo: sweep=%d, rate=%d, depth=%d\n",
+ sample.tremolo_sweep,
+ sample.tremolo_rate,
+ sample.tremolo_depth);
+
+ printf("Vibrato: sweep=%d, rate=%d, depth=%d\n",
+ sample.vibrato_sweep,
+ sample.vibrato_rate,
+ sample.vibrato_depth);
+ }
+
+ offset = offset + 96;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = GUS_DEV;
+ patch->instr_no = pgm;
+ patch->mode = sample.modes | WAVE_TREMOLO |
+ WAVE_VIBRATO | WAVE_SCALE;
+ patch->len = sample.len;
+ patch->loop_start = sample.loop_start;
+ patch->loop_end = sample.loop_end;
+ patch->base_note = sample.base_note;
+ patch->high_note = sample.high_note;
+ patch->low_note = sample.low_note;
+ patch->base_freq = sample.base_freq;
+ patch->detuning = sample.detune;
+ patch->panning = (sample.panning - 7) * 16;
+
+ memcpy (patch->env_rate, sample.envelope_rate, 6);
+ memcpy (patch->env_offset, sample.envelope_offset, 6);
+
+ patch->tremolo_sweep = sample.tremolo_sweep;
+ patch->tremolo_rate = sample.tremolo_rate;
+ patch->tremolo_depth = sample.tremolo_depth;
+
+ patch->vibrato_sweep = sample.vibrato_sweep;
+ patch->vibrato_rate = sample.vibrato_rate;
+ patch->vibrato_depth = sample.vibrato_depth;
+
+ patch->scale_frequency = sample.scale_frequency;
+ patch->scale_factor = sample.scale_factor;
+
+ patch->volume = header.master_volume;
+
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ if (!print_only)
+ {
+ if (read (patfd, patch->data, sample.len) != sample.len)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ exit (-1);
+ }
+
+ SEQ_WRPATCH (patch, sizeof (*patch) + sample.len);
+ }
+
+ offset = offset + sample.len;
+ }
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/gustest/midithru.c b/sys/i386/isa/sound/gustest/midithru.c
new file mode 100644
index 0000000..36e1e07
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/midithru.c
@@ -0,0 +1,325 @@
+#include <stdio.h>
+#include <sys/soundcard.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+
+SEQ_DEFINEBUF (2048);
+SEQ_PM_DEFINES;
+
+int seqfd, dev = 0;
+unsigned char buf[100];
+int bufp;
+
+/* LRU list for free operators */
+
+unsigned char free_list[256];
+int fhead=0, ftail=0, flen=0;
+
+/* LRU list for still playing notes */
+
+unsigned char note_list[256];
+int nhead=0, ntail=0, nlen=0;
+unsigned char oper_note[32];
+
+int pgm = 0;
+int num_voices;
+int bender = 0; /* Initially off */
+
+void
+seqbuf_dump ()
+{
+ if (_seqbufptr)
+ if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ {
+ perror ("write /dev/sequencer");
+ exit (-1);
+ }
+ _seqbufptr = 0;
+}
+
+void
+stop_note(int note, int velocity)
+{
+ int i, op;
+
+ op=255;
+
+ for (i=0;i<num_voices && op==255;i++)
+ {
+ if (oper_note[i]== note) op=i;
+ }
+
+ if (op==255)
+ {
+ fprintf(stderr, "Note %d off, note not started\n", note);
+ fprintf(stderr, "%d, %d\n", flen, nlen);
+ return; /* Has already been killed ??? */
+ }
+
+ SEQ_STOP_NOTE(dev, op, note, velocity);
+ SEQ_DUMPBUF();
+
+ oper_note[op] = 255;
+
+ free_list[ftail]=op;
+ flen++;
+ ftail = (ftail+1) % num_voices;
+
+ for (i=0;i<16;i++)
+ if (note_list[i] == op) note_list[i] = 255;
+
+ while (nlen && note_list[nhead] == 255)
+ {
+ nlen--;
+ /* printf("Remove from note queue %d, len %d\n", nhead, nlen); */
+ nhead = (nhead+1) % 256;
+ }
+}
+
+void
+kill_one_note()
+{
+ int oldest;
+
+ if (!nlen) {fprintf(stderr, "Free list empty but no notes playing\n");return;} /* No notes playing */
+
+ oldest = note_list[nhead];
+ nlen--;
+ nhead = (nhead+1) % 256;
+
+ fprintf(stderr, "Killing oper %d, note %d\n", oldest, oper_note[oldest]);
+
+ if (oldest== 255) return; /* Was already stopped. Why? */
+
+ stop_note(oper_note[oldest], 127);
+}
+
+void
+start_note(int note, int velocity)
+{
+ int free;
+
+ if (!flen) kill_one_note();
+
+ if (!flen) {printf("** no free voices\n");return;} /* Panic??? */
+
+ free = free_list[fhead];
+ flen--;
+ fhead = (fhead+1) % num_voices;
+
+ note_list[ntail] = free;
+
+ if (nlen>255)
+ {
+#if 0
+ fprintf(stderr, "Note list overflow %d, %d, %d\n",
+ nlen, nhead, ntail);
+#endif
+ nlen=0; /* Overflow -> hard reset */
+ }
+ nlen++;
+ ntail = (ntail+1) % 256;
+
+ oper_note[free] = note;
+
+ SEQ_SET_PATCH(dev, free, pgm);
+ SEQ_PITCHBEND(dev, free, bender);
+ SEQ_START_NOTE(dev, free, note, velocity);
+ SEQ_DUMPBUF();
+}
+
+void
+channel_pressure(int ch, int pressure)
+{
+ int i;
+
+ for (i=0;i<num_voices;i++)
+ {
+ if (oper_note[i] != 255)
+ {
+#if 1
+ SEQ_CHN_PRESSURE(dev, i, pressure);
+#else
+ SEQ_EXPRESSION(dev, i, pressure);
+#endif
+ SEQ_DUMPBUF();
+ }
+ }
+}
+
+void
+pitch_bender(int ch, int value)
+{
+ int i;
+
+ value -= 8192;
+
+ bender = value;
+
+ for (i=0;i<num_voices;i++)
+ {
+ if (oper_note[i] != 255)
+ {
+ bender = value;
+ SEQ_PITCHBEND(dev, i, value);
+ SEQ_DUMPBUF();
+ }
+ }
+}
+
+void
+do_buf()
+{
+ int ch = buf[0] & 0x0f;
+ int value;
+
+ switch (buf[0] & 0xf0)
+ {
+ case 0x90: /* Note on */
+ if (bufp < 3) break;
+ /* printf("Note on %d %d %d\n", ch, buf[1], buf[2]); */
+ if (buf[2])
+ start_note(buf[1], buf[2]);
+ else
+ stop_note(buf[1], buf[2]);
+ bufp=1;
+ break;
+
+ case 0xb0: /* Control change */
+ if (bufp < 3) break;
+ /* printf("Control change %d %d %d\n", ch, buf[1], buf[2]); */
+ bufp=1;
+ break;
+
+ case 0x80: /* Note off */
+ if (bufp < 3) break;
+ /* printf("Note off %d %d %d\n", ch, buf[1], buf[2]); */
+ stop_note(buf[1], buf[2]);
+ bufp=1;
+ break;
+
+ case 0xe0: /* Pitch bender */
+ if (bufp < 3) break;
+ value = ((buf[2] & 0x7f) << 7) | (buf[1] & 0x7f);
+ /* printf("Pitch bender %d %d\n", ch, value >> 7); */
+ pitch_bender(ch, value);
+ bufp=1;
+ break;
+
+ case 0xc0: /* Pgm change */
+ if (bufp < 2) break;
+ /* printf("Pgm change %d %d\n", ch, buf[1]); */
+ pgm = buf[1];
+ if (PM_LOAD_PATCH(dev, 0, pgm) < 0)
+ if (errno != ESRCH) /* No such process */
+ perror("PM_LOAD_PATCH");
+ bufp=0;
+ break;
+
+ case 0xd0: /* Channel pressure */
+ if (bufp < 2) break;
+ /* printf("Channel pressure %d %d\n", ch, buf[1]); */
+ channel_pressure(ch, buf[1]);
+ bufp=1;
+ break;
+
+ default:
+ bufp=0;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, n, max_voice = 999;
+
+ struct synth_info info;
+
+ unsigned char ev[4], *p;
+
+ if (argc >= 2) dev = atoi(argv[1]);
+
+ for (i=0;i<16;i++) oper_note[i] = 255;
+
+ if ((seqfd = open ("/dev/sequencer", O_RDWR, 0)) == -1)
+ {
+ perror ("open /dev/sequencer");
+ exit (-1);
+ }
+
+ if (argc >= 3)
+ {
+ int d = dev;
+ ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &d);
+ }
+
+ info.device = dev;
+
+ if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &info)==-1)
+ {
+ perror ("info /dev/sequencer");
+ exit (-1);
+ }
+
+ num_voices = info.nr_voices;
+ if (num_voices>max_voice)num_voices = max_voice;
+ fprintf(stderr, "Output to synth device %d (%s)\n", dev, info.name);
+ fprintf(stderr, "%d voices available\n", num_voices);
+
+ for (i=0;i<num_voices;i++)
+ {
+ flen++;
+ free_list[fhead] = i;
+ fhead = (fhead+1) % num_voices;
+ }
+
+ bufp = 0;
+ if (PM_LOAD_PATCH(dev, 0, 0) < 0) /* Load the default instrument */
+ if (errno != ESRCH) /* No such process */
+ perror("PM_LOAD_PATCH");
+
+ while (1)
+ {
+ if ((n = read (seqfd, ev, sizeof (ev))) == -1)
+ {
+ perror ("read /dev/sequencer");
+ exit (-1);
+ }
+
+ for (i = 0; i <= (n / 4); i++)
+ {
+ p = &ev[i * 4];
+
+ if (p[0] == SEQ_MIDIPUTC && p[2] == 0 /* Midi if# == 0 */)
+ {
+/* printf("%02x ", p[1]);fflush(stdout); */
+ if (p[1] & 0x80) /* Status */
+ {
+ if (bufp)
+ do_buf ();
+ buf[0] = p[1];
+ bufp = 1;
+ }
+ else if (bufp)
+ {
+ buf[bufp++] = p[1];
+ if ((buf[0] & 0xf0) == 0x90 || (buf[0] & 0xf0) == 0x80 || (buf[0] & 0xf0) == 0xb0 ||
+ (buf[0] & 0xf0) == 0xe0)
+ {
+ if (bufp == 3)
+ do_buf ();
+ }
+ else
+ if ((buf[0] & 0xf0) == 0xc0 || (buf[0] & 0xf0) == 0xd0)
+ {
+ if (bufp == 2) do_buf();
+ }
+ }
+ }
+ }
+ }
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/gustest/pmtest.c b/sys/i386/isa/sound/gustest/pmtest.c
new file mode 100644
index 0000000..3b6b1d5
--- /dev/null
+++ b/sys/i386/isa/sound/gustest/pmtest.c
@@ -0,0 +1,411 @@
+/*
+ * CAUTION! This program is just an incompletely implemented version
+ * of the patch manager daemon for GUS. Using this program
+ * with the driver version 1.99.9 will hang your system
+ * completely (sooner or later).
+ *
+ * This program is for information only. The final
+ * implementation of the patch manager will not be
+ * compatible with this one.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/ultrasound.h>
+#include <strings.h>
+#include <sys/errno.h>
+#include "gmidi.h"
+
+#ifndef PATCH_PATH
+#define PATCH_PATH "/D/ultrasnd/midi"
+#endif
+
+char loadmap[256] =
+{0}; /* 1 if the patch is already loaded */
+
+struct pat_header
+ {
+ char magic[12];
+ char version[10];
+ char description[60];
+ unsigned char instruments;
+ char voices;
+ char channels;
+ unsigned short nr_waveforms;
+ unsigned short master_volume;
+ unsigned long data_size;
+ };
+
+struct sample_header
+ {
+ char name[7];
+ unsigned char fractions;
+ long len;
+ long loop_start;
+ long loop_end;
+ unsigned short base_freq;
+ long low_note;
+ long high_note;
+ long base_note;
+ short detune;
+ unsigned char panning;
+
+ unsigned char envelope_rate[6];
+ unsigned char envelope_offset[6];
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ char modes;
+
+ short scale_frequency;
+ unsigned short scale_factor;
+ };
+int seqfd = 0, gus_dev = -1;
+
+struct patch_info *patch;
+
+int
+do_load_patch (struct patmgr_info *rec)
+{
+ int i, patfd, pgm, print_only = 0;
+ struct pat_header header;
+ struct sample_header sample;
+ char buf[256];
+ char name[256];
+ long offset;
+
+ pgm = rec->data.data8[0];
+
+ if (loadmap[pgm])
+ return 0; /* Already loaded */
+
+ sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]);
+
+ if ((patfd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ return errno;
+ }
+
+ if (read (patfd, buf, 0xef) != 0xef)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ return EIO;
+ }
+
+ memcpy ((char *) &header, buf, sizeof (header));
+
+ if (strncmp (header.magic, "GF1PATCH110", 12))
+ {
+ fprintf (stderr, "%s: Not a patch file\n", name);
+ return EINVAL;
+ }
+
+ if (strncmp (header.version, "ID#000002", 10))
+ {
+ fprintf (stderr, "%s: Incompatible patch file version\n", name);
+ return EINVAL;
+ }
+
+ header.nr_waveforms = *(unsigned short *) &buf[85];
+ header.master_volume = *(unsigned short *) &buf[87];
+
+ printf ("GUS: Loading: %s\n", name);
+
+ offset = 0xef;
+
+ for (i = 0; i < header.nr_waveforms; i++)
+ {
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ return errno;
+ }
+
+ if (read (patfd, &buf, sizeof (sample)) != sizeof (sample))
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ return EIO;
+ }
+
+ memcpy ((char *) &sample, buf, sizeof (sample));
+
+ /*
+ * Since some fields of the patch record are not 32bit aligned, we must
+ * handle them specially.
+ */
+ sample.low_note = *(long *) &buf[22];
+ sample.high_note = *(long *) &buf[26];
+ sample.base_note = *(long *) &buf[30];
+ sample.detune = *(short *) &buf[34];
+ sample.panning = (unsigned char) buf[36];
+
+ memcpy (sample.envelope_rate, &buf[37], 6);
+ memcpy (sample.envelope_offset, &buf[43], 6);
+
+ sample.tremolo_sweep = (unsigned char) buf[49];
+ sample.tremolo_rate = (unsigned char) buf[50];
+ sample.tremolo_depth = (unsigned char) buf[51];
+
+ sample.vibrato_sweep = (unsigned char) buf[52];
+ sample.vibrato_rate = (unsigned char) buf[53];
+ sample.vibrato_depth = (unsigned char) buf[54];
+ sample.modes = (unsigned char) buf[55];
+ sample.scale_frequency = *(short *) &buf[56];
+ sample.scale_factor = *(unsigned short *) &buf[58];
+
+ if (print_only)
+ {
+ printf ("\nSample: %03d / %s\n", i, sample.name);
+ printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end);
+ printf ("Flags: ");
+ if (sample.modes & WAVE_16_BITS)
+ printf ("16 bit ");
+ if (sample.modes & WAVE_UNSIGNED)
+ printf ("unsigned ");
+ if (sample.modes & WAVE_LOOP_BACK)
+ printf ("reverse ");
+ if (sample.modes & WAVE_BIDIR_LOOP)
+ printf ("bidir ");
+ if (sample.modes & WAVE_LOOPING)
+ printf ("looping ");
+ else
+ printf ("one_shot");
+ if (sample.modes & WAVE_SUSTAIN_ON)
+ printf ("sustain ");
+ if (sample.modes & WAVE_ENVELOPES)
+ printf ("enveloped ");
+ printf ("\n");
+
+ if (sample.modes & WAVE_ENVELOPES)
+ {
+ int i;
+
+ printf ("Envelope info: ");
+ for (i = 0; i < 6; i++)
+ {
+ printf ("%d/%d ", sample.envelope_rate[i],
+ sample.envelope_offset[i]);
+ }
+ printf ("\n");
+ }
+
+ printf ("Tremolo: sweep=%d, rate=%d, depth=%d\n",
+ sample.tremolo_sweep,
+ sample.tremolo_rate,
+ sample.tremolo_depth);
+
+ printf ("Vibrato: sweep=%d, rate=%d, depth=%d\n",
+ sample.vibrato_sweep,
+ sample.vibrato_rate,
+ sample.vibrato_depth);
+ }
+
+ offset = offset + 96;
+ patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len);
+
+ patch->key = GUS_PATCH;
+ patch->device_no = gus_dev;
+ patch->instr_no = pgm;
+ patch->mode = sample.modes | WAVE_TREMOLO |
+ WAVE_VIBRATO | WAVE_SCALE;
+ patch->len = sample.len;
+ patch->loop_start = sample.loop_start;
+ patch->loop_end = sample.loop_end;
+ patch->base_note = sample.base_note;
+ patch->high_note = sample.high_note;
+ patch->low_note = sample.low_note;
+ patch->base_freq = sample.base_freq;
+ patch->detuning = sample.detune;
+ patch->panning = (sample.panning - 7) * 16;
+
+ memcpy (patch->env_rate, sample.envelope_rate, 6);
+ memcpy (patch->env_offset, sample.envelope_offset, 6);
+
+ patch->tremolo_sweep = sample.tremolo_sweep;
+ patch->tremolo_rate = sample.tremolo_rate;
+ patch->tremolo_depth = sample.tremolo_depth;
+
+ patch->vibrato_sweep = sample.vibrato_sweep;
+ patch->vibrato_rate = sample.vibrato_rate;
+ patch->vibrato_depth = sample.vibrato_depth;
+
+ patch->scale_frequency = sample.scale_frequency;
+ patch->scale_factor = sample.scale_factor;
+
+ patch->volume = header.master_volume;
+
+ if (lseek (patfd, offset, 0) == -1)
+ {
+ perror (name);
+ return errno;
+ }
+
+ if (!print_only)
+ {
+ if (read (patfd, patch->data, sample.len) != sample.len)
+ {
+ fprintf (stderr, "%s: Short file\n", name);
+ return EIO;
+ }
+
+ if (write (seqfd, patch, sizeof (*patch) + sample.len) == -1)
+ {
+ perror ("/dev/pmgr0");
+ return errno;
+ }
+ }
+
+ offset = offset + sample.len;
+ }
+
+ loadmap[pgm] = 1;
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ struct patmgr_info inf;
+ int err, i, n;
+ struct synth_info info;
+
+ if ((seqfd = open ("/dev/patmgr0", O_RDWR, 0)) == -1)
+ {
+ fprintf (stderr, "Cannot open\n");
+ perror ("/dev/patmgr0");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
+ {
+ perror ("NRSYNTH: /dev/patmgr0");
+ exit (-1);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ info.device = i;
+
+ if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
+ {
+ perror ("SYNTH_INFO: /dev/patmgr0");
+ exit (-1);
+ }
+
+ if (info.synth_type == SYNTH_TYPE_SAMPLE
+ && info.synth_subtype == SAMPLE_TYPE_GUS)
+ gus_dev = i;
+ }
+
+ if (gus_dev == -1)
+ {
+ fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
+ exit (-1);
+ }
+
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ perror ("Sample reset");
+
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+
+ while (1)
+ {
+ if (read (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf))
+ {
+ perror ("Read");
+ exit (-1);
+ }
+
+ if (inf.key == PM_K_EVENT)
+ switch (inf.command)
+ {
+ case PM_E_OPENED:
+ printf ("Opened\n");
+ break;
+
+ case PM_E_CLOSED:
+ printf ("Closed\n");
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ perror ("Sample reset");
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+ break;
+
+ case PM_E_PATCH_RESET:
+ printf ("Patch reset called\n");
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+ break;
+
+ case PM_E_PATCH_LOADED:
+ printf ("Patch loaded by client\n");
+ break;
+
+ default:
+ printf ("Unknown event %d\n", inf.command);
+ inf.key = PM_ERROR;
+ inf.parm1 = EINVAL;
+ }
+ else if (inf.key == PM_K_COMMAND)
+ switch (inf.command)
+ {
+ case _PM_LOAD_PATCH:
+ if ((err = do_load_patch (&inf)))
+ if (err == ENOSPC)
+ {
+ if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
+ {
+ perror ("Sample reset");
+ return errno;
+ }
+
+ for (i = 0; i < 256; i++)
+ loadmap[i] = 0;
+ err = do_load_patch (&inf);
+ }
+
+ if (err)
+ {
+ inf.key = PM_ERROR;
+ inf.parm1 = err;
+ printf("Error = %d\n", err);
+ }
+ else
+ {
+ inf.key = PM_K_COMMAND;
+ inf.parm1 = 0;
+ }
+ break;
+
+ default:
+ printf ("Unknown command %d\n", inf.command);
+ inf.key = PM_ERROR;
+ inf.parm1 = EINVAL;
+ }
+ else
+ {
+ printf ("Unknown event %d/%d\n", inf.key, inf.command);
+ inf.key = PM_ERROR;
+ inf.parm1 = EINVAL;
+ }
+
+ if (write (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf))
+ {
+ perror ("write");
+ exit (-1);
+ }
+ }
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h
new file mode 100644
index 0000000..f705863
--- /dev/null
+++ b/sys/i386/isa/sound/local.h
@@ -0,0 +1,17 @@
+/* Generated by configure. Don't edit!!!! */
+
+#define KERNEL_SOUNDCARD
+#undef EXCLUDE_PAS
+#undef EXCLUDE_SB
+#undef EXCLUDE_ADLIB
+#undef EXCLUDE_GUS
+#define EXCLUDE_MPU401 /* Not ready yet */
+#undef EXCLUDE_SBPRO
+#undef EXCLUDE_AUDIO
+#undef EXCLUDE_MIDI
+#undef EXCLUDE_YM3812
+#undef EXCLUDE_SEQUENCER
+#undef EXCLUDE_CHIP_MIDI
+
+#define DSP_BUFFSIZE 32768
+#define SELECTED_SOUND_OPTIONS 0xffffffff
diff --git a/sys/i386/isa/sound/midi.c b/sys/i386/isa/sound/midi.c
new file mode 100644
index 0000000..83088d2
--- /dev/null
+++ b/sys/i386/isa/sound/midi.c
@@ -0,0 +1,176 @@
+/* UWM - comments to soft-eng@cs.uwm.edu */
+#define _MIDI_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_CHIP_MIDI
+
+
+static int generic_midi_busy[MAX_MIDI_DEV];
+
+long CMIDI_init (long mem_start)
+{
+
+ int i;
+ int n = num_midi_drivers;
+ /* int n = sizeof (midi_supported) / sizeof( struct generic_midi_info );
+ */
+ for (i = 0; i < n; i++)
+ {
+ if ( midi_supported[i].attach (mem_start) )
+ {
+ printk("MIDI: Successfully attached %s\n",midi_supported[i].name);
+ }
+
+ }
+ return (mem_start);
+}
+
+
+int
+CMIDI_open (int dev, struct fileinfo *file)
+{
+
+ int mode, err, retval;
+
+ dev = dev >> 4;
+
+ mode = file->mode & O_ACCMODE;
+
+
+ if (generic_midi_busy[dev])
+ return (RET_ERROR(EBUSY));
+
+
+ if (dev >= num_generic_midis)
+ {
+ printk(" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ if (!generic_midi_devs[dev])
+ {
+ printk(" MIDI device %d not initialized\n",dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ retval = generic_midi_devs[dev]->open (dev, mode) ;
+
+ /* If everything ok, set device as busy */
+
+ if ( retval >= 0 )
+ generic_midi_busy[dev] = 1;
+
+ return ( retval );
+
+}
+
+int
+CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk(" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /* Make double sure of healthiness -- doubt
+ * Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk(" MIDI device %d not initialized\n",dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ retval = generic_midi_devs[dev]->write (dev, buf);
+
+ return ( retval );
+
+}
+
+int
+CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count)
+{
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk(" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /* Make double sure of healthiness -- doubt
+ * Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk(" MIDI device %d not initialized\n",dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ retval = generic_midi_devs[dev]->read(dev,buf);
+
+ return (retval);
+
+}
+
+int
+CMIDI_close (int dev, struct fileinfo *file)
+{
+
+ int retval;
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk(" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /* Make double sure of healthiness -- doubt
+ * Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk(" MIDI device %d not initialized\n",dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
+
+
+ generic_midi_devs[dev]->close(dev);
+
+ generic_midi_busy[dev] = 0; /* Free the device */
+
+ return (0) ;
+
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c
new file mode 100644
index 0000000..ca1bdf7
--- /dev/null
+++ b/sys/i386/isa/sound/midibuf.c
@@ -0,0 +1,105 @@
+/*
+ * linux/kernel/chr_drv/sound/midibuf.c
+ *
+ * Device file manager for /dev/midi
+ *
+ * NOTE! This part of the driver is currently just a stub.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ *
+ * Based on the Midi driver for bsd386 by Mike Durian.
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MPU401)
+
+#if 0
+#include "midiioctl.h"
+#include "midivar.h"
+#endif
+
+static int midibuf_busy = 0;
+
+int
+MIDIbuf_open (int dev, struct fileinfo *file)
+{
+ int mode, err;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (midibuf_busy)
+ return RET_ERROR (EBUSY);
+
+ if (!mpu401_dev)
+ {
+ printk ("Midi: MPU-401 compatible Midi interface not present\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if ((err = midi_devs[mpu401_dev]->open (mpu401_dev, mode)) < 0)
+ return err;
+
+ midibuf_busy = 1;
+
+ return RET_ERROR (ENXIO);
+}
+
+void
+MIDIbuf_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ midi_devs[mpu401_dev]->close (mpu401_dev);
+ midibuf_busy = 0;
+}
+
+int
+MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ dev = dev >> 4;
+
+ return count;
+}
+
+
+int
+MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ dev = dev >> 4;
+
+ return RET_ERROR (EIO);
+}
+
+int
+MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+
+ default:
+ return midi_devs[0]->ioctl (dev, cmd, arg);
+ }
+}
+
+void
+MIDIbuf_bytes_received (int dev, unsigned char *buf, int count)
+{
+}
+
+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 0000000..6c3ef9a
--- /dev/null
+++ b/sys/i386/isa/sound/mpu401.c
@@ -0,0 +1,332 @@
+/*
+ * linux/kernel/chr_drv/sound/mpu401.c
+ *
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ *
+ * This version supports just the DUMB UART mode.
+ *
+ * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (mpu401_base)/* MPU-401 Data I/O Port on IBM */
+#define COMDPORT (mpu401_base+1) /* MPU-401 Command Port on IBM */
+#define STATPORT (mpu401_base+1) /* MPU-401 Status Port on IBM */
+
+#define mpu401_status() INB(STATPORT)
+#define input_avail() (!(mpu401_status()&INPUT_AVAIL))
+#define output_ready() (!(mpu401_status()&OUTPUT_READY))
+#define mpu401_cmd(cmd) OUTB(cmd, COMDPORT)
+#define mpu401_read() INB(DATAPORT)
+#define mpu401_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */
+#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */
+#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */
+#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */
+#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */
+
+static int mpu401_opened = 0;
+static int mpu401_base = 0x330;
+static int mpu401_irq;
+static int mpu401_detected = 0;
+static int my_dev;
+
+static int reset_mpu401 (void);
+
+static void
+mpu401_input_loop (void)
+{
+ int count;
+
+ count = 10;
+
+ while (count) /* Not timed out */
+ if (input_avail ())
+ {
+ unsigned char c = mpu401_read ();
+
+ count = 100;
+
+ if (mpu401_opened & OPEN_READ)
+ sequencer_midi_input (my_dev, c);
+ }
+ else
+ while (!input_avail () && count)
+ count--;
+}
+
+void
+mpuintr (int unit)
+{
+ unsigned char c;
+
+ if (input_avail ())
+ mpu401_input_loop ();
+}
+
+/*
+ * It looks like there is no input interrupts in the UART mode. Let's try
+ * polling.
+ */
+
+static void
+poll_mpu401 (unsigned long dummy)
+{
+ unsigned long flags;
+
+ static struct timer_list mpu401_timer =
+ {NULL, 0, 0, poll_mpu401};
+
+ if (!(mpu401_opened & OPEN_READ))
+ return; /* No longer required */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ mpu401_input_loop ();
+
+ mpu401_timer.expires = 1;
+ add_timer (&mpu401_timer); /* Come back later */
+
+ RESTORE_INTR (flags);
+}
+
+static int
+set_mpu401_irq (int interrupt_level)
+{
+ int retcode;
+
+#ifdef linux
+ struct sigaction sa;
+
+ sa.sa_handler = mpuintr;
+
+#ifdef SND_SA_INTERRUPT
+ sa.sa_flags = SA_INTERRUPT;
+#else
+ sa.sa_flags = 0;
+#endif
+
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ retcode = irqaction (interrupt_level, &sa);
+
+ if (retcode < 0)
+ {
+ printk ("MPU-401: IRQ%d already in use\n", interrupt_level);
+ }
+
+#else
+ /* # error Unimplemented for this OS */
+#endif
+ return retcode;
+}
+
+static int
+mpu401_open (int dev, int mode)
+{
+ if (mpu401_opened)
+ {
+ printk ("MPU-401: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ mpu401_input_loop ();
+
+ mpu401_opened = mode;
+ poll_mpu401 (0); /* Enable input polling */
+
+ return 0;
+}
+
+static void
+mpu401_close (int dev)
+{
+ mpu401_opened = 0;
+}
+
+static int
+mpu401_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 ())
+ mpu401_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;
+ }
+
+ mpu401_write (midi_byte);
+ return 1;
+}
+
+static int
+mpu401_command (int dev, unsigned char midi_byte)
+{
+ return 1;
+}
+
+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)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+mpu401_kick (int dev)
+{
+}
+
+static int
+mpu401_buffer_status (int dev)
+{
+ return 0; /* No data in buffers */
+}
+
+static struct midi_operations mpu401_operations =
+{
+ {"MPU-401", 0},
+ mpu401_open,
+ mpu401_close,
+ mpu401_ioctl,
+ mpu401_out,
+ mpu401_start_read,
+ mpu401_end_read,
+ mpu401_kick,
+ mpu401_command,
+ mpu401_buffer_status
+};
+
+
+long
+attach_mpu401 (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ mpu401_base = hw_config->io_base;
+ mpu401_irq = hw_config->irq;
+
+ if (!mpu401_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ mpu401_cmd (UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (mpu401_read () == MPU_ACK)
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+ printk (" <Roland MPU-401>");
+
+ my_dev = num_midis;
+ mpu401_dev = num_midis;
+ midi_devs[num_midis++] = &mpu401_operations;
+ return mem_start;
+}
+
+static int
+reset_mpu401 (void)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+
+ /*
+ * Send the RESET command. Try twice 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 */
+ mpu401_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 (mpu401_read () == MPU_ACK)
+ ok = 1;
+
+ }
+
+ mpu401_opened = 0;
+ if (ok)
+ mpu401_input_loop (); /* Flush input before enabling interrupts */
+
+ RESTORE_INTR (flags);
+
+ return ok;
+}
+
+
+int
+probe_mpu401 (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ mpu401_base = hw_config->io_base;
+ mpu401_irq = hw_config->irq;
+
+ if (set_mpu401_irq (mpu401_irq) < 0)
+ return 0;
+
+ ok = reset_mpu401 ();
+
+ mpu401_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c
new file mode 100644
index 0000000..c0d70e2
--- /dev/null
+++ b/sys/i386/isa/sound/opl3.c
@@ -0,0 +1,913 @@
+/*
+ * linux/kernel/chr_drv/sound/opl3.c
+ *
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+/* 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];
+
+typedef struct sbi_instrument instr_array[SBFM_MAXINSTR];
+static instr_array instrmap;
+static struct sbi_instrument *active_instrument[MAX_VOICE] =
+{NULL};
+
+static struct synth_info fm_info =
+{"AdLib", 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, const unsigned char addr, const unsigned char val);
+static int opl3_kill_note (int dev, int voice, int velocity);
+static unsigned char connection_mask = 0x00;
+
+void
+enable_opl3_mode (int left, int right, int both)
+{
+ 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;
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f); /* Select all 4-OP
+ * voices */
+ 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];
+ nr_voices = 6;
+}
+
+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;
+
+ 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 */
+ }
+
+ 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 velocity)
+{
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 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%04x\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;
+ 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 */
+ /* Connect the voice to both stereo channels */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, instr->operators[10] | 0x30);
+
+ /*
+ * 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 */
+ /* Connect the voice to both stereo channels */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, instr->operators[OFFS_4OP + 10] | 0x30);
+ }
+
+ 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, const unsigned char addr, const unsigned char 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 (addr, io_addr); /* Select register */
+
+ if (!opl3_enabled)
+ tenmicrosec ();
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+
+ OUTB (val, 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); /* OP1 volume to min */
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); /* OP2 volume to min */
+
+ if (physical_voices[logical_voices[i]].voice_mode == 4) /* 4 OP voice */
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); /* OP3 volume to min */
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); /* OP4 volume to min */
+ }
+
+ opl3_kill_note (dev, i, 64);
+ }
+
+ if (opl3_enabled)
+ {
+ 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;
+ 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)
+{
+}
+
+#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
+opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+ unsigned char data;
+ int block, fnum, freq;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ 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);
+ 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 struct synth_operations opl3_operations =
+{
+ &fm_info,
+ 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_patchmgr
+};
+
+long
+opl3_init (long mem_start)
+{
+ int i;
+
+ synth_devs[num_synths++] = &opl3_operations;
+ fm_model = 0;
+ opl3_ok = 1;
+ if (opl3_enabled)
+ {
+ printk (" <Yamaha OPL-3 FM>");
+ fm_model = 2;
+ nr_voices = 18;
+ fm_info.nr_drums = 0;
+ fm_info.capabilities |= SYNTH_CAP_OPL3;
+ strcpy (fm_info.name, "Yamaha OPL-3");
+
+ 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;
+ 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;
+
+ printk("\n");
+
+ 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 0000000..b577af7
--- /dev/null
+++ b/sys/i386/isa/sound/opl3.h
@@ -0,0 +1,263 @@
+/*
+ * 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 REGENTS 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 REGENTS 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 0000000..2ea9e5a
--- /dev/null
+++ b/sys/i386/isa/sound/os.h
@@ -0,0 +1,273 @@
+#ifndef _OS_H_
+#define _OS_H_
+/*
+ * OS specific settings for FreeBSD
+ *
+ * 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 FreeBSD (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
+
+/* lbolt is required by the FreeBSD version (only???) */
+extern int __timeout_val;
+extern int __process_aborting;
+
+/*
+ * 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.
+ */
+
+/*
+ * 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 int 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 int flag = {0}
+/*
+ * 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 FreeBSD.
+ */
+#define PROCESS_ABORTING (__process_aborting | curproc->p_sig)
+/*
+ * REQUEST_TIMEOUT is called before sleep. It shoud ensure that the
+ * process is woken up after given number of ticks (1/HZ secs.).
+ * The wqueue gives the wait queue.
+ */
+#define REQUEST_TIMEOUT(nticks, wqueue) __timeout_val = nticks;
+
+/*
+ * 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()
+ * The second parameter is a flag. It must be initialized to 1 before sleep
+ * and to zero after proces continues.
+ */
+#define INTERRUPTIBLE_SLEEP_ON(on_what, flag) \
+ { \
+ flag = 1; \
+ flag=tsleep(&(on_what), (PRIBIO-5)|PCATCH, "sndint", __timeout_val); \
+ if(flag == ERESTART) __process_aborting = 1;\
+ else __process_aborting = 0;\
+ __timeout_val = 0; \
+ flag = 0; \
+ }
+
+/* An the following wakes up a process */
+#define WAKE_UP(who) wakeup(&(who))
+
+/*
+ * 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...
+ *
+ */
+#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
+#define OUTB(addr, data) outb(data, addr)
+
+/* 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 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 0000000..4dadea3
--- /dev/null
+++ b/sys/i386/isa/sound/pas.h
@@ -0,0 +1,249 @@
+/* */
+/* 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
+
+#ifdef DEFINE_TRANSLATIONS
+ char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
+ { 4, 1, 2, 3, 0, 5, 6, 7 };
+ char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
+ char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
+ { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x05, 0x06, 0x07 };
+ char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
+ { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x28, 0x30, 0x38 };
+ char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
+ { 0x00, 0x40, 0x80, 0xC0 };
+ 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, 3, 0, 2, 3 };
+#else
+ extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */
+ extern char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */
+ extern char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */
+ extern char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */
+ extern char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */
+ extern 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 0000000..19df65d
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_card.c
@@ -0,0 +1,343 @@
+#define _PAS2_CARD_C_
+#define SND_SA_INTERRUPT
+/*
+ * linux/kernel/chr_drv/sound/pas2_card.c
+ *
+ * Detection routine for the Pro Audio Spectrum cards.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz
+ * (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be
+ * distributed with this file.
+ */
+
+#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"};
+
+/* 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;
+ }
+
+}
+
+static int
+set_pas_irq (int interrupt_level)
+{
+#ifdef linux
+ int retcode;
+ struct sigaction sa;
+
+ pas_write (0xff, INTERRUPT_STATUS); /* Reset pending interrupts */
+
+ sa.sa_handler = pasintr;
+
+#ifdef SND_SA_INTERRUPT
+ sa.sa_flags = SA_INTERRUPT;
+#else
+ sa.sa_flags = 0;
+#endif
+
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ retcode = irqaction (interrupt_level, &sa);
+
+ if (retcode < 0)
+ {
+ printk ("ProAudioSpectrum: IRQ%d already in use\n", interrupt_level);
+ }
+ return retcode;
+#else
+ /* # error This routine does not work with this OS */
+#endif
+}
+
+int
+pas_set_intr (int mask)
+{
+ int err;
+
+ if (!mask)
+ return 0;
+
+ if (!pas_intr_mask)
+ {
+ if ((err = set_pas_irq (pas_irq)) < 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)
+ {
+ 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;
+
+ 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
+ {
+ pas_write (I_C_3_PCM_IRQ_translate[pas_irq], 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;
+ }
+ }
+
+#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(S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); Don't do this */
+ 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_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)
+
+ /* Turn on Sound Blaster compatibility */
+ /* bit 1 = SB emulation */
+ /* bit 0 = MPU401 emulation (CDPC only :-( ) */
+ pas_write (0x02, COMPATIBILITY_ENABLE);
+
+ /* "Emulation address" */
+ pas_write ((SBC_BASE >> 4) & 0x0f, EMULATION_ADDRESS);
+#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;
+
+ if ((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 ();
+ }
+ }
+
+ printk("\n");
+ 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 0000000..a5fd604
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_midi.c
@@ -0,0 +1,269 @@
+/*
+ * linux/kernel/chr_drv/sound/pas2_midi.c
+ *
+ * The low level driver for the PAS Midi Interface.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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 int
+pas_midi_open (int dev, int mode)
+{
+ 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;
+
+ 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;
+}
+
+static struct midi_operations pas_midi_operations =
+{
+ {"Pro Audio Spectrum", 0},
+ 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
+};
+
+long
+pas_midi_init (long mem_start)
+{
+ 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)
+ {
+ sequencer_midi_input (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 %02x,%02x,%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 0000000..3aa2072
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_mixer.c
@@ -0,0 +1,481 @@
+#define _PAS2_MIXER_C_
+
+/*
+ * linux/kernel/chr_drv/sound/pas2_mixer.c
+ *
+ * Mixer routines for the Pro Audio Spectrum cards.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz
+ * (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be
+ * distributed with this file.
+ */
+
+#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
+mixer_set_levels (struct sb_mixer_levels *user_l)
+{
+#define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15))
+
+ struct sb_mixer_levels l;
+
+ IOCTL_FROM_USER ((char *) &l, (char *) user_l, 0, sizeof (l));
+
+ if (l.master.l & ~0xF || l.master.r & ~0xF
+ || l.line.l & ~0xF || l.line.r & ~0xF
+ || l.voc.l & ~0xF || l.voc.r & ~0xF
+ || l.fm.l & ~0xF || l.fm.r & ~0xF
+ || l.cd.l & ~0xF || l.cd.r & ~0xF
+ || l.mic & ~0x7)
+ return (RET_ERROR (EINVAL));
+
+ pas_mixer_set (SOUND_MIXER_VOLUME, cmix (l.master));
+ pas_mixer_set (SOUND_MIXER_LINE, cmix (l.line));
+ pas_mixer_set (SOUND_MIXER_PCM, cmix (l.voc));
+ pas_mixer_set (SOUND_MIXER_ALTPCM, cmix (l.voc));
+ pas_mixer_set (SOUND_MIXER_SYNTH, cmix (l.fm));
+ pas_mixer_set (SOUND_MIXER_CD, cmix (l.cd));
+ pas_mixer_set (SOUND_MIXER_MIC, ((l.mic * 100 + 3) / 7) | (((l.mic * 100 + 3) / 7) << 8));
+ return (0);
+}
+
+/*
+ * This sets aspects of the Mixer that are not volume levels. (Recording
+ * source, filter level, I/O filtering, and stereo.)
+ */
+static int
+mixer_set_params (struct sb_mixer_params *user_p)
+{
+ struct sb_mixer_params p;
+ S_BYTE val;
+ int src;
+ unsigned long flags;
+
+ IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
+
+ if (p.record_source != SRC_MIC
+ && p.record_source != SRC_CD
+ && p.record_source != SRC_LINE)
+ return (RET_ERROR (EINVAL));
+
+ /*
+ * I'm not sure if this is The Right Thing. Should stereo be entirely
+ * under control of DSP? I like being able to toggle it while a sound is
+ * playing, so I do this... because I can.
+ */
+
+ DISABLE_INTR (flags);
+
+ val = (pas_read (PCM_CONTROL) & ~P_C_MIXER_CROSS_FIELD) | P_C_MIXER_CROSS_R_TO_R | P_C_MIXER_CROSS_L_TO_L;
+ if (!p.dsp_stereo)
+ val |= (P_C_MIXER_CROSS_R_TO_L | P_C_MIXER_CROSS_L_TO_R); /* Mono */
+ pas_write (val, PCM_CONTROL);
+
+ RESTORE_INTR (flags);
+
+ switch (p.record_source)
+ {
+ case SRC_CD:
+ src = SOUND_MASK_CD;
+ break;
+
+ case SRC_LINE:
+ src = SOUND_MASK_LINE;
+ break;
+
+ default:
+ src = SOUND_MASK_MIC;
+ break;
+ }
+
+ pas_mixer_set (SOUND_MIXER_RECSRC, src);
+
+ /*
+ * setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) |
+ * (p.filter_output ? FILT_ON : FILT_OFF)));
+ */
+ return (0);
+}
+
+static int
+getmixer (int dev, int chn)
+{
+ if (chn == P_M_MV508_RIGHT)
+ {
+ return (levels[dev] >> 8) & 0x7f;
+ }
+ else
+ {
+ return levels[dev] & 0x7f;
+ }
+}
+
+/* Read the current mixer level settings into the user's struct. */
+static int
+mixer_get_levels (struct sb_mixer_levels *user_l)
+{
+
+ struct sb_mixer_levels l;
+
+ l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100; /* Master */
+ l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100; /* Master */
+
+ l.line.r = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_RIGHT) * 15) + 50) / 100; /* Line */
+ l.line.l = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.voc.r = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_RIGHT) * 15) + 50) / 100; /* DAC */
+ l.voc.l = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.fm.r = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_RIGHT) * 15) + 50) / 100; /* FM */
+ l.fm.l = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.cd.r = ((getmixer (SOUND_MIXER_CD, P_M_MV508_RIGHT) * 15) + 50) / 100; /* CD */
+ l.cd.l = ((getmixer (SOUND_MIXER_CD, P_M_MV508_LEFT) * 15) + 50) / 100;
+
+ l.mic = ((getmixer (SOUND_MIXER_MIC, P_M_MV508_LEFT) * 7) + 50) / 100; /* Microphone */
+
+ IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
+ return (0);
+}
+
+/* Read the current mixer parameters into the user's struct. */
+static int
+mixer_get_params (struct sb_mixer_params *user_params)
+{
+ S_BYTE val;
+ struct sb_mixer_params params;
+
+ switch (rec_devices)
+ {
+ case SOUND_MASK_CD:
+ params.record_source = SRC_CD;
+ break;
+
+ case SOUND_MASK_LINE:
+ params.record_source = SRC_LINE;
+ break;
+
+ case SOUND_MASK_MIC:
+ params.record_source = SRC_MIC;
+ break;
+
+ default:
+ params.record_source = SRC_MIC;
+ pas_mixer_set (SOUND_MIXER_RECSRC, SOUND_MASK_MIC); /* Adjust */
+ }
+
+ params.hifreq_filter = OFF;
+ params.filter_input = OFF;
+ params.filter_output = OFF;
+
+ val = INB (PCM_CONTROL);
+ params.dsp_stereo = ((val & P_C_MIXER_CROSS_FIELD) == (P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R));
+
+ IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
+ return (0);
+}
+
+/*****/
+
+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]);
+ }
+ }
+ }
+ else
+ {
+ switch (cmd)
+ {
+ case MIXER_IOCTL_SET_LEVELS:
+ mixer_set_levels ((struct sb_mixer_levels *) arg);
+ return mixer_get_levels ((struct sb_mixer_levels *) arg);
+ case MIXER_IOCTL_SET_PARAMS:
+ mixer_set_params ((struct sb_mixer_params *) arg);
+ return mixer_get_params ((struct sb_mixer_params *) arg);
+ case MIXER_IOCTL_READ_LEVELS:
+ return mixer_get_levels ((struct sb_mixer_levels *) arg);
+ case MIXER_IOCTL_READ_PARAMS:
+ return mixer_get_params ((struct sb_mixer_params *) arg);
+ case MIXER_IOCTL_RESET:
+ pas_mixer_reset ();
+ return (0);
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+ pas_mixer_ioctl
+};
+
+int
+pas_init_mixer (void)
+{
+ pas_mixer_reset ();
+
+ 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 0000000..f4634c5
--- /dev/null
+++ b/sys/i386/isa/sound/pas2_pcm.c
@@ -0,0 +1,412 @@
+#define _PAS2_PCM_C_
+/*
+ * linux/kernel/chr_drv/sound/pas2_pcm.c
+ *
+ * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz
+ * (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be
+ * distributed with this file.
+ */
+
+#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/sample (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;
+ arg = 1193180 / foo;
+
+ if (pcm_channels & 2)
+ foo = foo >> 1;
+
+ pcm_speed = arg;
+
+ tmp = pas_read (FILTER_FREQUENCY);
+
+ 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:
+ 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_SAMPLESIZE:
+ 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 (mode != OPEN_READ && mode != OPEN_WRITE)
+ {
+ printk ("PAS2: Attempt to open PCM device for simultaneous read and write");
+ return RET_ERROR (EINVAL);
+ }
+
+ if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
+ return err;
+
+ if (!DMAbuf_open_dma (dev))
+ {
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ return RET_ERROR (EBUSY);
+ }
+
+ pcm_count = 0;
+
+ pcm_set_bits (8);
+ pcm_set_channels (1);
+ pcm_set_speed (DSP_DEFAULT_SPEED);
+
+ 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)
+{
+ 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 (sound_dsp_dmachan[dev] > 3)
+ cnt >>= 1;
+ cnt--;
+
+ if (sound_dma_automode[dev] &&
+ 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);
+
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+ count--;
+
+ 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)
+{
+ 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 (sound_dsp_dmachan[dev] > 3)
+ cnt >>= 1;
+ cnt--;
+
+ if (sound_dma_automode[my_devnum] &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /* Auto mode on. No need to react */
+
+ DISABLE_INTR (flags);
+
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+
+ count--;
+
+ 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",
+ 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, /* halt_xfer */
+ NULL, /* has_output_drained */
+ NULL /* copy_from_user */
+};
+
+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_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations;
+ sound_dsp_dmachan[my_devnum] = hw_config->dma;
+ if (hw_config->dma > 3)
+ {
+ sound_buffcounts[my_devnum] = 1;
+ sound_buffsizes[my_devnum] = 2 * 65536;
+ sound_dma_automode[my_devnum] = 1;
+ }
+ else
+ {
+ sound_buffcounts[my_devnum] = 1;
+ sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
+ sound_dma_automode[my_devnum] = 1;
+ }
+ }
+ 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 (!sound_dma_automode[my_devnum])
+ {
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+ }
+
+ switch (pcm_mode)
+ {
+
+ case PCM_DAC:
+ DMAbuf_outputintr (my_devnum);
+ 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 0000000..48c1004
--- /dev/null
+++ b/sys/i386/isa/sound/patmgr.c
@@ -0,0 +1,239 @@
+/*
+ * linux/kernel/chr_drv/sound/patmgr.c
+ *
+ * The patch maneger interface for the /dev/sequencer
+ *
+ * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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;
+
+ 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 (appl_wait_flag)
+ WAKE_UP (appl_proc);
+ }
+
+ 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)
+ {
+ DISABLE_INTR (flags);
+
+ while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && !PROCESS_ABORTING)
+ {
+ INTERRUPTIBLE_SLEEP_ON (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ 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 (appl_wait_flag)
+ {
+ WAKE_UP (appl_proc);
+ }
+ }
+
+ 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 (server_wait_flag[dev])
+ {
+ WAKE_UP (server_procs[dev]);
+ }
+
+ INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag);
+
+ 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 (server_wait_flag[dev])
+ {
+ WAKE_UP (server_procs[dev]);
+ }
+
+ INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag);
+ 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/pro_midi.c b/sys/i386/isa/sound/pro_midi.c
new file mode 100644
index 0000000..b7f6b9a
--- /dev/null
+++ b/sys/i386/isa/sound/pro_midi.c
@@ -0,0 +1,155 @@
+/* UWM -- comments to soft-eng@cs.uwm.edu */
+#define ALL_EXTERNAL_TO_ME
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+#define ESUCCESS 0
+
+#if !defined(EXCLUDE_PRO_MIDI) && !defined(EXCLUDE_CHIP_MIDI)
+
+
+/** Structure for handling operations **/
+
+
+static struct generic_midi_operations pro_midi_operations = {
+
+ {"Pro_Audio_Spectrum 16 MV101", 0},
+ pro_midi_open,
+ pro_midi_close,
+ pro_midi_write,
+ pro_midi_read
+};
+
+/*
+ * Note! Note! Note!
+ * Follow the same model for any other attach function you
+ * may write
+ */
+
+long pro_midi_attach( long mem_start)
+{
+ pro_midi_dev = num_generic_midis;
+ generic_midi_devs[num_generic_midis++] = &pro_midi_operations;
+ return mem_start;
+}
+
+int pro_midi_open(int dev, int mode)
+{
+
+ int intr_mask, s;
+
+
+ s = splhigh();
+
+
+ /* Reset the input and output FIFO pointers */
+
+
+ outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
+
+ /* Get the interrupt status */
+
+ intr_mask = inb(INTERRUPT_MASK);
+
+
+ /* Enable MIDI IRQ */
+
+ intr_mask |= I_M_MIDI_IRQ_ENABLE;
+ outb(INTERRUPT_MASK, intr_mask);
+
+
+ /* Enable READ/WRITE on MIDI port. This part is quite unsure though */
+
+ outb(MIDI_CONTROL,M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ);
+
+ /* Acknowledge pending interrupts */
+
+ outb(MIDI_STATUS,0xff);
+
+
+ splx(s);
+
+ return(ESUCCESS);
+
+
+}
+
+
+void pro_midi_close(int dev)
+{
+
+ int intr_mask;
+
+ /* Clean up */
+
+ outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
+ intr_mask = inb(INTERRUPT_MASK);
+ intr_mask &= ~I_M_MIDI_IRQ_ENABLE;
+ outb(INTERRUPT_MASK,intr_mask);
+
+ return;
+}
+
+int pro_midi_write(int dev, struct uio *uio)
+{
+
+ int s;
+ unsigned char data;
+
+ /* printf("midi: Going to do write routine..\n"); */
+ while(uio->uio_resid) {
+
+ if ( uiomove(&data,1,uio) ) return(ENOTTY);
+
+ s = splhigh();
+
+ DELAY(30);
+ outb(MIDI_DATA,data);
+ DELAY(70); /* Ze best pause.. find a better one if
+ * you can :)
+ */
+ splx(s);
+ }
+
+ return(ESUCCESS);
+
+}
+
+
+int pro_midi_read(int dev, struct uio *uio)
+{
+
+ int s;
+ unsigned char data;
+
+ s = splhigh();
+
+ /* For each uio_iov[] entry .... */
+
+ while (uio->uio_resid) {
+
+ if((( inb(MIDI_STATUS) & M_S_INPUT_AVAIL) == 0 ) &&
+ ((inb(MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0 ) )
+
+ data = 0xfe;
+ else
+ data = inb(MIDI_DATA);
+
+ if ( uiomove(&data, 1 , uio)) {
+
+ printf("midi: Bad copyout()!\n");
+ return(ENOTTY);
+
+ }
+
+ }
+ splx(s);
+ return(ESUCCESS);
+
+}
+
+#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 0000000..b9d0701
--- /dev/null
+++ b/sys/i386/isa/sound/sb_card.c
@@ -0,0 +1,33 @@
+
+/*
+ * linux/kernel/chr_drv/sound/sb_card.c
+ *
+ * Detection routine for the SoundBlaster cards.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#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 0000000..d1adbde
--- /dev/null
+++ b/sys/i386/isa/sound/sb_dsp.c
@@ -0,0 +1,1303 @@
+/*
+ * linux/kernel/chr_drv/sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DS chips.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ *
+ * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen
+ * <shaehnic@ucsd.edu>
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+#undef SB_TEST_IRQ
+
+#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 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)
+
+static int sbc_base = 0;
+static int sbc_irq = 0;
+
+#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/*
+ * Mixer registers
+ *
+ * NOTE! RECORD_SRC == IN_FILTER
+ */
+
+#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 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)
+
+/* Convenient byte masks */
+#define B1(x) ((x) & 0x01)
+#define B2(x) ((x) & 0x03)
+#define B3(x) ((x) & 0x07)
+#define B4(x) ((x) & 0x0f)
+#define B5(x) ((x) & 0x1f)
+#define B6(x) ((x) & 0x3f)
+#define B7(x) ((x) & 0x7f)
+#define B8(x) ((x) & 0xff)
+#define F(x) (!!(x)) /* 0 or 1 only */
+
+#define MONO_DAC 0x00
+#define STEREO_DAC 0x02
+
+/* DSP Commands */
+
+#define DSP_CMD_SPKON 0xD1
+#define DSP_CMD_SPKOFF 0xD3
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * '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.
+ */
+
+#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
+
+static int sb_dsp_ok = 0; /* Set to 1 after successful initialization */
+static int midi_disabled = 0;
+static int dsp_highspeed = 0, dsp_stereo = 0;
+static int dsp_current_speed = DSP_DEFAULT_SPEED;
+
+#ifndef EXCLUDE_SBPRO
+static int rec_devices = SOUND_MASK_MIC;
+static int hi_filter = 0, filter_in = 0, filter_out = 0;
+
+#endif
+
+static int midi_mode = NORMAL_MIDI;
+static int midi_busy = 0; /* 1 if the process has output to MIDI */
+static int dsp_busy = 0;
+
+static volatile int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT
+ * or IMODE_NONE */
+static volatile int irq_ok = 0;
+
+static int dsp_model = 1; /* 1=SB, 2=SB Pro */
+static int duplex_midi = 0;
+static int my_dev = 0;
+
+static volatile int intr_active = 0;
+
+static int dsp_speed (int);
+static int dsp_set_stereo (int mode);
+static int dsp_command (unsigned char val);
+
+#ifndef EXCLUDE_SBPRO
+static void setmixer (unsigned char port, unsigned char value);
+static int getmixer (unsigned char port);
+static void init_mixer (void);
+static int detect_mixer (void);
+
+#endif
+
+#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
+
+/* Common code for the midi and pcm functions */
+
+static int
+dsp_command (unsigned char val)
+{
+ 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 ((INB (DSP_STATUS) & 0x80) == 0)
+ {
+ OUTB (val, DSP_COMMAND);
+ return 1;
+ }
+ }
+
+ printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val);
+ printk ("IRQ conflict???\n");
+ return 0;
+}
+
+void
+sbintr (int unused)
+{
+ int status, data;
+
+ status = INB (DSP_DATA_AVAIL);/* Clear interrupt */
+
+ if (intr_active)
+ switch (irq_mode)
+ {
+ case IMODE_OUTPUT:
+ intr_active = 0;
+ DMAbuf_outputintr (my_dev);
+ break;
+
+ case IMODE_INPUT:
+ intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ /* A complete buffer has been input. Let's start new one */
+ break;
+
+ case IMODE_INIT:
+ intr_active = 0;
+ irq_ok = 1;
+ break;
+
+ case IMODE_MIDI:
+ printk ("+");
+ data = INB (DSP_READ);
+ printk ("%02x", data);
+
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+static int
+set_dsp_irq (int interrupt_level)
+{
+ int retcode;
+
+#ifdef linux
+ struct sigaction sa;
+
+ sa.sa_handler = sbintr;
+
+#ifdef SND_SA_INTERRUPT
+ sa.sa_flags = SA_INTERRUPT;
+#else
+ sa.sa_flags = 0;
+#endif
+
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ retcode = irqaction (interrupt_level, &sa);
+
+ if (retcode < 0)
+ {
+ printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level);
+ }
+
+#else
+ /* # error Unimplemented for this OS */
+#endif
+ return retcode;
+}
+
+static int
+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)
+ dsp_command (DSP_CMD_SPKON);
+ else
+ dsp_command (DSP_CMD_SPKOFF);
+}
+
+static int
+dsp_speed (int speed)
+{
+ unsigned char tconst;
+ unsigned long flags;
+
+
+ if (speed < 4000)
+ speed = 4000;
+
+ if (speed > 44100)
+ speed = 44100; /* Invalid speed */
+
+ if (dsp_model == 1 && speed > 22050)
+ speed = 22050;
+ /* SB Classic doesn't support higher speed */
+
+
+ if (dsp_stereo && speed > 22050)
+ speed = 22050;
+ /* Max. stereo speed is 22050 */
+
+ if ((speed > 22050) && midi_busy)
+ {
+ printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n");
+ speed = 22050;
+ }
+
+ if (dsp_stereo)
+ speed <<= 1;
+
+ /* Now the speed should be valid */
+
+ if (speed > 22050)
+ { /* High speed mode */
+ tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8);
+ dsp_highspeed = 1;
+
+ DISABLE_INTR (flags);
+ if (dsp_command (0x40))
+ dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ speed = (256000000 / (65536 - (tconst << 8)));
+ }
+ else
+ {
+ dsp_highspeed = 0;
+ tconst = (256 - (1000000 / speed)) & 0xff;
+
+ DISABLE_INTR (flags);
+ if (dsp_command (0x40)) /* Set time constant */
+ dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ speed = 1000000 / (256 - tconst);
+ }
+
+ if (dsp_stereo)
+ speed >>= 1;
+
+ dsp_current_speed = speed;
+ return speed;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ dsp_stereo = 0;
+
+ if (dsp_model == 1)
+ return 0; /* Sorry no stereo */
+
+ if (mode && midi_busy)
+ {
+ printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
+ return 0;
+ }
+
+ dsp_stereo = !!mode;
+
+#ifndef EXCLUDE_SBPRO
+ setmixer (OUT_FILTER, ((getmixer (OUT_FILTER) & ~STEREO_DAC)
+ | (mode ? STEREO_DAC : MONO_DAC)));
+#endif
+ dsp_speed (dsp_current_speed);/* Speed must be recalculated if #channels
+ * changes */
+ return mode;
+}
+
+static void
+sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag)
+{
+ unsigned long flags;
+
+ if (!irq_mode)
+ dsp_speaker (ON);
+
+ irq_mode = IMODE_OUTPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+ count--;
+
+ if (dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (dsp_command (0x48)) /* High speed size */
+ {
+ dsp_command (count & 0xff);
+ dsp_command ((count >> 8) & 0xff);
+ 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 (dsp_command (0x14)) /* 8-bit DAC (DMA) */
+ {
+ dsp_command (count & 0xff);
+ dsp_command ((count >> 8) & 0xff);
+ }
+ else
+ printk ("SB Error: Unable to start DAC\n");
+ RESTORE_INTR (flags);
+ }
+ intr_active = 1;
+}
+
+static void
+sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag)
+{
+ /* Start a DMA input to the buffer pointed by dmaqtail */
+
+ unsigned long flags;
+
+ if (!irq_mode)
+ dsp_speaker (OFF);
+
+ irq_mode = IMODE_INPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (sound_dsp_dmachan[dev] > 3)
+ count >>= 1;
+ count--;
+
+ if (dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (dsp_command (0x48)) /* High speed size */
+ {
+ dsp_command (count & 0xff);
+ dsp_command ((count >> 8) & 0xff);
+ 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 (dsp_command (0x24)) /* 8-bit ADC (DMA) */
+ {
+ dsp_command (count & 0xff);
+ dsp_command ((count >> 8) & 0xff);
+ }
+ else
+ printk ("SB Error: Unable to start ADC\n");
+ RESTORE_INTR (flags);
+ }
+
+ intr_active = 1;
+}
+
+static void
+dsp_cleanup (void)
+{
+ intr_active = 0;
+}
+
+static int
+sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+ return 0;
+}
+
+static int
+sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (ON);
+ return 0;
+}
+
+static void
+sb_dsp_halt_xfer (int dev)
+{
+}
+
+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 (!irq_ok)
+ {
+ printk ("SB Error: Incorrect IRQ setting (%d)\n", sbc_irq);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (intr_active || (midi_busy && midi_mode == UART_MIDI))
+ {
+ printk ("SB: PCM not possible during MIDI input\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (mode != OPEN_READ && mode != OPEN_WRITE)
+ {
+ printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ retval = set_dsp_irq (sbc_irq);
+ if (retval)
+ return retval;
+
+ if (!DMAbuf_open_dma (dev))
+ {
+ RELEASE_IRQ (sbc_irq);
+ printk ("SB: DMA Busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ dsp_set_stereo (OFF);
+ dsp_speed (DSP_DEFAULT_SPEED);
+ irq_mode = IMODE_NONE;
+
+ dsp_busy = 1;
+
+ return 0;
+}
+
+static void
+sb_dsp_close (int dev)
+{
+ DMAbuf_close_dma (dev);
+ RELEASE_IRQ (sbc_irq);
+ dsp_cleanup ();
+ dsp_speed (DSP_DEFAULT_SPEED);
+ dsp_set_stereo (OFF);
+ dsp_speaker (OFF);
+ dsp_busy = 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:
+ 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);
+
+ reset_dsp ();
+ 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 (!reset_dsp ())
+ return 0;
+
+ return 1; /* Detected */
+}
+
+#ifndef EXCLUDE_SBPRO
+
+static void
+setmixer (unsigned char port, unsigned char value)
+{
+ OUTB (port, MIXER_ADDR); /* Select register */
+ tenmicrosec ();
+ OUTB (value, MIXER_DATA);
+ tenmicrosec ();
+}
+
+static int
+getmixer (unsigned char port)
+{
+ int val;
+
+ OUTB (port, MIXER_ADDR); /* Select register */
+ tenmicrosec ();
+ val = INB (MIXER_DATA);
+ tenmicrosec ();
+
+ return val;
+}
+
+static int
+detect_mixer (void)
+{
+ /*
+ * 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?)
+ */
+ setmixer (FM_VOL, 0xff);
+ setmixer (VOC_VOL, 0x33);
+
+ if (getmixer (FM_VOL) != 0xff)
+ return 0; /* No match */
+ if (getmixer (VOC_VOL) != 0x33)
+ return 0;
+
+ return 1;
+}
+
+static void
+init_mixer (void)
+{
+ setmixer (MASTER_VOL, 0xbb);
+ setmixer (VOC_VOL, 0x99);
+ setmixer (LINE_VOL, 0xbb);
+ setmixer (FM_VOL, 0x99);
+ setmixer (CD_VOL, 0x11);
+ setmixer (MIC_MIX, 0x11);
+ setmixer (RECORD_SRC, 0x31);
+ setmixer (OUT_FILTER, 0x31);
+}
+
+static void
+set_filter (int record_source, int hifreq_filter, int filter_input, int filter_output)
+{
+ setmixer (RECORD_SRC, (record_source
+ | (hifreq_filter ? FREQ_HI : FREQ_LOW)
+ | (filter_input ? FILT_ON : FILT_OFF)));
+
+ setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC)
+ | (filter_output ? FILT_ON : FILT_OFF)));
+
+ hi_filter = hifreq_filter;
+ filter_in = filter_input;
+ filter_out = filter_output;
+}
+
+static int
+mixer_output (int right_vol, int left_vol, int div, int device)
+{
+ int left = ((left_vol * div) + 50) / 100;
+ int right = ((right_vol * div) + 50) / 100;
+
+ setmixer (device, ((left & 0xf) << 4) | (right & 0xf));
+
+ return (left_vol | (right_vol << 8));
+}
+
+static int
+sbp_mixer_set (int whichDev, unsigned int level)
+{
+ int left, right, devmask;
+
+ left = level & 0x7f;
+ right = (level & 0x7f00) >> 8;
+
+ switch (whichDev)
+ {
+ case SOUND_MIXER_VOLUME: /* Master volume (0-15) */
+ return mixer_output (right, left, 15, MASTER_VOL);
+ break;
+ case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */
+ return mixer_output (right, left, 15, FM_VOL);
+ break;
+ case SOUND_MIXER_PCM: /* PAS PCM (0-15) */
+ return mixer_output (right, left, 15, VOC_VOL);
+ break;
+ case SOUND_MIXER_LINE: /* External line (0-15) */
+ return mixer_output (right, left, 15, LINE_VOL);
+ break;
+ case SOUND_MIXER_CD: /* CD (0-15) */
+ return mixer_output (right, left, 15, CD_VOL);
+ break;
+ case SOUND_MIXER_MIC: /* External microphone (0-7) */
+ return mixer_output (right, left, 7, MIC_VOL);
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /* More than one devices selected. Drop the
+ * previous selection */
+ devmask &= ~rec_devices;
+ }
+
+ 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 ^ rec_devices)/* Input source changed */
+ {
+ switch (devmask)
+ {
+
+ case SOUND_MASK_MIC:
+ set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
+ break;
+
+ case SOUND_MASK_LINE:
+ set_filter (SRC_LINE, hi_filter, filter_in, filter_out);
+ break;
+
+ case SOUND_MASK_CD:
+ set_filter (SRC_CD, hi_filter, filter_in, filter_out);
+ break;
+
+ default:
+ set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
+ }
+ }
+
+ rec_devices = devmask;
+
+ return rec_devices;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+}
+
+static int
+mixer_input (int div, int device)
+{
+ int level, left, right, half;
+
+ level = getmixer (device);
+ half = div / 2;
+
+ left = ((((level & 0xf0) >> 4) * 100) + half) / div;
+ right = (((level & 0x0f) * 100) + half) / div;
+
+ return (right << 8) | left;
+}
+
+static int
+sbp_mixer_get (int whichDev)
+{
+
+ switch (whichDev)
+ {
+ case SOUND_MIXER_VOLUME: /* Master volume (0-15) */
+ return mixer_input (15, MASTER_VOL);
+ break;
+ case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */
+ return mixer_input (15, FM_VOL);
+ break;
+ case SOUND_MIXER_PCM: /* PAS PCM (0-15) */
+ return mixer_input (15, VOC_VOL);
+ break;
+ case SOUND_MIXER_LINE: /* External line (0-15) */
+ return mixer_input (15, LINE_VOL);
+ break;
+ case SOUND_MIXER_CD: /* CD (0-15) */
+ return mixer_input (15, CD_VOL);
+ break;
+ case SOUND_MIXER_MIC: /* External microphone (0-7) */
+ return mixer_input (7, MIC_VOL);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+}
+
+/*
+ * Sets mixer volume levels. All levels except mic are 0 to 15, mic is 7. See
+ * sbinfo.doc for details on granularity and such. Basically, the mixer
+ * forces the lowest bit high, effectively reducing the possible settings by
+ * one half. Yes, that's right, volume levels have 8 settings, and
+ * microphone has four. Sucks.
+ */
+static int
+mixer_set_levels (struct sb_mixer_levels *user_l)
+{
+ struct sb_mixer_levels l;
+
+ IOCTL_FROM_USER ((char *) &l, ((char *) user_l), 0, sizeof (l));
+
+ if (l.master.l & ~0xF || l.master.r & ~0xF
+ || l.line.l & ~0xF || l.line.r & ~0xF
+ || l.voc.l & ~0xF || l.voc.r & ~0xF
+ || l.fm.l & ~0xF || l.fm.r & ~0xF
+ || l.cd.l & ~0xF || l.cd.r & ~0xF
+ || l.mic & ~0x7)
+ return (RET_ERROR (EINVAL));
+
+ setmixer (MASTER_VOL, (l.master.l << 4) | l.master.r);
+ setmixer (LINE_VOL, (l.line.l << 4) | l.line.r);
+ setmixer (VOC_VOL, (l.voc.l << 4) | l.voc.r);
+ setmixer (FM_VOL, (l.fm.l << 4) | l.fm.r);
+ setmixer (CD_VOL, (l.cd.l << 4) | l.cd.r);
+ setmixer (MIC_VOL, l.mic);
+ return (0);
+}
+
+/*
+ * This sets aspects of the Mixer that are not volume levels. (Recording
+ * source, filter level, I/O filtering, and stereo.)
+ */
+
+static int
+mixer_set_params (struct sb_mixer_params *user_p)
+{
+ struct sb_mixer_params p;
+
+ IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
+
+ if (p.record_source != SRC_MIC
+ && p.record_source != SRC_CD
+ && p.record_source != SRC_LINE)
+ return (EINVAL);
+
+ /*
+ * I'm not sure if this is The Right Thing. Should stereo be entirely
+ * under control of DSP? I like being able to toggle it while a sound is
+ * playing, so I do this... because I can.
+ */
+
+ dsp_stereo = !!p.dsp_stereo;
+
+ set_filter (p.record_source, p.hifreq_filter, p.filter_input, p.filter_output);
+
+ switch (p.record_source)
+ {
+
+ case SRC_MIC:
+ rec_devices = SOUND_MASK_MIC;
+ break;
+
+ case SRC_LINE:
+ rec_devices = SOUND_MASK_LINE;
+ break;
+
+ case SRC_CD:
+ rec_devices = SOUND_MASK_CD;
+ }
+
+ return (0);
+}
+
+/* Read the current mixer level settings into the user's struct. */
+static int
+mixer_get_levels (struct sb_mixer_levels *user_l)
+{
+ S_BYTE val;
+ struct sb_mixer_levels l;
+
+ val = getmixer (MASTER_VOL); /* Master */
+ l.master.l = B4 (val >> 4);
+ l.master.r = B4 (val);
+
+ val = getmixer (LINE_VOL); /* FM */
+ l.line.l = B4 (val >> 4);
+ l.line.r = B4 (val);
+
+ val = getmixer (VOC_VOL); /* DAC */
+ l.voc.l = B4 (val >> 4);
+ l.voc.r = B4 (val);
+
+ val = getmixer (FM_VOL); /* FM */
+ l.fm.l = B4 (val >> 4);
+ l.fm.r = B4 (val);
+
+ val = getmixer (CD_VOL); /* CD */
+ l.cd.l = B4 (val >> 4);
+ l.cd.r = B4 (val);
+
+ val = getmixer (MIC_VOL); /* Microphone */
+ l.mic = B3 (val);
+
+ IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
+
+ return (0);
+}
+
+/* Read the current mixer parameters into the user's struct. */
+static int
+mixer_get_params (struct sb_mixer_params *user_params)
+{
+ S_BYTE val;
+ struct sb_mixer_params params;
+
+ val = getmixer (RECORD_SRC);
+ params.record_source = val & 0x07;
+ params.hifreq_filter = !!(val & FREQ_HI);
+ params.filter_input = (val & FILT_OFF) ? OFF : ON;
+ params.filter_output = (getmixer (OUT_FILTER) & FILT_OFF) ? OFF : ON;
+ params.dsp_stereo = dsp_stereo;
+
+ IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
+ return (0);
+}
+
+static int
+sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ return IOCTL_OUT (arg, sbp_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_DEVMASK:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~SOUND_MASK_MIC);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, sbp_mixer_get (cmd & 0xff));
+ }
+ }
+ }
+ else
+ {
+ switch (cmd)
+ {
+ case MIXER_IOCTL_SET_LEVELS:
+ return (mixer_set_levels ((struct sb_mixer_levels *) arg));
+ case MIXER_IOCTL_SET_PARAMS:
+ return (mixer_set_params ((struct sb_mixer_params *) arg));
+ case MIXER_IOCTL_READ_LEVELS:
+ return (mixer_get_levels ((struct sb_mixer_levels *) arg));
+ case MIXER_IOCTL_READ_PARAMS:
+ return (mixer_get_params ((struct sb_mixer_params *) arg));
+ case MIXER_IOCTL_RESET:
+ init_mixer ();
+ return (0);
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+}
+
+/* End of mixer code */
+#endif
+
+#ifndef EXCLUDE_MIDI
+
+/* Midi code */
+
+static int
+sb_midi_open (int dev, int mode)
+{
+ int ret;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: MIDI hardware not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (mode != OPEN_WRITE && !duplex_midi)
+ {
+ printk ("SoundBlaster: Midi input not currently supported\n");
+ return RET_ERROR (EPERM);
+ }
+
+ midi_mode = NORMAL_MIDI;
+ if (mode != OPEN_WRITE)
+ {
+ if (dsp_busy || intr_active)
+ return RET_ERROR (EBUSY);
+ midi_mode = UART_MIDI;
+ }
+
+ if (dsp_highspeed || dsp_stereo)
+ {
+ printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (midi_mode == UART_MIDI)
+ {
+ irq_mode = IMODE_MIDI;
+
+ reset_dsp ();
+ dsp_speaker (OFF);
+
+ if (!dsp_command (0x35))
+ return RET_ERROR (EIO); /* Enter the UART mode */
+ intr_active = 1;
+
+ if ((ret = set_dsp_irq (sbc_irq)) < 0)
+ {
+ reset_dsp ();
+ return 0; /* IRQ not free */
+ }
+ }
+
+ midi_busy = 1;
+
+ return 0;
+}
+
+static void
+sb_midi_close (int dev)
+{
+ if (midi_mode == UART_MIDI)
+ {
+ reset_dsp (); /* The only way to kill the UART mode */
+ RELEASE_IRQ (sbc_irq);
+ }
+ intr_active = 0;
+ midi_busy = 0;
+}
+
+static int
+sb_midi_out (int dev, unsigned char midi_byte)
+{
+ unsigned long flags;
+
+ midi_busy = 1; /* Kill all notes after close */
+
+ if (midi_mode == NORMAL_MIDI)
+ {
+ DISABLE_INTR (flags);
+ if (dsp_command (0x38))
+ dsp_command (midi_byte);
+ else
+ printk ("SB Error: Unable to send a MIDI byte\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ dsp_command (midi_byte); /* UART write */
+
+ return 1;
+}
+
+static int
+sb_midi_start_read (int dev)
+{
+ if (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 (midi_mode == UART_MIDI)
+ {
+ reset_dsp ();
+ intr_active = 0;
+ }
+ return 0;
+}
+
+static int
+sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EPERM);
+}
+
+/* End of midi code */
+#endif
+
+#ifndef EXCLUDE_AUDIO
+static struct audio_operations sb_dsp_operations =
+{
+ "SoundBlaster",
+ 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, /* has_output_drained */
+ NULL /* copy_from_user */
+};
+
+#endif
+
+#ifndef EXCLUDE_SBPRO
+static struct mixer_operations sb_mixer_operations =
+{
+ sb_mixer_ioctl
+};
+
+#endif
+
+#ifndef EXCLUDE_MIDI
+static struct midi_operations sb_midi_operations =
+{
+ {"SoundBlaster", 0},
+ 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 */
+};
+
+#endif
+
+static int
+verify_irq (void)
+{
+#if 0
+ unsigned long loop;
+
+ irq_ok = 0;
+
+ if (set_dsp_irq (sbc_irq) == -1)
+ {
+ printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
+ return 0;
+ }
+
+
+ irq_mode = IMODE_INIT;
+
+ dsp_command (0xf2); /* This should cause immediate interrupt */
+
+ for (loop = 100000; loop > 0 && !irq_ok; loop--);
+
+ RELEASE_IRQ (sbc_irq);
+
+ if (!irq_ok)
+ {
+ printk ("SB Warning: IRQ test not passed!");
+ irq_ok = 1;
+ }
+#else
+ irq_ok = 1;
+#endif
+ return irq_ok;
+}
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ int i, major, minor;
+
+ major = minor = 0;
+ dsp_command (0xe1); /* Get version */
+
+ for (i = 1000; i; i--)
+ {
+ if (inb (DSP_DATA_AVAIL) & 0x80)
+ { /* wait for Data Ready */
+ if (major == 0)
+ major = inb (DSP_READ);
+ else
+ {
+ minor = inb (DSP_READ);
+ break;
+ }
+ }
+ }
+
+#ifndef EXCLUDE_SBPRO
+ if (detect_mixer ())
+ {
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor);
+ init_mixer ();
+#if SBC_DMA < 4
+ /* This is a kludge for SB16 cards */
+ if (major == 3)
+ dsp_model = 2; /* Do not enable if SB16 */
+#endif
+ mixer_devs[num_mixers++] = &sb_mixer_operations;
+
+ if (major == 2 || major == 3)
+ duplex_midi = 1;
+
+#ifndef EXCLUDE_YM8312
+ if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */
+ {
+ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+ }
+#endif
+ }
+ else
+#endif
+ sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor);
+
+ printk (" <%s>", sb_dsp_operations.name);
+
+ if (!verify_irq ())
+ return mem_start;
+
+#ifndef EXCLUDE_AUDIO
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
+ sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+ sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+ sound_dma_automode[my_dev] = 0;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+#endif
+
+#ifndef EXCLUDE_MIDI
+ if (!midi_disabled) /* Midi don't work in the SB emulation mode
+ * of PAS */
+ midi_devs[num_midis++] = &sb_midi_operations;
+#endif
+
+ sb_dsp_ok = 1;
+ printk("\n");
+ return mem_start;
+}
+
+void
+sb_dsp_disable_midi (void)
+{
+ midi_disabled = 1;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c
new file mode 100644
index 0000000..2d4ef37
--- /dev/null
+++ b/sys/i386/isa/sound/sequencer.c
@@ -0,0 +1,1137 @@
+/*
+ * linux/kernel/chr_drv/sound/sequencer.c
+ *
+ * The sequencer personality manager.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
+ * details. Should be distributed with this file.
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static int sequencer_ok = 0;
+
+DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+
+static int midi_opened[MAX_MIDI_DEV] =
+{0}; /* 1 if the process has opened MIDI */
+static int midi_written[MAX_MIDI_DEV] =
+{0};
+
+long seq_time = 0; /* Reference point for the timer */
+
+#include "tuning.h"
+
+#define EV_SZ 8
+static unsigned char queue[SEQ_MAX_QUEUE][EV_SZ];
+static unsigned char iqueue[SEQ_MAX_QUEUE][4];
+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 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
+#endif
+
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c = count, p = 0;
+
+ dev = dev >> 4;
+
+ if (dev) /* Patch manager device */
+ return pmgr_read (dev - 1, file, buf, count);
+
+ while (c > 3)
+ {
+ if (!iqlen)
+ {
+ INTERRUPTIBLE_SLEEP_ON (midi_sleeper, midi_sleep_flag);
+
+ if (!iqlen)
+ return count - c;
+ }
+
+ COPY_TO_USER (buf, p, &iqueue[iqhead][0], 4);
+ p += 4;
+ c -= 4;
+
+ iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+ iqlen--;
+ }
+
+ return count - c;
+}
+
+void
+sequencer_midi_output (int dev)
+{
+ /* Currently NOP */
+}
+
+static void
+copy_to_input (unsigned char *event)
+{
+ unsigned long flags;
+
+ if (iqlen >= (SEQ_MAX_QUEUE - 1))
+ return; /* Overflow */
+
+ memcpy (iqueue[iqtail], event, 4);
+ iqlen++;
+ iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+
+ DISABLE_INTR (flags);
+ if (midi_sleep_flag)
+ {
+ WAKE_UP (midi_sleeper);
+ }
+ RESTORE_INTR (flags);
+}
+
+void
+sequencer_midi_input (int dev, unsigned char data)
+{
+ int tstamp;
+ unsigned char event[4];
+
+ if (data == 0xfe) /* Active sensing */
+ return; /* Ignore */
+
+ tstamp = GET_TIME () - seq_time; /* Time since open() */
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+
+ copy_to_input ((unsigned char *) &tstamp);
+
+ event[0] = SEQ_MIDIPUTC;
+ event[1] = data;
+ event[2] = dev;
+ event[3] = 0;
+
+ copy_to_input (event);
+}
+
+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 >= num_synths)
+ 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 == SEQ_EXTENDED || ev_code == SEQ_PRIVATE)
+ {
+
+ 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
+ ev_size = 4;
+
+ if (event[0] == SEQ_MIDIPUTC)
+ {
+
+ if (!midi_opened[event[2]])
+ {
+ int mode;
+ int dev = event[2];
+
+ if (dev >= num_midis)
+ {
+ 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)) < 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 && !seq_sleep_flag)
+ {
+ /* Sleep until there is enough space on the queue */
+ INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ }
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ return 0; /* To be sure */
+
+ memcpy (&queue[qtail][0], 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 >= num_synths)
+ 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[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;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static void
+seq_startplay (void)
+{
+ int this_one;
+ unsigned long *delay;
+ unsigned char *q;
+
+ while (qlen > 0)
+ {
+ qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+ qlen--;
+
+ q = &queue[this_one][0];
+
+ switch (q[0])
+ {
+ case SEQ_NOTEOFF:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->kill_note (0, q[1], 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;
+
+ request_sound_timer (time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (seq_sleep_flag)
+ {
+ seq_sleep_flag = 0;
+ WAKE_UP (seq_sleeper);
+ }
+ RESTORE_INTR (flags);
+ }
+ return; /* Stop here. Timer routine will continue
+ * playing after the delay */
+ }
+ 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 ();
+ 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:
+ copy_to_input (q); /* Echo back to the process */
+ break;
+
+ case SEQ_PRIVATE:
+ if (q[1] < num_synths)
+ synth_devs[q[1]]->hw_control (q[1], q);
+ break;
+
+ case SEQ_EXTENDED:
+ extended_event (q);
+ break;
+
+ default:;
+ }
+
+ }
+
+ seq_playing = 0;
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (seq_sleep_flag)
+ {
+ seq_sleep_flag = 0;
+ WAKE_UP (seq_sleeper);
+ }
+ RESTORE_INTR (flags);
+ }
+
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+ {
+ int retval, mode, i;
+
+ 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 (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);
+ }
+
+ if (!(num_synths + num_midis))
+ return RET_ERROR (ENXIO);
+
+ synth_open_mask = 0;
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ for (i = 0; i < num_synths; i++) /* Open synth devices */
+ if (synth_devs[i]->open (i, mode) < 0)
+ printk ("Sequencer: Warning! Cannot open synth device #%d\n", i);
+ else
+ synth_open_mask |= (1 << i);
+
+ seq_time = GET_TIME ();
+
+ for (i = 0; i < num_midis; i++)
+ {
+ midi_opened[i] = 0;
+ midi_written[i] = 0;
+ }
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ { /* Initialize midi input devices */
+ if (!num_midis)
+ {
+ printk ("Sequencer: No Midi devices. Input not possible\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if ((retval = midi_devs[i]->open (i, mode)) >= 0)
+ midi_opened[i] = 1;
+ }
+ }
+
+ sequencer_busy = 1;
+ seq_sleep_flag = midi_sleep_flag = 0;
+ 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 && n)
+ {
+ n = 0;
+
+ for (i = 0; i < num_midis; 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)
+ {
+ REQUEST_TIMEOUT (HZ / 10, seq_sleeper);
+ INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ }
+ }
+}
+
+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
+ */
+ while (!PROCESS_ABORTING && 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 < num_midis; i++)
+ if (midi_opened[i])
+ midi_devs[i]->close (i);
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ for (i = 0; i < num_synths; i++)
+ if (synth_open_mask & (1 << i)) /* Actually opened */
+ if (synth_devs[i])
+ synth_devs[i]->close (i);
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+
+ sequencer_busy = 0;
+ }
+
+static int
+seq_sync (void)
+{
+ if (qlen && !seq_playing && !PROCESS_ABORTING)
+ seq_startplay ();
+
+ if (qlen && !seq_sleep_flag) /* Queue not empty */
+ {
+ INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ }
+
+ 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))
+ {
+ REQUEST_TIMEOUT (1, seq_sleeper);
+ INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ n--;
+ }
+}
+
+static void
+seq_reset (void)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int i, chn;
+
+ sound_stop_timer ();
+
+ qlen = qhead = qtail = 0;
+ iqlen = iqhead = iqtail = 0;
+
+ for (i = 0; i < num_synths; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ synth_devs[i]->reset (i);
+
+ for (i = 0; i < num_midis; i++)
+ if (midi_written[i]) /* Midi used. Some notes may still be playing */
+ {
+ for (chn = 0; chn < 16; chn++)
+ {
+ midi_outc (i, 0xb0 + chn); /* Channel message */
+ 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 (seq_sleep_flag)
+ printk ("Sequencer Warning: Unexpected sleeping process\n");
+
+}
+
+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_SEQ_SYNC:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return 0;
+ while (qlen && !PROCESS_ABORTING)
+ seq_sync ();
+ 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 >= num_midis)
+ 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)) < 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 */
+ 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, num_synths);
+ break;
+
+ case SNDCTL_SEQ_NRMIDIS:
+ return IOCTL_OUT (arg, num_midis);
+ 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 >= num_synths)
+ 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 >= num_midis)
+ 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;
+
+ 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)
+{
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!iqlen)
+ {
+ midi_sleep_flag = 1;
+ select_wait (&midi_sleeper, wait);
+ return 0;
+ }
+ return 1;
+
+ break;
+
+ case SEL_OUT:
+ if (qlen >= SEQ_MAX_QUEUE)
+ {
+ seq_sleep_flag = 1;
+ select_wait (&seq_sleeper, wait);
+ 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
+ }; /* Note freq*1000 for octave 5 */
+
+#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;
+
+ 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;
+
+ semitones = bend / 100;
+ cents = bend % 100;
+
+ amount = semitone_tuning[semitones] * 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;
+ 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;
+}
+
+void
+sequencer_midi_input (int dev, unsigned char data)
+{
+ return;
+}
+
+void
+sequencer_midi_output (int dev)
+{
+ return;
+}
+
+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 0000000..fdf1fe1
--- /dev/null
+++ b/sys/i386/isa/sound/sound_calls.h
@@ -0,0 +1,183 @@
+/*
+ * DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_read (int dev, snd_rw_buf *user_buf, int count);
+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);
+
+/*
+ * System calls for the /dev/dsp
+ */
+
+int dsp_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int dsp_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int dsp_open (int dev, struct fileinfo *file, int bits);
+void dsp_release (int dev, struct fileinfo *file);
+int dsp_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long dsp_init (long mem_start);
+
+/*
+ * System calls for the /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_midi_input(int dev, unsigned char data);
+void sequencer_midi_output(int dev);
+void sequencer_timer(void);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+
+#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);
+
+/*
+ * 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, struct uio *uio);
+int pro_midi_read(int dev, struct uio *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);
+
+/* 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);
+
+/* 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);
+
+/* 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 char data);
+void guswave_dma_irq(void);
+void gus_delay(void);
+
+/* 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 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);
diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h
new file mode 100644
index 0000000..09d36e5
--- /dev/null
+++ b/sys/i386/isa/sound/sound_config.h
@@ -0,0 +1,182 @@
+/* linux/kernel/chr_drv/sound/sound-config.h
+
+A driver for Soundcards, misc configuration parameters.
+
+(C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) */
+
+#include "local.h"
+
+
+#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
+
+/** UWM - new MIDI stuff **/
+
+#ifdef EXCLUDE_CHIP_MIDI
+#define EXCLUDE_PRO_MIDI
+#endif
+
+/** UWM - stuff **/
+
+#if defined(EXCLUDE_SEQUENCER) && defined(EXCLUDE_AUDIO)
+#undef CONFIGURE_SOUNDCARD
+#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 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
+
+/************* 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 SND_MAJOR 14
+
+#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 512
+
+#define SBFM_MAXINSTR (256) /* Size of the FM Instrument
+ bank */
+/* 128 instruments for general MIDI setup and 16 unassigned */
+
+#define SND_NDEVS 50 /* 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 /* MIDI input /dev/midin (not implemented
+ yet) */
+#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/sndstatus */
+
+/* UWM ... note add new MIDI devices here..
+ * Also do not forget to add table midi_supported[]
+ * Minor numbers for on-chip midi devices start from 15.. and
+ * should be contiguous.. viz. 15,16,17....
+ * Also note the max # of midi devices as MAX_MIDI_DEV
+ */
+
+#define CMIDI_DEV_PRO 15 /* Chip midi device == /dev/pro_midi */
+
+/*
+ * Add other midis here...
+ .
+ .
+ .
+ .
+ */
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#define MAX_DSP_DEV 3
+#define MAX_MIXER_DEV 1
+#define MAX_SYNTH_DEV 3
+#define MAX_MIDI_DEV 3
+
+struct fileinfo {
+ int mode; /* Open mode */
+ };
+
+struct address_info {
+ int io_base;
+ int irq;
+ int dma;
+};
+
+#define OPEN_READ 1
+#define OPEN_WRITE 2
+#define OPEN_READWRITE 3
+
+#include "os.h"
+#include "sound_calls.h"
+#include "dev_table.h"
+#include "debug.h"
+
+#endif
diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c
new file mode 100644
index 0000000..a0ded01
--- /dev/null
+++ b/sys/i386/isa/sound/soundcard.c
@@ -0,0 +1,593 @@
+/*
+ * sound/386bsd/soundcard.c
+ *
+ * Soundcard driver for 386BSD.
+ *
+ * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi)
+ * See COPYING for further details. Should be distributed with this file.
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "dev_table.h"
+
+int __timeout_val = 0;
+int __process_aborting = 0;
+
+u_int snd1mask;
+u_int snd2mask;
+u_int snd3mask;
+u_int snd4mask;
+u_int snd5mask;
+
+struct sbc_device
+{
+ int usecount;
+};
+
+#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
+
+static struct sbc_device sbc_devices[SND_NDEVS];
+static int timer_running = 0;
+
+static int in_use = 0; /* Total # of open device files (excluding
+ * minor 0) */
+
+static int soundcards_installed = 0; /* Number of installed
+ * soundcards */
+static int soundcard_configured = 0;
+extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
+extern int snd_raw_count[MAX_DSP_DEV];
+
+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);
+
+int
+get_time()
+{
+extern struct timeval time;
+
+ return(time.tv_usec + (time.tv_sec*1000000));
+}
+
+
+int
+sndread (int dev, struct uio *buf)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0xff) /* Changed to 0xff from 0x0f */
+ {
+ case SND_DEV_AUDIO:
+ FIX_RETURN (audio_read (dev, &files[dev], buf, count));
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ FIX_RETURN (dsp_read (dev, &files[dev], buf, count));
+ break;
+
+ case SND_DEV_SEQ:
+ FIX_RETURN (sequencer_read (dev, &files[dev], buf, count));
+ break;
+
+#ifndef EXCLUDE_CHIP_MIDI
+ case CMIDI_DEV_PRO:
+ FIX_RETURN (CMIDI_read (dev, &files[dev], buf, count));
+
+ break;
+#endif
+
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ FIX_RETURN (MIDIbuf_read (dev, &files[dev], buf, count));
+#endif
+
+ default:
+ ;
+ }
+
+ FIX_RETURN (-EPERM);
+}
+
+int
+sndwrite (int dev, struct uio *buf)
+{
+ int count = buf->uio_resid;
+
+ DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count));
+
+ dev = minor (dev);
+
+ switch (dev & 0xff) /* Changed to 0xff from 0x0f */
+ {
+
+ case SND_DEV_SEQ:
+ FIX_RETURN (sequencer_write (dev, &files[dev], buf, count));
+ break;
+
+ case SND_DEV_AUDIO:
+ FIX_RETURN (audio_write (dev, &files[dev], buf, count));
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ FIX_RETURN (dsp_write (dev, &files[dev], buf, count));
+ break;
+
+#ifndef EXCLUDE_CHIP_MIDI
+ case CMIDI_DEV_PRO:
+ FIX_RETURN (CMIDI_write (dev, &files[dev], buf, count));
+ break;
+#endif
+
+ default:
+ FIX_RETURN (-EPERM);
+ }
+
+ FIX_RETURN (count);
+}
+
+int
+sndopen (dev_t dev, int flags)
+{
+ int retval;
+
+ dev = minor (dev);
+
+ /* printf("SND: Minor number is now : %ld\n",dev); */
+
+ DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
+
+ if ((dev >= SND_NDEVS) || (dev < 0))
+ {
+ printk ("Invalid minor device %d\n", dev);
+ FIX_RETURN (-ENODEV);
+ }
+
+ 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;
+
+ switch (dev & 0xff) /* Changed to 0xff from 0x0f */
+ {
+ case SND_DEV_CTL:
+ if (!soundcards_installed)
+ if (soundcard_configured)
+ {
+ printk ("Soundcard not installed\n");
+ FIX_RETURN (-ENODEV);
+ }
+ break;
+
+ case SND_DEV_SEQ:
+ if ((retval = sequencer_open (dev, &files[dev])) < 0)
+ FIX_RETURN (retval);
+ break;
+
+/** UWM stuff **/
+
+#ifndef EXCLUDE_CHIP_MIDI
+ case CMIDI_DEV_PRO:
+ FIX_RETURN ( CMIDI_open (dev, &files[dev]) );
+ break;
+#endif
+
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ if ((retval = MIDIbuf_open (dev, &files[dev])) < 0)
+ FIX_RETURN (retval);
+ break;
+#endif
+
+ case SND_DEV_AUDIO:
+ if ((retval = audio_open (dev, &files[dev])) < 0)
+ FIX_RETURN (retval);
+ break;
+
+ case SND_DEV_DSP:
+ if ((retval = dsp_open (dev, &files[dev], 8)) < 0)
+ FIX_RETURN (retval);
+ break;
+
+ case SND_DEV_DSP16:
+ if ((retval = dsp_open (dev, &files[dev], 16)) < 0)
+ FIX_RETURN (retval);
+ break;
+
+ default:
+ printk ("Invalid minor device %d\n", dev);
+ FIX_RETURN (-ENODEV);
+ }
+
+ sbc_devices[dev].usecount++;
+ in_use++;
+
+ FIX_RETURN (0);
+}
+
+int
+sndclose (dev_t dev, int flags)
+{
+
+ dev = minor (dev);
+
+ DEB (printk ("sound_release(dev=%d)\n", dev));
+
+ switch (dev & 0xff) /* Changed to 0xff from 0x0f */
+ {
+ case SND_DEV_SEQ:
+ sequencer_release (dev, &files[dev]);
+ break;
+
+#ifndef EXCLUDE_CHIP_MIDI
+ case CMIDI_DEV_PRO:
+ CMIDI_close (dev, &files[dev]);
+ break;
+#endif
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ MIDIbuf_release (dev, &files[dev]);
+ break;
+#endif
+
+ case SND_DEV_AUDIO:
+ audio_release (dev, &files[dev]);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ dsp_release (dev, &files[dev]);
+ break;
+
+ default:;
+ }
+
+ sbc_devices[dev].usecount--;
+ in_use--; /* If not control port */
+
+ FIX_RETURN (0);
+}
+
+int
+sndioctl (dev_t dev, int cmd, caddr_t arg, int mode)
+{
+ dev = minor (dev);
+
+ DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_CTL:
+ if (!num_mixers)
+ FIX_RETURN (-ENODEV);
+
+ if (dev >= num_mixers)
+ FIX_RETURN (-ENODEV);
+
+ FIX_RETURN (mixer_devs[dev]->ioctl (dev, cmd, (unsigned int) arg));
+ break;
+
+ case SND_DEV_SEQ:
+ FIX_RETURN (sequencer_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
+ break;
+
+ case SND_DEV_AUDIO:
+ FIX_RETURN (audio_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ FIX_RETURN (dsp_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ FIX_RETURN (MIDIbuf_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
+ break;
+#endif
+
+ default:
+ FIX_RETURN (-EPERM);
+ break;
+ }
+
+ FIX_RETURN (-EPERM);
+}
+
+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 (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 dsp_initialized = 0;
+ 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 */
+ }
+
+#ifndef EXCLUDE_AUDIO
+ soundcard_configured = 1;
+ if (num_dspdevs)
+ sound_mem_init ();
+#endif
+
+ if (num_dspdevs && !dsp_initialized) /* Audio devices present */
+ {
+ dsp_initialized = 1;
+ mem_start = DMAbuf_init (mem_start);
+ mem_start = audio_init (mem_start);
+ mem_start = dsp_init (mem_start);
+ }
+
+/** UWM stuff **/
+
+#ifndef EXCLUDE_CHIP_MIDI
+
+ if (!generic_midi_initialized)
+ {
+ generic_midi_initialized = 1;
+ mem_start = CMIDI_init (mem_start);
+ }
+
+#endif
+
+#ifndef EXCLUDE_MPU401
+ if (num_midis && !midi_initialized)
+ {
+ midi_initialized = 1;
+ mem_start = MIDIbuf_init (mem_start);
+ }
+#endif
+
+ if ((num_midis + num_synths) && !seq_initialized)
+ {
+ seq_initialized = 1;
+ mem_start = sequencer_init (mem_start);
+ }
+
+ for (i = 0; i < SND_NDEVS; i++)
+ {
+ sbc_devices[i].usecount = 0;
+ }
+
+ return TRUE;
+}
+
+void
+tenmicrosec (void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ inb (0x80);
+}
+
+#ifdef EXCLUDE_GUS
+void
+gusintr (int unit)
+{
+ return (0);
+}
+#endif
+
+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_dspdevs; 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 (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;
+
+ /* 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;
+
+ 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
+ */
+ rounded = (addr & ~(dma_pagesize - 1)) + dma_pagesize;
+
+ 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;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/tuning.h b/sys/i386/isa/sound/tuning.h
new file mode 100644
index 0000000..858e1fe
--- /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/ulaw.h b/sys/i386/isa/sound/ulaw.h
new file mode 100644
index 0000000..be9f92d
--- /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,
+};
OpenPOWER on IntegriCloud