aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorSteven Wallace <swallace@FreeBSD.org>1994-10-01 01:33:47 +0000
committerSteven Wallace <swallace@FreeBSD.org>1994-10-01 01:33:47 +0000
commit8f25169ce64c84f2254305ced3738be611861bd3 (patch)
treeb0ab422841f5c2ef086c0ba888b0be8a372904f9 /sys
parente902866cf755930157f2094d6fb5fc8a90fd9adf (diff)
parentfbb347fe32427a1b3fb420db2f83b7cfefaa7890 (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/i386/isa/sound/ad1848.c953
-rw-r--r--sys/i386/isa/sound/configure.c830
-rw-r--r--sys/i386/isa/sound/midi_ctrl.h22
-rw-r--r--sys/i386/isa/sound/midi_synth.c474
-rw-r--r--sys/i386/isa/sound/midi_synth.h45
-rw-r--r--sys/i386/isa/sound/pss.c924
-rw-r--r--sys/i386/isa/sound/pss.h371
-rw-r--r--sys/i386/isa/sound/sound_timer.c406
-rw-r--r--sys/i386/isa/sound/sys_timer.c304
-rw-r--r--sys/i386/isa/sound/uart6850.c323
10 files changed, 4652 insertions, 0 deletions
diff --git a/sys/i386/isa/sound/ad1848.c b/sys/i386/isa/sound/ad1848.c
new file mode 100644
index 000000000000..b08f5f76cc68
--- /dev/null
+++ b/sys/i386/isa/sound/ad1848.c
@@ -0,0 +1,953 @@
+/*
+ * sound/ad1848.c
+ *
+ * The low level driver for the AD1848/CS4248 codec chip which
+ * is used for example in the MS Sound System.
+ *
+ * The CS4231 which is used in the GUS MAX and some other cards is
+ * upwards compatible with AD1848 and this driver is able to drive it.
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define DEB(x)
+#define DEB1(x)
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848)
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT 1
+#define IMODE_INPUT 2
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+typedef struct
+{
+ int base;
+ int irq;
+ int dma_capture, dma_playback;
+ unsigned char MCE_bit;
+
+ int speed;
+ unsigned char speed_bits;
+ int channels;
+ int audio_format;
+ unsigned char format_bits;
+
+ int xfer_count;
+ int irq_mode;
+ int intr_active;
+ int opened;
+ char *chip_name;
+ int mode;
+}
+
+ad1848_info;
+
+static int nr_ad1848_devs = 0;
+static char irq2dev[16] =
+{-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1};
+
+static int ad_format_mask[2 /*devc->mode*/ ] =
+{
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM
+};
+
+static ad1848_info dev_info[MAX_AUDIO_DEV];
+
+#define io_Index_Addr(d) ((d)->base)
+#define io_Indexed_Data(d) ((d)->base+1)
+#define io_Status(d) ((d)->base+2)
+#define io_Polled_IO(d) ((d)->base+3)
+
+static int ad1848_open (int dev, int mode);
+static void ad1848_close (int dev);
+static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
+static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int ad1848_prepare_for_IO (int dev, int bsize, int bcount);
+static void ad1848_reset (int dev);
+static void ad1848_halt (int dev);
+void ad1848_interrupt (int dev);
+
+static int
+ad_read (ad1848_info * devc, int reg)
+{
+ unsigned long flags;
+ int x;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
+ x = INB (io_Indexed_Data (devc));
+ /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
+ RESTORE_INTR (flags);
+
+ return x;
+}
+
+static void
+ad_write (ad1848_info * devc, int reg, int data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
+ OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc));
+ /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */
+ RESTORE_INTR (flags);
+}
+
+static void
+ad_set_MCE (ad1848_info * devc, int state)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (state)
+ devc->MCE_bit = 0x40;
+ else
+ devc->MCE_bit = 0x00;
+ OUTB (devc->MCE_bit, io_Index_Addr (devc));
+ RESTORE_INTR (flags);
+}
+
+static void
+wait_for_calibration (ad1848_info * devc)
+{
+ int timeout = 0;
+
+ /*
+ * Wait until the auto calibration process has finished.
+ *
+ * 1) Wait until the chip becomes ready (reads don't return 0x80).
+ * 2) Wait until the ACI bit of I11 gets on and then off.
+ */
+
+ timeout = 100000;
+ while (timeout > 0 && INB (devc->base) == 0x80)
+ timeout--;
+ if (INB (devc->base) == 0x80)
+ printk ("ad1848: Auto calibration timed out(1).\n");
+
+ timeout = 100000;
+ while (timeout > 0 && !(ad_read (devc, 11) & 0x20))
+ timeout--;
+ if (!(ad_read (devc, 11) & 0x20))
+ printk ("ad1848: Auto calibration timed out(2).\n");
+
+ timeout = 100000;
+ while (timeout > 0 && ad_read (devc, 11) & 0x20)
+ timeout--;
+ if (ad_read (devc, 11) & 0x20)
+ printk ("ad1848: Auto calibration timed out(3).\n");
+}
+
+static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] =
+{
+ {
+ "Generic AD1848 codec",
+ DMA_AUTOMODE,
+ AFMT_U8, /* Will be set later */
+ NULL,
+ ad1848_open,
+ ad1848_close,
+ ad1848_output_block,
+ ad1848_start_input,
+ ad1848_ioctl,
+ ad1848_prepare_for_IO,
+ ad1848_prepare_for_IO,
+ ad1848_reset,
+ ad1848_halt,
+ NULL,
+ NULL
+ }};
+
+static int
+ad1848_open (int dev, int mode)
+{
+ int err;
+ ad1848_info *devc = NULL;
+ unsigned long flags;
+
+ DEB (printk ("ad1848_open(int mode = %X)\n", mode));
+
+ if (dev < 0 || dev >= num_audiodevs)
+ return RET_ERROR (ENXIO);
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ if (devc->opened)
+ {
+ RESTORE_INTR (flags);
+ printk ("ad1848: Already opened\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (devc->irq) /* Not managed by another driver */
+ if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt)) < 0)
+ {
+ printk ("ad1848: IRQ in use\n");
+ RESTORE_INTR (flags);
+ return err;
+ }
+
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ RESTORE_INTR (flags);
+ printk ("ad1848: DMA in use\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ devc->intr_active = 0;
+ devc->opened = 1;
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+static void
+ad1848_close (int dev)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DEB (printk ("ad1848_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ devc->intr_active = 0;
+ if (devc->irq) /* Not managed by another driver */
+ snd_release_irq (devc->irq);
+ ad1848_reset (dev);
+ DMAbuf_close_dma (dev);
+ devc->opened = 0;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+set_speed (ad1848_info * devc, int arg)
+{
+ /*
+ * The sampling speed is encoded in the least significant nible of I8. The
+ * LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other
+ * three bits select the divisor (indirectly):
+ *
+ * The available speeds are in the following table. Keep the speeds in
+ * the increasing order.
+ */
+ typedef struct
+ {
+ int speed;
+ unsigned char bits;
+ }
+ speed_struct;
+
+ static speed_struct speed_table[] =
+ {
+ {5510, (0 << 1) | 1},
+ {5510, (0 << 1) | 1},
+ {6620, (7 << 1) | 1},
+ {8000, (0 << 1) | 0},
+ {9600, (7 << 1) | 0},
+ {11025, (1 << 1) | 1},
+ {16000, (1 << 1) | 0},
+ {18900, (2 << 1) | 1},
+ {22050, (3 << 1) | 1},
+ {27420, (2 << 1) | 0},
+ {32000, (3 << 1) | 0},
+ {33075, (6 << 1) | 1},
+ {37800, (4 << 1) | 1},
+ {44100, (5 << 1) | 1},
+ {48000, (6 << 1) | 0}
+ };
+
+ int i, n, selected = -1;
+
+ n = sizeof (speed_table) / sizeof (speed_struct);
+
+ if (arg < speed_table[0].speed)
+ selected = 0;
+ if (arg > speed_table[n - 1].speed)
+ selected = n - 1;
+
+ for (i = 1 /*really*/ ; selected == -1 && i < n; i++)
+ if (speed_table[i].speed == arg)
+ selected = i;
+ else if (speed_table[i].speed > arg)
+ {
+ int diff1, diff2;
+
+ diff1 = arg - speed_table[i - 1].speed;
+ diff2 = speed_table[i].speed - arg;
+
+ if (diff1 < diff2)
+ selected = i - 1;
+ else
+ selected = i;
+ }
+
+ if (selected == -1)
+ {
+ printk ("ad1848: Can't find speed???\n");
+ selected = 3;
+ }
+
+ devc->speed = speed_table[selected].speed;
+ devc->speed_bits = speed_table[selected].bits;
+ return devc->speed;
+}
+
+static int
+set_channels (ad1848_info * devc, int arg)
+{
+ if (arg != 1 && arg != 2)
+ return devc->channels;
+
+ devc->channels = arg;
+ return arg;
+}
+
+static int
+set_format (ad1848_info * devc, int arg)
+{
+
+ static struct format_tbl
+ {
+ int format;
+ unsigned char bits;
+ }
+ format2bits [] =
+ {
+ {
+ 0, 0
+ }
+ ,
+ {
+ AFMT_MU_LAW, 1
+ }
+ ,
+ {
+ AFMT_A_LAW, 3
+ }
+ ,
+ {
+ AFMT_IMA_ADPCM, 5
+ }
+ ,
+ {
+ AFMT_U8, 0
+ }
+ ,
+ {
+ AFMT_S16_LE, 2
+ }
+ ,
+ {
+ AFMT_S16_BE, 6
+ }
+ ,
+ {
+ AFMT_S8, 0
+ }
+ ,
+ {
+ AFMT_U16_LE, 0
+ }
+ ,
+ {
+ AFMT_U16_BE, 0
+ }
+ };
+ int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
+
+ if (!(arg & ad_format_mask[devc->mode]))
+ arg = AFMT_U8;
+
+ devc->audio_format = arg;
+
+ for (i = 0; i < n; i++)
+ if (format2bits[i].format == arg)
+ {
+ if ((devc->format_bits = format2bits[i].bits) == 0)
+ return devc->audio_format = AFMT_U8; /* Was not supported */
+
+ return arg;
+ }
+
+ /* Still hanging here. Something must be terribly wrong */
+ devc->format_bits = 0;
+ return devc->audio_format = AFMT_U8;
+}
+
+static int
+ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return set_speed (devc, arg);
+ return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return devc->speed;
+ return IOCTL_OUT (arg, devc->speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return set_channels (devc, arg + 1) - 1;
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1);
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return set_channels (devc, arg);
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return devc->channels;
+ return IOCTL_OUT (arg, devc->channels);
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return set_format (devc, arg);
+ return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return devc->audio_format;
+ return IOCTL_OUT (arg, devc->audio_format);
+
+ default:;
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ cnt = count;
+
+ if (devc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (devc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ ad1848_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+
+ ad_set_MCE (devc, 1);
+
+ ad_write (devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+
+
+ ad_write (devc, 9, 0x0d); /*
+ * Playback enable, single DMA channel mode,
+ * auto calibration on.
+ */
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+
+ devc->xfer_count = cnt;
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static void
+ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ /* int count_reg = (devc->mode == 1) ? 14 : 30; */
+
+ cnt = count;
+ if (devc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (devc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ ad1848_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ ad_set_MCE (devc, 1);
+#if 0
+ ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff));
+ ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff));
+#else
+ ad_write (devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+ if (devc->mode == 2)
+ {
+ ad_write (devc, 31, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 32, (unsigned char) ((cnt >> 8) & 0xff));
+ }
+#endif
+
+ ad_write (devc, 9, 0x0e); /*
+ * Capture enable, single DMA channel mode,
+ * auto calibration on.
+ */
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+
+ devc->xfer_count = cnt;
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static int
+ad1848_prepare_for_IO (int dev, int bsize, int bcount)
+{
+ int timeout;
+ unsigned char fs;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ ad_set_MCE (devc, 1); /* Enables changes to the format select reg */
+ fs = devc->speed_bits | (devc->format_bits << 5);
+
+ if (devc->channels > 1)
+ fs |= 0x10;
+
+ ad_write (devc, 8, fs);
+ /*
+ * Write to I8 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+ while (timeout > 0 && INB (devc->base) == 0x80)
+ timeout--;
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+ RESTORE_INTR (flags);
+
+ /*
+ * If mode == 2 (CS4231), set I28 also. It's the capture format register.
+ */
+ if (devc->mode == 2)
+ {
+ ad_set_MCE (devc, 1);
+ ad_write (devc, 28, fs);
+
+ /*
+ * Write to I28 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+ while (timeout > 0 && INB (devc->base) == 0x80)
+ timeout--;
+
+ ad_set_MCE (devc, 0); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ wait_for_calibration (devc);
+ RESTORE_INTR (flags);
+ }
+ devc->xfer_count = 0;
+ return 0;
+}
+
+static void
+ad1848_reset (int dev)
+{
+ ad1848_halt (dev);
+}
+
+static void
+ad1848_halt (int dev)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ ad_write (devc, 9, 0); /* Clear the PEN and CEN bits (among others) */
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+}
+
+int
+ad1848_detect (int io_base)
+{
+
+#define DDB(x) x
+
+ unsigned char tmp;
+ int i;
+ ad1848_info *devc = &dev_info[nr_ad1848_devs];
+ unsigned char tmp1 = 0xff, tmp2 = 0xff;
+
+ if (nr_ad1848_devs >= MAX_AUDIO_DEV)
+ return 0;
+
+ devc->base = io_base;
+ devc->MCE_bit = 0x40;
+ devc->irq = 0;
+ devc->dma_capture = 0;
+ devc->dma_playback = 0;
+ devc->opened = 0;
+ devc->chip_name = "AD1848";
+ devc->mode = 1; /* MODE1 = original AD1848 */
+
+ /*
+ * Check that the I/O address is in use.
+ *
+ * The bit 0x80 of the base I/O port is known to be 0 after the
+ * chip has performed it's power on initialization. Just assume
+ * this has happened before the OS is starting.
+ *
+ * If the I/O address is unused, it typically returns 0xff.
+ */
+
+ if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */
+ {
+ DDB (printk ("ad_detect_A\n"));
+ return 0;
+ }
+
+ /*
+ * Test if it's possible to change contents of the indirect registers.
+ * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
+ * so try to avoid using it.
+*/
+
+ ad_write (devc, 0, 0xaa);
+ ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
+
+ if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45)
+ {
+ DDB (printk ("ad_detect_B (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ ad_write (devc, 0, 0x45);
+ ad_write (devc, 1, 0xaa);
+
+ if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa)
+ {
+ DDB (printk ("ad_detect_C (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ /*
+ * The indirect register I12 has some read only bits. Lets
+ * try to change them.
+ */
+
+ tmp = ad_read (devc, 12);
+ ad_write (devc, 12, (~tmp) & 0x0f);
+
+ if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f))
+ {
+ DDB (printk ("ad_detect_D (%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+ * 0x01=RevB and 0x0A=RevC.
+ */
+
+ /*
+ * The original AD1848/CS4248 has just 15 indirect registers. This means
+ * that I0 and I16 should return the same value (etc.).
+ * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+ * with CS4231.
+ */
+
+ ad_write (devc, 12, 0); /* Mode2=disabled */
+
+ for (i = 0; i < 16; i++)
+ if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16)))
+ {
+ DDB (printk ("ad_detect_F(%d/%x/%x)\n", i, tmp1, tmp2));
+ return 0;
+ }
+
+ /*
+ * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
+ * The bit 0x80 is always 1 in CS4248 and CS4231.
+ */
+
+ ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */
+
+ tmp1 = ad_read (devc, 12);
+ if (tmp1 & 0x80)
+ devc->chip_name = "CS4248";
+
+ if ((tmp1 & 0xc0) == (0x80 | 0x40))
+ {
+ /*
+ * CS4231 detected - is it?
+ *
+ * Verify that setting I0 doesn't change I16.
+ */
+ ad_write (devc, 16, 0); /* Set I16 to known value */
+
+ ad_write (devc, 0, 0x45);
+ if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */
+ {
+
+ ad_write (devc, 0, 0xaa);
+ if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */
+ {
+ DDB (printk ("ad_detect_H(%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * It's a CS4231 - So what!
+ * (Mode2 will be supported later)
+ */
+ devc->chip_name = "CS4231";
+ devc->mode = 2;
+ }
+ }
+
+ return 1;
+}
+
+void
+ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture)
+{
+ /*
+ * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+ * so that this driver doesn't need to allocate/deallocate it.
+ * The actually used IRQ is ABS(irq).
+ */
+
+ /*
+ * Initial values for the indirect registers of CS4248/AD1848.
+ */
+ static int init_values[] =
+ {
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x08, 0x02, 0x00, 0xca, 0x00, 0x00, 0x00
+ };
+ int i, my_dev;
+ ad1848_info *devc = &dev_info[nr_ad1848_devs];
+
+ if (!ad1848_detect (io_base))
+ return;
+
+ devc->irq = (irq > 0) ? irq : 0;
+ devc->dma_capture = dma_playback;
+ devc->dma_playback = dma_capture;
+ devc->opened = 0;
+
+ if (nr_ad1848_devs != 0)
+ {
+ memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs],
+ (char *) &ad1848_pcm_operations[0],
+ sizeof (struct audio_operations));
+ }
+
+ for (i = 0; i < 16; i++)
+ ad_write (devc, i, init_values[i]);
+
+ OUTB (0, io_Status (devc)); /* Clear pending interrupts */
+
+#ifndef SCO
+ sprintf (ad1848_pcm_operations[nr_ad1848_devs].name,
+ "%s (%s)", name, devc->chip_name);
+#endif
+
+ if (irq > 0)
+ printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name);
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs];
+ if (irq > 0)
+ irq2dev[irq] = my_dev;
+ else if (irq < 0)
+ irq2dev[-irq] = my_dev;
+
+ audio_devs[my_dev]->dmachan = dma_playback;
+ audio_devs[my_dev]->buffcount = 1;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE * 2;
+ audio_devs[my_dev]->devc = devc;
+ audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode];
+ nr_ad1848_devs++;
+ }
+ else
+ printk ("AD1848: Too many PCM devices available\n");
+}
+
+void
+ad1848_interrupt (int irq)
+{
+ unsigned char status;
+ ad1848_info *devc;
+ int dev;
+
+ if (irq < 0 || irq > 15)
+ return; /* Bogus irq */
+ dev = irq2dev[irq];
+ if (dev < 0 || dev >= num_audiodevs)
+ return; /* Bogus dev */
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+ status = INB (io_Status (devc));
+
+ if (status & 0x01)
+ {
+ if (devc->opened && devc->irq_mode == IMODE_OUTPUT)
+ {
+ DMAbuf_outputintr (dev, 1);
+ }
+
+ if (devc->opened && devc->irq_mode == IMODE_INPUT)
+ DMAbuf_inputintr (dev);
+ }
+
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+}
+
+#endif
+/*
+ * Some extra code for the MS Sound System
+ */
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MSS)
+
+int
+probe_ms_sound (struct address_info *hw_config)
+{
+ if ((INB (hw_config->io_base + 3) & 0x04) == 0)
+ return 0; /* WSS ID test failed */
+
+ if (hw_config->irq > 11)
+ return 0;
+
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ return 0;
+
+ return ad1848_detect (hw_config->io_base + 4);
+}
+
+long
+attach_ms_sound (long mem_start, struct address_info *hw_config)
+{
+ static unsigned char interrupt_bits[12] =
+ {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
+ char bits;
+
+ static unsigned char dma_bits[4] = {1, 2, 0, 3};
+
+ int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
+
+ if (!ad1848_detect (hw_config->io_base + 4))
+ return mem_start;
+
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ return mem_start;
+
+ OUTB (bits | 0x40, config_port); /* Verify IRQ (I guess) */
+ if ((INB (version_port) & 0x40) == 0)
+ printk ("[IRQ?]");
+
+ OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
+
+ ad1848_init ("MS Sound System", hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/configure.c b/sys/i386/isa/sound/configure.c
new file mode 100644
index 000000000000..5b7217bccb46
--- /dev/null
+++ b/sys/i386/isa/sound/configure.c
@@ -0,0 +1,830 @@
+/*
+ * sound/configure.c - Configuration program for the Linux Sound Driver
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+
+#define B(x) (1 << (x))
+
+/*
+ * Option numbers
+ */
+
+#define OPT_PAS 0
+#define OPT_SB 1
+#define OPT_ADLIB 2
+#define OPT_LAST_MUTUAL 2
+
+#define OPT_GUS 3
+#define OPT_MPU401 4
+#define OPT_UART6850 5
+#define OPT_PSS 6
+#define OPT_GUS16 7
+#define OPT_GUSMAX 8
+#define OPT_MSS 9
+
+#define OPT_HIGHLEVEL 10 /* This must be same than the next one */
+#define OPT_SBPRO 10
+#define OPT_SB16 11
+#define OPT_AUDIO 12
+#define OPT_MIDI_AUTO 13
+#define OPT_MIDI 14
+#define OPT_YM3812_AUTO 15
+#define OPT_YM3812 16
+#define OPT_SEQUENCER 17
+#define OPT_LAST 17 /* Must be the same than the defined OPT */
+
+#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)| \
+ B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)|B(OPT_MSS))
+/*
+ * Options that have been disabled for some reason (incompletely implemented
+ * and/or tested). Don't remove from this list before looking at file
+ * experimental.txt for further info.
+ */
+#define DISABLED_OPTIONS (B(OPT_PSS))
+
+typedef struct
+ {
+ unsigned long conditions;
+ unsigned long exclusive_options;
+ char macro[20];
+ int verify;
+ int alias;
+ int default_answ;
+ }
+
+hw_entry;
+
+
+/*
+ * The rule table for the driver options. The first field defines a set of
+ * options which must be selected before this entry can be selected. The
+ * second field is a set of options which are not allowed with this one. If
+ * the fourth field is zero, the option is selected without asking
+ * confirmation from the user.
+ *
+ * With this version of the rule table it is possible to select just one type of
+ * hardware.
+ *
+ * NOTE! Keep the following table and the questions array in sync with the
+ * option numbering!
+ */
+
+hw_entry hw_table[] =
+{
+/*
+ * 0
+ */
+ {0, 0, "PAS", 1, 0, 0},
+ {0, 0, "SB", 1, 0, 0},
+ {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0},
+
+ {0, 0, "GUS", 1, 0, 0},
+ {0, 0, "MPU401", 1, 0, 0},
+ {0, 0, "UART6850", 1, 0, 0},
+ {0, 0, "PSS", 1, 0, 0},
+ {B (OPT_GUS), 0, "GUS16", 1, 0, 0},
+ {B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0},
+ {0, 0, "MSS", 1, 0, 0},
+
+ {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1},
+ {B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1},
+{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0, 1},
+ {B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI, 0},
+ {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0, 1},
+ {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
+ {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1},
+ {B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0, 1}
+};
+
+char *questions[] =
+{
+ "ProAudioSpectrum 16 support",
+ "SoundBlaster support",
+ "AdLib support",
+ "Gravis Ultrasound support",
+ "MPU-401 support (NOT for SB16)",
+ "6850 UART Midi support",
+ "PSS (ECHO-ADI2111) support",
+ "16 bit sampling option of GUS (_NOT_ GUS MAX)",
+ "GUS MAX support",
+ "Microsoft Sound System support",
+
+ "SoundBlaster Pro support",
+ "SoundBlaster 16 support",
+ "digitized voice support",
+ "This should not be asked",
+ "MIDI interface support",
+ "This should not be asked",
+ "FM synthesizer (YM3812/OPL-3) support",
+ "/dev/sequencer support",
+ "Should I die"
+};
+
+unsigned long selected_options = 0;
+int sb_dma = 0;
+
+int
+can_select_option (int nr)
+{
+ switch (nr)
+ {
+ case 0:
+ fprintf (stderr, "The SoundBlaster, AdLib and ProAudioSpectrum\n"
+ "CARDS cannot be installed at the same time.\n\n"
+ "However the PAS16 has a SB emulator so you could select"
+ "the SoundBlaster DRIVER with it.\n");
+ fprintf (stderr, " - ProAudioSpectrum 16\n");
+ fprintf (stderr, " - SoundBlaster / SB Pro\n");
+ fprintf (stderr, " (Could be selected with a PAS16 also)\n");
+ fprintf (stderr, " - AdLib\n");
+ fprintf (stderr, "\nDon't enable SoundBlaster if you have GUS at 0x220!\n\n");
+ break;
+
+ case OPT_LAST_MUTUAL + 1:
+ fprintf (stderr, "\nThe following cards should work with any other cards.\n"
+ "CAUTION! Don't enable MPU-401 if you don't have it.\n");
+ break;
+
+ case OPT_HIGHLEVEL:
+ fprintf (stderr, "\nSelect one or more of the following options\n");
+ break;
+
+
+ }
+
+ if (hw_table[nr].conditions)
+ if (!(hw_table[nr].conditions & selected_options))
+ return 0;
+
+ if (hw_table[nr].exclusive_options)
+ if (hw_table[nr].exclusive_options & selected_options)
+ return 0;
+
+ if (DISABLED_OPTIONS & B (nr))
+ return 0;
+
+ return 1;
+}
+
+int
+think_positively (int def_answ)
+{
+ char answ[512];
+ int len;
+
+ if ((len = read (0, &answ, sizeof (answ))) < 1)
+ {
+ fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
+
+ perror ("stdin");
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (-1);
+ }
+
+ if (len < 2) /*
+ * There is an additional LF at the end
+ */
+ return def_answ;
+
+ answ[len - 1] = 0;
+
+ if (!strcmp (answ, "y") || !strcmp (answ, "Y"))
+ return 1;
+
+ return 0;
+}
+
+int
+ask_value (char *format, int default_answer)
+{
+ char answ[512];
+ int len, num;
+
+play_it_again_Sam:
+
+ if ((len = read (0, &answ, sizeof (answ))) < 1)
+ {
+ fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
+
+ perror ("stdin");
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (-1);
+ }
+
+ if (len < 2) /*
+ * There is an additional LF at the end
+ */
+ return default_answer;
+
+ answ[len - 1] = 0;
+
+ if (sscanf (answ, format, &num) != 1)
+ {
+ fprintf (stderr, "Illegal format. Try again: ");
+ goto play_it_again_Sam;
+ }
+
+ return num;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i, num, def_size, full_driver = 1;
+ char answ[10];
+
+ printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n");
+
+ fprintf (stderr, "\nConfiguring the sound support\n\n");
+
+ fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? ");
+
+ if (think_positively (0))
+ {
+ /*
+ * Select all but some most dangerous cards. These cards are difficult to
+ * detect reliably or conflict with some other cards (SCSI, Mitsumi)
+ */
+ selected_options = 0xffffffff &
+ ~(B (OPT_MPU401) | B (OPT_UART6850) | B (OPT_PSS)) &
+ ~DISABLED_OPTIONS;
+
+ fprintf (stderr, "Note! MPU-401, PSS and 6850 UART drivers not enabled\n");
+ full_driver = 1;
+ }
+ else
+ {
+ fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?");
+ if (think_positively (0))
+ {
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (0);
+ }
+ /*
+ * Partial driver
+ */
+
+ full_driver = 0;
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (can_select_option (i))
+ {
+ if (!(selected_options & B (i))) /*
+ * Not selected yet
+ */
+ if (!hw_table[i].verify)
+ {
+ if (hw_table[i].alias)
+ selected_options |= B (hw_table[i].alias);
+ else
+ selected_options |= B (i);
+ }
+ else
+ {
+ int def_answ = hw_table[i].default_answ;
+
+ fprintf (stderr,
+ def_answ ? " %s (y/n) ? " : " %s (n/y) ? ",
+ questions[i]);
+ if (think_positively (def_answ))
+ if (hw_table[i].alias)
+ selected_options |= B (hw_table[i].alias);
+ else
+ selected_options |= B (i);
+ }
+ }
+ }
+
+ if (selected_options & B (OPT_SBPRO))
+ {
+ fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
+ if (think_positively (0))
+ printf("#define __SGNXPRO__\n");
+ }
+
+ if (selected_options & B (OPT_SB16))
+ selected_options |= B (OPT_SBPRO);
+
+ if (selected_options & B (OPT_PSS))
+ {
+ genld_again:
+ fprintf
+ (stderr,
+ "if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
+ "then you must include the LD in the kernel.\n"
+ "(do you wish to include a LD) ? ");
+ if (think_positively (0))
+ {
+ char path[512];
+
+ fprintf (stderr,
+ "Enter the path to your LD file (pwd is sound): ");
+ scanf ("%s", path);
+ fprintf (stderr, "including LD file %s\n", path);
+ selected_options |= B (OPT_SB) | B (OPT_MPU401) | B (OPT_ADLIB);
+
+ /* Gen LD header */
+ {
+ int fd;
+ int count;
+ char c;
+ int i = 0;
+
+ if ((fd = open (path, 0)) > 0)
+ {
+ FILE *sf = fopen ("synth-ld.h", "w");
+
+ fprintf (sf, "/* automaticaly generated by configure */\n");
+ fprintf (sf, "unsigned char pss_synth[] = {\n");
+ while (1)
+ {
+ count = read (fd, &c, 1);
+ if (count == 0)
+ break;
+ if (i != 0 && (i % 10) == 0)
+ fprintf (sf, "\n");
+ fprintf (sf, "0x%02x,", c & 0xFFL);
+ i++;
+ }
+ fprintf (sf, "};\n"
+ "#define pss_synthLen %d\n", i);
+ fclose (sf);
+ close (fd);
+ }
+ else
+ {
+ fprintf (stderr, "couldn't open %s as the ld file\n",
+ path);
+ fprintf (stderr, "try again with correct path? ");
+ if (think_positively (1))
+ goto genld_again;
+ }
+ }
+ }
+ else
+ {
+ FILE *sf = fopen ("synth-ld.h", "w");
+
+ fprintf (sf, "/* automaticaly generated by configure */\n");
+ fprintf (sf, "unsigned char pss_synth[1];\n"
+ "#define pss_synthLen 0\n");
+ fclose (sf);
+ }
+ }
+
+ if (!(selected_options & ANY_DEVS))
+ {
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n\n");
+ exit (0);
+ }
+ else
+ printf ("#define KERNEL_SOUNDCARD\n");
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (!hw_table[i].alias)
+ if (selected_options & B (i))
+ printf ("#undef EXCLUDE_%s\n", hw_table[i].macro);
+ else
+ printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
+
+
+ /*
+ * IRQ and DMA settings
+ */
+ printf ("\n");
+
+#if defined(linux)
+ if ((selected_options & B (OPT_SB)) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
+ {
+ fprintf (stderr, "\nI/O base for SB?\n"
+ "The factory default is 220\n"
+ "Enter the SB I/O base: ");
+
+ num = ask_value ("%x", 0x220);
+ fprintf (stderr, "SB I/O base set to %03x\n", num);
+ printf ("#define SBC_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for SoundBlaster?\n"
+ "The IRQ address is defined by the jumpers on your card.\n"
+ "The factory default is either 5 or 7 (depending on the model).\n"
+ "Valid values are 9(=2), 5, 7 and 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 7);
+ if (num != 9 && num != 5 && num != 7 && num != 10)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "SoundBlaster IRQ set to %d\n", num);
+
+ printf ("#define SBC_IRQ %d\n", num);
+
+ if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_PSS)))
+ {
+ fprintf (stderr, "\nDMA channel for SoundBlaster?\n"
+ "For SB 1.0, 1.5 and 2.0 this MUST be 1\n"
+ "SB Pro supports DMA channels 0, 1 and 3 (jumper)\n"
+ "For SB16 give the 8 bit DMA# here\n"
+ "The default value is 1\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 1);
+ if (num < 0 || num > 3)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 1;
+ }
+ fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
+ printf ("#define SBC_DMA %d\n", num);
+ sb_dma = num;
+ }
+
+ if (selected_options & B (OPT_SB16))
+ {
+
+ fprintf (stderr, "\n16 bit DMA channel for SoundBlaster 16?\n"
+ "Possible values are 5, 6 or 7\n"
+ "The default value is 6\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 6);
+ if ((num < 5 || num > 7) && (num != sb_dma))
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 6;
+ }
+ fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
+ printf ("#define SB16_DMA %d\n", num);
+
+ fprintf (stderr, "\nI/O base for SB16 Midi?\n"
+ "Possible values are 300 and 330\n"
+ "The factory default is 330\n"
+ "Enter the SB16 Midi I/O base: ");
+
+ num = ask_value ("%x", 0x330);
+ fprintf (stderr, "SB16 Midi I/O base set to %03x\n", num);
+ printf ("#define SB16MIDI_BASE 0x%03x\n", num);
+ }
+ }
+
+ if (selected_options & B (OPT_PAS))
+ {
+ if (selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
+ {
+ fprintf (stderr, "\nIRQ number for ProAudioSpectrum?\n"
+ "The recommended value is the IRQ used under DOS.\n"
+ "Please refer to the ProAudioSpectrum User's Guide.\n"
+ "The default value is 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 10);
+ if (num == 6 || num < 3 || num > 15 || num == 2) /*
+ * Illegal
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 10;
+ }
+ fprintf (stderr, "ProAudioSpectrum IRQ set to %d\n", num);
+ printf ("#define PAS_IRQ %d\n", num);
+ }
+
+ if (selected_options & B (OPT_AUDIO))
+ {
+ fprintf (stderr, "\nDMA number for ProAudioSpectrum?\n"
+ "The recommended value is the DMA channel under DOS.\n"
+ "Please refer to the ProAudioSpectrum User's Guide.\n"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num == 4 || num < 0 || num > 7)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nProAudioSpectrum DMA set to %d\n", num);
+ printf ("#define PAS_DMA %d\n", num);
+ }
+ }
+
+ if (selected_options & B (OPT_GUS))
+ {
+ fprintf (stderr, "\nI/O base for Gravis Ultrasound?\n"
+ "Valid choices are 210, 220, 230, 240, 250 or 260\n"
+ "The factory default is 220\n"
+ "Enter the GUS I/O base: ");
+
+ num = ask_value ("%x", 0x220);
+ if ((num > 0x260) || ((num & 0xf0f) != 0x200) || ((num & 0x0f0) > 0x060))
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 0x220;
+ }
+
+ if ((selected_options & B (OPT_SB)) && (num == 0x220))
+ {
+ fprintf (stderr, "FATAL ERROR!!!!!!!!!!!!!!\n"
+ "\t0x220 cannot be used if SoundBlaster is enabled.\n"
+ "\tRun the config again.\n");
+ printf ("#undef CONFIGURE_SOUNDCARD\n");
+ printf ("#undef KERNEL_SOUNDCARD\n");
+ exit (-1);
+ }
+ fprintf (stderr, "GUS I/O base set to %03x\n", num);
+ printf ("#define GUS_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for Gravis UltraSound?\n"
+ "The recommended value is the IRQ used under DOS.\n"
+ "Please refer to the Gravis Ultrasound User's Guide.\n"
+ "The default value is 15.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 15);
+ if (num == 6 || num < 3 || num > 15 || num == 2) /*
+ * Invalid
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 15;
+ }
+ fprintf (stderr, "Gravis UltraSound IRQ set to %d\n", num);
+ printf ("#define GUS_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for Gravis UltraSound?\n"
+ "The recommended value is the DMA channel under DOS.\n"
+ "Please refer to the Gravis Ultrasound User's Guide.\n"
+ "The default value is 6\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 6);
+ if (num == 4 || num < 0 || num > 7)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 6;
+ }
+ fprintf (stderr, "\nGravis UltraSound DMA set to %d\n", num);
+ printf ("#define GUS_DMA %d\n", num);
+ }
+
+ if (selected_options & B (OPT_MPU401))
+ {
+ fprintf (stderr, "\nI/O base for MPU-401?\n"
+ "The factory default is 330\n"
+ "Enter the MPU-401 I/O base: ");
+
+ num = ask_value ("%x", 0x330);
+ fprintf (stderr, "MPU-401 I/O base set to %03x\n", num);
+ printf ("#define MPU_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for MPU-401?\n"
+ "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
+ "The default value is 9.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 9);
+ if (num == 6 || num < 3 || num > 15) /*
+ * Used for floppy
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 5;
+ }
+ fprintf (stderr, "MPU-401 IRQ set to %d\n", num);
+ printf ("#define MPU_IRQ %d\n", num);
+ }
+
+ if (selected_options & B (OPT_UART6850))
+ {
+ fprintf (stderr, "\nI/O base for 6850 UART Midi?\n"
+ "Be carefull. No defaults.\n"
+ "Enter the 6850 UART I/O base: ");
+
+ num = ask_value ("%x", 0);
+ if (num == 0)
+ {
+ /*
+ * Invalid value entered
+ */
+ printf ("#define EXCLUDE_UART6850\n");
+ }
+ else
+ {
+ fprintf (stderr, "6850 UART I/O base set to %03x\n", num);
+ printf ("#define U6850_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for 6850 UART?\n"
+ "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
+ "The default value is 5.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 5);
+ if (num == 6 || num < 3 || num > 15) /*
+ * Used for floppy
+ */
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 5;
+ }
+ fprintf (stderr, "6850 UART IRQ set to %d\n", num);
+ printf ("#define U6850_IRQ %d\n", num);
+ }
+ }
+
+ if (selected_options & B (OPT_PSS))
+ {
+ fprintf (stderr, "\nI/O base for PSS?\n"
+ "The factory default is 220\n"
+ "Enter the PSS I/O base: ");
+
+ num = ask_value ("%x", 0x220);
+ fprintf (stderr, "PSS I/O base set to %03x\n", num);
+ printf ("#define PSS_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for PSS?\n"
+ "Valid numbers are: 3, 4, 5, 7, 9(=2) or 10.\n"
+ "The default value is 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 10);
+ if (num == 6 || num < 3 || num > 15) /* Used for floppy */
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "PSS IRQ set to %d\n", num);
+ printf ("#define PSS_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for ECHO-PSS?\n"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num == 4 || num < 0 || num > 7)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nECHO-PSS DMA set to %d\n", num);
+ printf ("#define PSS_DMA %d\n", num);
+ }
+
+ if (selected_options & B (OPT_MSS))
+ {
+ fprintf (stderr, "\nI/O base for MSS (MS Sound System)?\n"
+ "The factory default is 530\n"
+ "Other possible values are 604, E80 or F40\n"
+ "Enter the MSS I/O base: ");
+
+ num = ask_value ("%x", 0x530);
+ fprintf (stderr, "MSS I/O base set to %03x\n", num);
+ printf ("#define MSS_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for MSS?\n"
+ "Valid numbers are: 7, 9(=2), 10 and 11.\n"
+ "The default value is 10.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 10);
+ if (num == 6 || num < 3 || num > 15) /* Used for floppy */
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "MSS IRQ set to %d\n", num);
+ printf ("#define MSS_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for MSS?\n"
+ "Valid values are 1 and 3 (sometimes 0)"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num == 4 || num < 0 || num > 7)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nMSS DMA set to %d\n", num);
+ printf ("#define MSS_DMA %d\n", num);
+ }
+
+ if (selected_options & B (OPT_GUS16))
+ {
+ fprintf (stderr, "\nI/O base for GUS16 (GUS 16 bit sampling option)?\n"
+ "The factory default is 530\n"
+ "Other possible values are 604, E80 or F40\n"
+ "Enter the GUS16 I/O base: ");
+
+ num = ask_value ("%x", 0x530);
+ fprintf (stderr, "GUS16 I/O base set to %03x\n", num);
+ printf ("#define GUS16_BASE 0x%03x\n", num);
+
+ fprintf (stderr, "\nIRQ number for GUS16?\n"
+ "Valid numbers are: 3, 4, 5, 7, or 9(=2).\n"
+ "The default value is 7.\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 7);
+ if (num == 6 || num < 3 || num > 15) /* Used for floppy */
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 7;
+ }
+ fprintf (stderr, "GUS16 IRQ set to %d\n", num);
+ printf ("#define GUS16_IRQ %d\n", num);
+
+ fprintf (stderr, "\nDMA number for GUS16?\n"
+ "The default value is 3\n"
+ "Enter the value: ");
+
+ num = ask_value ("%d", 3);
+ if (num < 0 || num > 3)
+ {
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = 3;
+ }
+ fprintf (stderr, "\nGUS16 DMA set to %d\n", num);
+ printf ("#define GUS16_DMA %d\n", num);
+ }
+#endif
+
+ if (selected_options & B (OPT_AUDIO))
+ {
+ def_size = 16384;
+
+ if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_SB16)))
+ def_size = 32768;
+
+#ifndef __386BSD__
+ if ((selected_options & (B (OPT_PAS) | B (OPT_PAS) | B (OPT_GUS16) | B (OPT_GUSMAX) |
+ B (OPT_MSS) | B (OPT_PSS))) &&
+ !full_driver)
+ def_size = 65536; /*
+ * PAS16 or SB16
+ */
+#endif
+
+ fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n"
+ "%d is recommended value for this configuration.\n"
+ "Enter the value: ", def_size);
+
+ num = ask_value ("%d", def_size);
+ if (num != 4096 && num != 16384 && num != 32768 && num != 65536)
+ {
+
+ fprintf (stderr, "*** Illegal input! ***\n");
+ num = def_size;
+ }
+ fprintf (stderr, "The DMA buffer size set to %d\n", num);
+ printf ("#define DSP_BUFFSIZE %d\n", num);
+ }
+
+ printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
+ fprintf (stderr, "The sound driver is now configured.\n");
+
+#if defined(SCO) || defined(ISC) || defined(SYSV)
+ fprintf (stderr, "Remember to update the System file\n");
+#endif
+
+ exit (0);
+}
diff --git a/sys/i386/isa/sound/midi_ctrl.h b/sys/i386/isa/sound/midi_ctrl.h
new file mode 100644
index 000000000000..616b5914c6e2
--- /dev/null
+++ b/sys/i386/isa/sound/midi_ctrl.h
@@ -0,0 +1,22 @@
+static unsigned char ctrl_def_values[128] =
+{
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 0 to 7 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 8 to 15 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 16 to 23 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 24 to 31 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
+};
diff --git a/sys/i386/isa/sound/midi_synth.c b/sys/i386/isa/sound/midi_synth.c
new file mode 100644
index 000000000000..fd6d8bde62cd
--- /dev/null
+++ b/sys/i386/isa/sound/midi_synth.c
@@ -0,0 +1,474 @@
+/*
+ * sound/midi_synth.c
+ *
+ * High level midi sequencer manager for dumb MIDI interfaces.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI)
+
+#define _MIDI_SYNTH_C_
+
+DEFINE_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
+
+#include "midi_synth.h"
+
+static int midi2synth[MAX_MIDI_DEV];
+static unsigned char prev_out_status[MAX_MIDI_DEV];
+
+static void
+midi_outc (int midi_dev, int data)
+{
+ int timeout;
+
+ for (timeout = 0; timeout < 32000; timeout++)
+ if (midi_devs[midi_dev]->putc (midi_dev, (unsigned char) (data & 0xff)))
+ {
+ if (data & 0x80) /*
+ * Status byte
+ */
+ prev_out_status[midi_dev] =
+ (unsigned char) (data & 0xff); /*
+ * Store for running status
+ */
+ return; /*
+ * Mission complete
+ */
+ }
+
+ /*
+ * Sorry! No space on buffers.
+ */
+ printk ("Midi send timed out\n");
+}
+
+static int
+prefix_cmd (int midi_dev, unsigned char status)
+{
+ if (midi_devs[midi_dev]->prefix_cmd == NULL)
+ return 1;
+
+ return midi_devs[midi_dev]->prefix_cmd (midi_dev, status);
+}
+
+static void
+midi_synth_input (int dev, unsigned char data)
+{
+ int orig_dev;
+
+ if (dev < 0 || dev > num_synths)
+ return;
+
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
+
+ orig_dev = midi2synth[dev];
+
+}
+
+static void
+midi_synth_output (int dev)
+{
+ /*
+ * Currently NOP
+ */
+}
+
+int
+midi_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ /*
+ * int orig_dev = synth_devs[dev]->midi_dev;
+ */
+
+ switch (cmd)
+ {
+
+ case SNDCTL_SYNTH_INFO:
+ IOCTL_TO_USER ((char *) arg, 0, synth_devs[dev]->info,
+ sizeof (struct synth_info));
+
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+int
+midi_synth_kill_note (int dev, int channel, int note, int velocity)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
+ { /*
+ * Use running status
+ */
+ if (!prefix_cmd (orig_dev, note))
+ return 0;
+
+ midi_outc (orig_dev, note);
+
+ if (msg == 0x90) /*
+ * Running status = Note on
+ */
+ midi_outc (orig_dev, 0);/*
+ * Note on with velocity 0 == note
+ * off
+ */
+ else
+ midi_outc (orig_dev, velocity);
+ }
+ else
+ {
+ if (velocity == 64)
+ {
+ if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, 0); /*
+ * Zero G
+ */
+ }
+ else
+ {
+ if (!prefix_cmd (orig_dev, 0x80 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x80 | (channel & 0x0f)); /*
+ * Note off
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ }
+
+ return 0;
+}
+
+int
+midi_synth_set_instr (int dev, int channel, int instr_no)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ if (instr_no < 0 || instr_no > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+
+ if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /*
+ * Program change
+ */
+ midi_outc (orig_dev, instr_no);
+
+ return 0;
+}
+
+int
+midi_synth_start_note (int dev, int channel, int note, int velocity)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (chn == channel && msg == 0x90)
+ { /*
+ * Use running status
+ */
+ if (!prefix_cmd (orig_dev, note))
+ return 0;
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ else
+ {
+ if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ return 0;
+}
+
+void
+midi_synth_reset (int dev)
+{
+}
+
+int
+midi_synth_open (int dev, int mode)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int err;
+
+ if (orig_dev < 0 || orig_dev > num_midis)
+ return RET_ERROR (ENXIO);
+
+ midi2synth[orig_dev] = dev;
+ prev_out_status[orig_dev] = 0;
+
+ if ((err = midi_devs[orig_dev]->open (orig_dev, mode,
+ midi_synth_input, midi_synth_output)) < 0)
+ return err;
+
+ return 1;
+}
+
+void
+midi_synth_close (int dev)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ /*
+ * Shut up the synths by sending just single active sensing message.
+ */
+ midi_devs[orig_dev]->putc (orig_dev, 0xfe);
+
+ midi_devs[orig_dev]->close (orig_dev);
+}
+
+void
+midi_synth_hw_control (int dev, unsigned char *event)
+{
+}
+
+int
+midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ struct sysex_info sysex;
+ int i;
+ unsigned long left, src_offs, eox_seen = 0;
+ int first_byte = 1;
+
+ if (!prefix_cmd (orig_dev, 0xf0))
+ return 0;
+
+ if (format != SYSEX_PATCH)
+ {
+ printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < sizeof (struct sysex_info))
+ {
+ printk ("MIDI Error: Patch header too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ count -= sizeof (struct sysex_info);
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ COPY_FROM_USER (&((char *) &sysex)[offs], addr, offs, sizeof (struct sysex_info) - offs);
+
+ if (count < sysex.len)
+ {
+ printk ("MIDI Warning: Sysex record too short (%d<%d)\n",
+ count, (int) sysex.len);
+ sysex.len = count;
+ }
+
+ left = sysex.len;
+ src_offs = 0;
+
+ RESET_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
+
+ for (i = 0; i < left && !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag); i++)
+ {
+ unsigned char data;
+
+ GET_BYTE_FROM_USER (data, addr, sizeof (struct sysex_info) + i);
+
+ if (first_byte && data != 0xf0)
+ midi_outc (orig_dev, 0xf0); /* Sysex start */
+
+ eox_seen = (data == 0xf7);/*
+ * Last byte was end of sysex
+ */
+
+ if (i == 0)
+ {
+ if (data != 0xf0) /*
+ * Sysex start
+ */
+ return RET_ERROR (EINVAL);
+ }
+
+ while (!midi_devs[orig_dev]->putc (orig_dev, (unsigned char) (data & 0xff)) &&
+ !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag))
+ DO_SLEEP (sysex_sleeper, sysex_sleep_flag, 1); /* Wait for timeout */
+
+ if (!first_byte && data & 0x80)
+ return 0;
+ first_byte = 0;
+ }
+
+ if (!eox_seen)
+ midi_outc (orig_dev, 0xf7);
+ return 0;
+}
+
+void
+midi_synth_panning (int dev, int channel, int pressure)
+{
+}
+
+void
+midi_synth_aftertouch (int dev, int channel, int pressure)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (pressure < 0 || pressure > 127)
+ return;
+ if (channel < 0 || channel > 15)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xd0 || chn != channel) /*
+ * Test for running status
+ */
+ {
+ if (!prefix_cmd (orig_dev, 0xd0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xd0 | (channel & 0x0f)); /*
+ * Channel pressure
+ */
+ }
+ else if (!prefix_cmd (orig_dev, pressure))
+ return;
+ midi_outc (orig_dev, pressure);
+}
+
+void
+midi_synth_controller (int dev, int channel, int ctrl_num, int value)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int chn, msg;
+
+ if (ctrl_num < 1 || ctrl_num > 127)
+ return; /* NOTE! Controller # 0 ignored */
+ if (channel < 0 || channel > 15)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xb0 || chn != channel)
+ {
+ if (!prefix_cmd (orig_dev, 0xb0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xb0 | (channel & 0x0f));
+ }
+ else if (!prefix_cmd (orig_dev, ctrl_num))
+ return;
+
+ midi_outc (orig_dev, ctrl_num);
+ midi_outc (orig_dev, value & 0x7f);
+}
+
+int
+midi_synth_patchmgr (int dev, struct patmgr_info *rec)
+{
+ return RET_ERROR (EINVAL);
+}
+
+void
+midi_synth_bender (int dev, int channel, int value)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, prev_chn;
+
+ if (channel < 0 || channel > 15)
+ return;
+
+ if (value < 0 || value > 16383)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ prev_chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xd0 || prev_chn != channel) /*
+ * * Test for running status */
+ {
+ if (!prefix_cmd (orig_dev, 0xe0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xe0 | (channel & 0x0f));
+ }
+ else if (!prefix_cmd (orig_dev, value & 0x7f))
+ return;
+
+ midi_outc (orig_dev, value & 0x7f);
+ midi_outc (orig_dev, (value >> 7) & 0x7f);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/midi_synth.h b/sys/i386/isa/sound/midi_synth.h
new file mode 100644
index 000000000000..04075e294ca6
--- /dev/null
+++ b/sys/i386/isa/sound/midi_synth.h
@@ -0,0 +1,45 @@
+int midi_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg);
+int midi_synth_kill_note (int dev, int channel, int note, int velocity);
+int midi_synth_set_instr (int dev, int channel, int instr_no);
+int midi_synth_start_note (int dev, int channel, int note, int volume);
+void midi_synth_reset (int dev);
+int midi_synth_open (int dev, int mode);
+void midi_synth_close (int dev);
+void midi_synth_hw_control (int dev, unsigned char *event);
+int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag);
+void midi_synth_panning (int dev, int channel, int pressure);
+void midi_synth_aftertouch (int dev, int channel, int pressure);
+void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
+int midi_synth_patchmgr (int dev, struct patmgr_info *rec);
+void midi_synth_bender (int dev, int chn, int value);
+
+
+#ifndef _MIDI_SYNTH_C_
+static struct synth_info std_synth_info =
+{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
+
+static struct synth_operations std_midi_synth =
+{
+ &std_synth_info,
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ midi_synth_open,
+ midi_synth_close,
+ midi_synth_ioctl,
+ midi_synth_kill_note,
+ midi_synth_start_note,
+ midi_synth_set_instr,
+ midi_synth_reset,
+ midi_synth_hw_control,
+ midi_synth_load_patch,
+ midi_synth_aftertouch,
+ midi_synth_controller,
+ midi_synth_panning,
+ NULL,
+ midi_synth_patchmgr,
+ midi_synth_bender
+};
+#endif
diff --git a/sys/i386/isa/sound/pss.c b/sys/i386/isa/sound/pss.c
new file mode 100644
index 000000000000..6a0e7a8327ff
--- /dev/null
+++ b/sys/i386/isa/sound/pss.c
@@ -0,0 +1,924 @@
+/* Marc.Hoffman@analog.com
+
+ This is a pss driver.
+
+ it is based on Greg.Yukna@analog.com @file{host} for DOG
+
+ Unfortunately I can't distribute the ld file needed to
+ make the pss card to emulate the SB stuff.
+
+ I have provided a simple interface to the PSS unlike the
+ DOG version. to download a new algorithim just cat it to
+ /dev/pss 14,9.
+
+ You really need to rebuild this with the synth.ld file
+
+ get the <synth>.ld from your dos directory maybe
+ voyetra\dsp001.ld
+
+ ld2inc < synth.ld > synth-ld.h
+ (make config does the same).
+
+ rebuild
+
+ Okay if you blow things away no problem just
+
+ main(){ioctl(open("/dev/pss"),SNDCTL_PSS_RESET)};
+
+ and everything will be okay.
+
+ At first I was going to wory about applications that were using
+ the sound stuff and disallow the use of /dev/pss. But for
+ now I figured it doesn't matter.
+
+ And if you change algos all the other applications running die off
+ due to DMA problems. Yeah just pull the plug and watch em die.
+
+ If the registers get hosed
+ main(){ioctl(open("/dev/pss"),SNDCTL_PSS_SETUP_REGISTERS)};
+
+ Probably everything else can be done via mmap
+
+ Oh if you want to develope code for the ADSP-21xx or Program the
+ 1848 just send me mail and I will hook you up.
+
+ marc.hoffman@analog.com
+
+ */
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PSS)
+
+#ifndef PSS_MSS_BASE
+#define PSS_MSS_BASE 0
+#endif
+
+#ifndef PSS_MPU_BASE
+#define PSS_MPU_BASE 0
+#endif
+
+#ifndef PSS_MPU_IRQ
+#define PSS_MPU_IRQ 0
+#endif
+
+#undef DEB
+#define DEB(x) x
+
+#include "pss.h"
+
+static int pss_ok = 0;
+static int sb_ok = 0;
+
+static int pss_base;
+static int pss_irq;
+static int pss_dma;
+
+static int gamePort = 0;
+
+static int sbInt;
+static int cdPol;
+static int cdAddr = 0; /* 0x340; */
+static int cdInt = 10;
+
+/* Define these by hand in local.h */
+static int wssAddr = PSS_MSS_BASE;
+static int midiAddr = PSS_MPU_BASE;
+static int midiInt = PSS_MPU_IRQ;
+
+static int SoundPortAddress;
+static int SoundPortData;
+static int speaker = 1;
+
+
+static struct pss_speaker default_speaker =
+{0, 0, 0, PSS_STEREO};
+
+DEFINE_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
+
+#include "synth-ld.h"
+
+static int pss_download_boot (unsigned char *block, int size);
+static int pss_reset_dsp (void);
+
+static inline void
+pss_outpw (unsigned short port, unsigned short value)
+{
+ __asm__ __volatile__ ("outw %w0, %w1"
+ : /* no outputs */
+ :"a" (value), "d" (port));
+}
+
+static inline unsigned int
+pss_inpw (unsigned short port)
+{
+ unsigned int _v;
+ __asm__ __volatile__ ("inw %w1,%w0"
+ :"=a" (_v):"d" (port), "0" (0));
+
+ return _v;
+}
+
+static void
+PSS_write (int data)
+{
+ int i, limit;
+
+ limit = GET_TIME () + 10; /* The timeout is 0.1 secods */
+ /*
+ * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 5000000 && GET_TIME () < limit; i++)
+ {
+ if (pss_inpw (pss_base + PSS_STATUS) & PSS_WRITE_EMPTY)
+ {
+ pss_outpw (pss_base + PSS_DATA, data);
+ return;
+ }
+ }
+ printk ("PSS: DSP Command (%04x) Timeout.\n", data);
+ printk ("IRQ conflict???\n");
+}
+
+
+static void
+pss_setaddr (int addr, int configAddr)
+{
+ int val;
+
+ val = pss_inpw (configAddr);
+ val &= ADDR_MASK;
+ val |= (addr << 4);
+ pss_outpw (configAddr, val);
+}
+
+/*_____ pss_checkint
+ This function tests an interrupt number to see if
+ it is availible. It takes the interrupt button
+ as it's argument and returns TRUE if the interrupt
+ is ok.
+*/
+static int
+pss_checkint (int intNum)
+{
+ int val;
+ int ret;
+ int i;
+
+ /*_____ Set the interrupt bits */
+ switch (intNum)
+ {
+ case 3:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_3_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 5:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_5_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 7:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_7_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 9:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_9_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 10:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_10_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 11:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_11_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ case 12:
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_MASK;
+ val |= INT_12_BITS;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ break;
+ default:
+ printk ("unknown interupt selected. %d\n", intNum);
+ return 0;
+ }
+
+ /*_____ Set the interrupt test bit */
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val |= INT_TEST_BIT;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+
+ /*_____ Check if the interrupt is in use */
+ /*_____ Do it a few times in case there is a delay */
+ ret = 0;
+ for (i = 0; i < 5; i++)
+ {
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ if (val & INT_TEST_PASS)
+ {
+ ret = 1;
+ break;
+ }
+ }
+ /*_____ Clear the Test bit and the interrupt bits */
+ val = pss_inpw (pss_base + PSS_CONFIG);
+ val &= INT_TEST_BIT_MASK;
+ val &= INT_MASK;
+ pss_outpw (pss_base + PSS_CONFIG, val);
+ return (ret);
+}
+
+/*____ pss_setint
+ This function sets the correct bits in the
+ configuration register to
+ enable the chosen interrupt.
+*/
+static void
+pss_setint (int intNum, int configAddress)
+{
+ int val;
+
+ switch (intNum)
+ {
+ case 0:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ pss_outpw (configAddress, val);
+ break;
+ case 3:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_3_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 5:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_5_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 7:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_7_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 9:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_9_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 10:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_10_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 11:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_11_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ case 12:
+ val = pss_inpw (configAddress);
+ val &= INT_MASK;
+ val |= INT_12_BITS;
+ pss_outpw (configAddress, val);
+ break;
+ default:
+ printk ("pss_setint unkown int\n");
+ }
+}
+
+
+/*____ pss_setsbint
+ This function sets the correct bits in the
+ SoundBlaster configuration PSS register to
+ enable the chosen interrupt.
+ It takes a interrupt button as its argument.
+*/
+static void
+pss_setsbint (int intNum)
+{
+ int val;
+ int sbConfigAddress;
+
+ sbConfigAddress = pss_base + SB_CONFIG;
+ switch (intNum)
+ {
+ case 3:
+ val = pss_inpw (sbConfigAddress);
+ val &= INT_MASK;
+ val |= INT_3_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ case 5:
+ val = pss_inpw (sbConfigAddress);
+ val &= INT_MASK;
+ val |= INT_5_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ case 7:
+ val = pss_inpw (sbConfigAddress);
+ val &= INT_MASK;
+ val |= INT_7_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ default:
+ printk ("pss_setsbint: unknown_int\n");
+ }
+}
+
+/*____ pss_setsbdma
+ This function sets the correct bits in the
+ SoundBlaster configuration PSS register to
+ enable the chosen DMA channel.
+ It takes a DMA button as its argument.
+*/
+static void
+pss_setsbdma (int dmaNum)
+{
+ int val;
+ int sbConfigAddress;
+
+ sbConfigAddress = pss_base + SB_CONFIG;
+
+ switch (dmaNum)
+ {
+ case 1:
+ val = pss_inpw (sbConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_1_BITS;
+ pss_outpw (sbConfigAddress, val);
+ break;
+ default:
+ printk ("Personal Sound System ERROR! pss_setsbdma: unknown_dma\n");
+ }
+}
+
+/*____ pss_setwssdma
+ This function sets the correct bits in the
+ WSS configuration PSS register to
+ enable the chosen DMA channel.
+ It takes a DMA button as its argument.
+*/
+static void
+pss_setwssdma (int dmaNum)
+{
+ int val;
+ int wssConfigAddress;
+
+ wssConfigAddress = pss_base + PSS_WSS_CONFIG;
+
+ switch (dmaNum)
+ {
+ case 0:
+ val = pss_inpw (wssConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_0_BITS;
+ pss_outpw (wssConfigAddress, val);
+ break;
+ case 1:
+ val = pss_inpw (wssConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_1_BITS;
+ pss_outpw (wssConfigAddress, val);
+ break;
+ case 3:
+ val = pss_inpw (wssConfigAddress);
+ val &= DMA_MASK;
+ val |= DMA_3_BITS;
+ pss_outpw (wssConfigAddress, val);
+ break;
+ default:
+ printk ("Personal Sound System ERROR! pss_setwssdma: unknown_dma\n");
+ }
+}
+
+
+/*_____ SetSpeakerOut
+ This function sets the Volume, Bass, Treble and Mode of
+ the speaker out channel.
+ */
+void
+pss_setspeaker (struct pss_speaker *spk)
+{
+ PSS_write (SET_MASTER_COMMAND);
+ if (spk->volume > PHILLIPS_VOL_MAX)
+ spk->volume = PHILLIPS_VOL_MAX;
+ if (spk->volume < PHILLIPS_VOL_MIN)
+ spk->volume = PHILLIPS_VOL_MIN;
+
+ PSS_write (MASTER_VOLUME_LEFT
+ | (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_VOLUME_RIGHT
+ | (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
+
+ if (spk->bass > PHILLIPS_BASS_MAX)
+ spk->bass = PHILLIPS_BASS_MAX;
+ if (spk->bass < PHILLIPS_BASS_MIN)
+ spk->bass = PHILLIPS_BASS_MIN;
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_BASS
+ | (PHILLIPS_BASS_CONSTANT + spk->bass / PHILLIPS_BASS_STEP));
+
+ if (spk->treb > PHILLIPS_TREBLE_MAX)
+ spk->treb = PHILLIPS_TREBLE_MAX;
+ if (spk->treb < PHILLIPS_TREBLE_MIN)
+ spk->treb = PHILLIPS_TREBLE_MIN;
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_TREBLE
+ | (PHILLIPS_TREBLE_CONSTANT + spk->treb / PHILLIPS_TREBLE_STEP));
+
+ PSS_write (SET_MASTER_COMMAND);
+ PSS_write (MASTER_SWITCH | spk->mode);
+}
+
+static void
+pss_init1848 (void)
+{
+ /*_____ Wait for 1848 to init */
+ while (INB (SoundPortAddress) & SP_IN_INIT);
+
+ /*_____ Wait for 1848 to autocal */
+ OUTB (SoundPortAddress, SP_TEST_AND_INIT);
+ while (INB (SoundPortData) & AUTO_CAL_IN_PROG);
+}
+
+static int
+pss_configure_registers_to_look_like_sb (void)
+{
+ pss_setaddr (wssAddr, pss_base + PSS_WSS_CONFIG);
+
+ SoundPortAddress = wssAddr + 4;
+ SoundPortData = wssAddr + 5;
+
+ DEB (printk ("Turning Game Port %s.\n",
+ gamePort ? "On" : "Off"));
+
+ /*_____ Turn on the Game port */
+ if (gamePort)
+ pss_outpw (pss_base + PSS_STATUS,
+ pss_inpw (pss_base + PSS_STATUS) | GAME_BIT);
+ else
+ pss_outpw (pss_base + PSS_STATUS,
+ pss_inpw (pss_base + PSS_STATUS) & GAME_BIT_MASK);
+
+
+ DEB (printk ("PSS attaching base %x irq %d dma %d\n",
+ pss_base, pss_irq, pss_dma));
+
+ /* Check if sb is enabled if it is check the interrupt */
+ pss_outpw (pss_base + SB_CONFIG, 0);
+
+ if (pss_irq != 0)
+ {
+ DEB (printk ("PSS Emulating Sound Blaster ADDR %04x\n", pss_base));
+ DEB (printk ("PSS SBC: attaching base %x irq %d dma %d\n",
+ SBC_BASE, SBC_IRQ, SBC_DMA));
+
+ if (pss_checkint (SBC_IRQ) == 0)
+ {
+ printk ("PSS! attach: int_error\n");
+ return 0;
+ }
+
+ pss_setsbint (SBC_IRQ);
+ pss_setsbdma (SBC_DMA);
+ sb_ok = 1;
+ }
+ else
+ {
+ sb_ok = 0;
+ printk ("PSS: sound blaster error init\n");
+ }
+
+ /* Check if cd is enabled if it is check the interrupt */
+ pss_outpw (pss_base + CD_CONFIG, 0);
+
+ if (cdAddr != 0)
+ {
+ DEB (printk ("PSS:CD drive %x irq: %d", cdAddr, cdInt));
+ if (cdInt != 0)
+ {
+ if (pss_checkint (cdInt) == 0)
+ {
+ printk ("Can't allocate cdInt %d\n", cdInt);
+ }
+ else
+ {
+ int val;
+
+ printk ("CD poll ");
+ pss_setaddr (cdAddr, pss_base + CD_CONFIG);
+ pss_setint (cdInt, pss_base + CD_CONFIG);
+
+ /* set the correct bit in the
+ configuration register to
+ set the irq polarity for the CD-Rom.
+ NOTE: This bit is in the address config
+ field, It must be configured after setting
+ the CD-ROM ADDRESS!!! */
+ val = pss_inpw (pss_base + CD_CONFIG);
+ pss_outpw (pss_base + CD_CONFIG, 0);
+ val &= CD_POL_MASK;
+ if (cdPol)
+ val |= CD_POL_BIT;
+ pss_outpw (pss_base + CD_CONFIG, val);
+ }
+ }
+ }
+
+ /* Check if midi is enabled if it is check the interrupt */
+ pss_outpw (pss_base + MIDI_CONFIG, 0);
+ if (midiAddr != 0)
+ {
+ printk ("midi init %x %d\n", midiAddr, midiInt);
+ if (pss_checkint (midiInt) == 0)
+ {
+ printk ("midi init int error %x %d\n", midiAddr, midiInt);
+ }
+ else
+ {
+ pss_setaddr (midiAddr, pss_base + MIDI_CONFIG);
+ pss_setint (midiInt, pss_base + MIDI_CONFIG);
+ }
+ }
+ return 1;
+}
+
+long
+attach_pss (long mem_start, struct address_info *hw_config)
+{
+ if (pss_ok)
+ {
+ if (hw_config)
+ {
+ printk (" <PSS-ESC614>");
+ }
+
+ return mem_start;
+ }
+
+ pss_ok = 1;
+
+ if (pss_configure_registers_to_look_like_sb () == 0)
+ return mem_start;
+
+ if (sb_ok)
+ if (pss_synthLen
+ && pss_download_boot (pss_synth, pss_synthLen))
+ {
+ if (speaker)
+ pss_setspeaker (&default_speaker);
+ pss_ok = 1;
+ }
+ else
+ pss_reset_dsp ();
+
+ return mem_start;
+}
+
+int
+probe_pss (struct address_info *hw_config)
+{
+ pss_base = hw_config->io_base;
+ pss_irq = hw_config->irq;
+ pss_dma = hw_config->dma;
+
+ if ((pss_inpw (pss_base + 4) & 0xff00) == 0x4500)
+ {
+ attach_pss (0, hw_config);
+ return 1;
+ }
+ printk (" fail base %x irq %d dma %d\n", pss_base, pss_irq, pss_dma);
+ return 0;
+}
+
+
+static int
+pss_reattach (void)
+{
+ pss_ok = 0;
+ attach_pss (0, 0);
+ return 1;
+}
+
+static int
+pss_reset_dsp ()
+{
+ unsigned long i, limit = GET_TIME () + 10;
+
+ pss_outpw (pss_base + PSS_CONTROL, 0x2000);
+
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ pss_inpw (pss_base + PSS_CONTROL);
+
+ pss_outpw (pss_base + PSS_CONTROL, 0x0000);
+
+ return 1;
+}
+
+
+static int
+pss_download_boot (unsigned char *block, int size)
+{
+ int i, limit, val, count;
+
+ printk ("PSS: downloading boot code synth.ld... ");
+
+ /*_____ Warn DSP software that a boot is coming */
+ pss_outpw (pss_base + PSS_DATA, 0x00fe);
+
+ limit = GET_TIME () + 10;
+
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ if (pss_inpw (pss_base + PSS_DATA) == 0x5500)
+ break;
+
+ pss_outpw (pss_base + PSS_DATA, *block++);
+
+ pss_reset_dsp ();
+ printk ("start ");
+
+ count = 1;
+ while (1)
+ {
+ int j;
+
+ for (j = 0; j < 327670; j++)
+ {
+ /*_____ Wait for BG to appear */
+ if (pss_inpw (pss_base + PSS_STATUS) & PSS_FLAG3)
+ break;
+ }
+
+ if (j == 327670)
+ {
+ /* It's ok we timed out when the file was empty */
+ if (count >= size)
+ break;
+ else
+ {
+ printk ("\nPSS: DownLoad timeout problems, byte %d=%d\n",
+ count, size);
+ return 0;
+ }
+ }
+ /*_____ Send the next byte */
+ pss_outpw (pss_base + PSS_DATA, *block++);
+ count++;
+ }
+
+ /*_____ Why */
+ pss_outpw (pss_base + PSS_DATA, 0);
+
+ limit = GET_TIME () + 10;
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ val = pss_inpw (pss_base + PSS_STATUS);
+
+ printk ("downloaded\n");
+
+ limit = GET_TIME () + 10;
+ for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+ {
+ val = pss_inpw (pss_base + PSS_STATUS);
+ if (val & 0x4000)
+ break;
+ }
+
+ /* now read the version */
+ for (i = 0; i < 32000; i++)
+ {
+ val = pss_inpw (pss_base + PSS_STATUS_REG);
+ if (val & PSS_READ_FULL)
+ break;
+ }
+ if (i == 32000)
+ return 0;
+
+ val = pss_inpw (pss_base + PSS_DATA_REG);
+
+ return 1;
+}
+
+
+/* The following is a simple device driver for the pss.
+ All I really care about is comunication to and from the pss.
+
+ The ability to reinitialize the <synth.ld> This will be
+ default when release is choosen.
+
+ SNDCTL_PSS_DOWNLOAD:
+
+ Okay we need to creat new minor numbers for the
+ DOWNLOAD functionality.
+
+ 14,0x19 -- /dev/pssld where a read operation would output the
+ current ld to user space
+ where a write operation would effectively
+ download a new ld.
+
+ 14,0x09 -- /dev/psecho would open up a comunication path to the
+ esc614 asic. Given the ability to send
+ messages to the asic and recive messages too.
+
+ All messages would get read and written in the
+ same manner. It would be up to the application
+ and the ld to maintain a relationship
+ of what the messages mean.
+
+ for this device we need to implement select. */
+#define CODE_BUFFER_LEN (64*1024)
+static char *code_buffer;
+static int code_length;
+
+static int lock_pss = 0;
+
+int
+pss_open (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ DEB (printk ("pss_open\n"));
+
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ if (lock_pss)
+ return 0;
+
+ lock_pss = 1;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+ if (mode == O_WRONLY)
+ {
+ printk ("pss-open for WRONLY\n");
+ code_length = 0;
+ }
+
+ RESET_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
+ return 1;
+}
+
+void
+pss_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ DEB (printk ("pss_release\n"));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+ if (mode == O_WRONLY && code_length > 0)
+ {
+#ifdef linux
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
+#endif
+ if (!pss_download_boot (code_buffer, code_length))
+ {
+ pss_reattach ();
+ }
+ }
+ lock_pss = 0;
+}
+
+int
+pss_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p;
+
+ DEB (printk ("pss_read\n"));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+ p = 0;
+ c = count;
+
+ return count - c;
+}
+
+int
+pss_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ DEB (printk ("pss_write\n"));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+ dev = dev >> 4;
+
+ if (count) /* Flush output */
+ {
+ COPY_FROM_USER (&code_buffer[code_length], buf, 0, count);
+ code_length += count;
+ }
+ return count;
+}
+
+
+int
+pss_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ DEB (printk ("pss_ioctl dev=%d cmd=%x\n", dev, cmd));
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+
+ switch (cmd)
+ {
+ case SNDCTL_PSS_RESET:
+ pss_reattach ();
+ return 1;
+
+ case SNDCTL_PSS_SETUP_REGISTERS:
+ pss_configure_registers_to_look_like_sb ();
+ return 1;
+
+ case SNDCTL_PSS_SPEAKER:
+ {
+ struct pss_speaker params;
+ COPY_FROM_USER (&params, (char *) arg, 0, sizeof (struct pss_speaker));
+
+ pss_setspeaker (&params);
+ return 0;
+ }
+ default:
+ return RET_ERROR (EIO);
+ }
+}
+
+/* This is going to be used to implement
+ waiting on messages sent from the DSP and to the
+ DSP when comunication is used via the pss directly.
+
+ We need to find out if the pss can generate a diffrent
+ interupt other than the one it has been setup for.
+
+ This way we can carry on a conversation with the pss
+ on a seprate chanel. This would be usefull for debugging. */
+
+pss_select (int dev, struct fileinfo * file, int sel_type, select_table * wait)
+{
+ return 0;
+ if (pss_ok == 0)
+ return RET_ERROR (EIO);
+
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ select_wait (&pss_sleeper, wait);
+ return 0;
+ break;
+
+ case SEL_OUT:
+ select_wait (&pss_sleeper, wait);
+ return 0;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+long
+pss_init (long mem_start)
+{
+ DEB (printk ("pss_init\n"));
+ if (pss_ok)
+ {
+ code_buffer = mem_start;
+ mem_start += CODE_BUFFER_LEN;
+ }
+ return mem_start;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/pss.h b/sys/i386/isa/sound/pss.h
new file mode 100644
index 000000000000..e020af6ae9b1
--- /dev/null
+++ b/sys/i386/isa/sound/pss.h
@@ -0,0 +1,371 @@
+/******************************************************************************
+
+ def.h
+
+ Version 1.3 11/2/93
+
+ Copyright (c) 1993 Analog Devices Inc. All rights reserved
+
+******************************************************************************/
+/* Port offsets from base port for Sound Blaster DSP */
+#define DSP_PORT_CMSD0 0x00 /* C/MS music voice 1-6 data port, write only */
+#define DSP_PORT_CMSR0 0x01 /* C/MS music voice 1-6 register port, write only */
+#define DSP_PORT_CMSD1 0x02 /* C/MS music voice 7-12 data port, write only */
+#define DSP_PORT_CMSR1 0x03 /* C/MS music voice 7-12 register port, write only */
+
+#define DSP_PORT_STATUS 0x04 /* DSP Status bits, read only */
+#define DSP_PORT_CONTROL 0x04 /* DSP Control bits, write only */
+#define DSP_PORT_DATA_LSB 0x05 /* Read or write LSB of 16 bit data */
+
+
+#define DSP_PORT_RESET 0x06 /* DSP Reset, write only */
+#define DSP_PORT_07h 0x07 /* reserved port */
+
+#define DSP_PORT_FMD0 0x08 /* FM music data/status port, read/write */
+#define DSP_PORT_FMR0 0x09 /* FM music data/status port, write only */
+
+#define DSP_PORT_RDDATA 0x0A /* DSP Read data, read only reading signals DSP */
+#define DSP_PORT_0Bh 0x0B /* reserved port */
+#define DSP_PORT_WRDATA 0x0C /* DSP Write data or command, write */
+#define DSP_PORT_WRBUSY 0x0C /* DSP Write buffer status (bit 7), read */
+#define DSP_PORT_0Dh 0x0D /* reserved port */
+#define DSP_PORT_DATAAVAIL 0x0E /* DSP Data available status (bit 7), read only */
+#define DSP_PORT_INTERFACE 0x0E /* Sets DMA Channel and Interrupt, write only */
+#define DSP_PORT_0Fh 0x0F /* reserved port (used on Pro cards) */
+
+#define ADDR_MASK 0x003f
+
+#define INT_MASK 0xffc7
+#define INT_3_BITS 0x0008
+#define INT_5_BITS 0x0010
+#define INT_7_BITS 0x0018
+#define INT_9_BITS 0x0020
+#define INT_10_BITS 0x0028
+#define INT_11_BITS 0x0030
+#define INT_12_BITS 0x0038
+
+#define GAME_BIT 0x0400
+#define GAME_BIT_MASK 0xfbff
+
+#define INT_TEST_BIT 0x0200
+#define INT_TEST_PASS 0x0100
+#define INT_TEST_BIT_MASK 0xFDFF
+
+#define DMA_MASK 0xfff8
+#define DMA_0_BITS 0x0001
+#define DMA_1_BITS 0x0002
+#define DMA_3_BITS 0x0003
+#define DMA_5_BITS 0x0004
+#define DMA_6_BITS 0x0005
+#define DMA_7_BITS 0x0006
+
+#define DMA_TEST_BIT 0x0080
+#define DMA_TEST_PASS 0x0040
+#define DMA_TEST_BIT_MASK 0xFF7F
+
+
+/* Echo DSP Flags */
+
+#define DSP_FLAG3 0x10
+#define DSP_FLAG2 0x08
+#define DSP_FLAG1 0x80
+#define DSP_FLAG0 0x40
+
+#define PSS_CONFIG 0x10
+#define PSS_WSS_CONFIG 0x12
+#define SB_CONFIG 0x14
+#define MIDI_CONFIG 0x18
+#define CD_CONFIG 0x16
+#define UART_CONFIG 0x1a
+
+#define PSS_DATA 0x00
+#define PSS_STATUS 0x02
+#define PSS_CONTROL 0x02
+#define PSS_ID_VERS 0x04
+
+#define PSS_FLAG3 0x0800
+#define PSS_FLAG2 0x0400
+#define PSS_FLAG1 0x1000
+#define PSS_FLAG0 0x0800
+
+/*_____ WSS defines */
+#define WSS_BASE_ADDRESS 0x530
+#define WSS_CONFIG 0x0
+#define WSS_VERSION 0x03
+#define WSS_SP0 0x04
+#define WSS_SP1 0x05
+#define WSS_SP2 0x06
+#define WSS_SP3 0x07
+
+/*_____ SoundPort register addresses */
+
+#define SP_LIN_SOURCE_CTRL 0x00
+#define SP_RIN_SOURCE_CTRL 0x01
+#define SP_LIN_GAIN_CTRL 0x10
+#define SP_RIN_GAIN_CTRL 0x11
+#define SP_LAUX1_CTRL 0x02
+#define SP_RAUX1_CTRL 0x03
+#define SP_LAUX2_CTRL 0x04
+#define SP_RAUX2_CTRL 0x05
+#define SP_LOUT_CTRL 0x06
+#define SP_ROUT_CTRL 0x07
+#define SP_CLK_FORMAT 0x48
+#define SP_INT_CONF 0x09
+#define SP_INT_CONF_MCE 0x49
+#define SP_PIN_CTRL 0x0a
+#define SP_TEST_INIT 0x0b
+#define SP_MISC_CTRL 0x0c
+#define SP_MIX_CTRL 0x0d
+#define SP_DMA_UCNT 0x0e
+#define SP_DMA_LCNT 0x0f
+
+/*_____ Gain constants */
+
+#define GAIN_0 0x00
+#define GAIN_1_5 0x01
+#define GAIN_3 0x02
+#define GAIN_4_5 0x03
+#define GAIN_6 0x04
+#define GAIN_7_5 0x05
+#define GAIN_9 0x06
+#define GAIN_10_5 0x07
+#define GAIN_12 0x08
+#define GAIN_13_5 0x09
+#define GAIN_15 0x0a
+#define GAIN_16_5 0x0b
+#define GAIN_18 0x0c
+#define GAIN_19_5 0x0d
+#define GAIN_21 0x0e
+#define GAIN_22_5 0x0f
+#define MUTE 0XFFFF
+
+/*_____ Attenuation constants */
+
+#define ATTEN_0 0x00
+#define ATTEN_1_5 0x01
+#define ATTEN_3 0x02
+#define ATTEN_4_5 0x03
+#define ATTEN_6 0x04
+#define ATTEN_7_5 0x05
+#define ATTEN_9 0x06
+#define ATTEN_10_5 0x07
+#define ATTEN_12 0x08
+#define ATTEN_13_5 0x09
+#define ATTEN_15 0x0a
+#define ATTEN_16_5 0x0b
+#define ATTEN_18 0x0c
+#define ATTEN_19_5 0x0d
+#define ATTEN_21 0x0e
+#define ATTEN_22_5 0x0f
+
+
+#define PSS_WRITE_EMPTY 0x8000
+
+#define CD_POL_MASK 0xFFBF
+#define CD_POL_BIT 0x0040
+
+
+
+/******************************************************************************
+
+ host.h
+
+ Version 1.2 9/27/93
+
+ Copyright (c) 1993 Analog Devices Inc. All rights reserved
+
+******************************************************************************/
+#define SB_WRITE_FULL 0x80
+#define SB_READ_FULL 0x80
+#define SB_WRITE_STATUS 0x0C
+#define SB_READ_STATUS 0x0E
+#define SB_READ_DATA 0x0A
+#define SB_WRITE_DATA 0x0C
+
+#define PSS_DATA_REG 0x00
+#define PSS_STATUS_REG 0x02
+#define PSS_WRITE_EMPTY 0x8000
+#define PSS_READ_FULL 0x4000
+
+/*_____ 1848 Sound Port bit defines */
+
+#define SP_IN_INIT 0x80
+#define MODE_CHANGE_ENABLE 0x40
+#define MODE_CHANGE_MASK 0xbf
+#define TRANSFER_DISABLE 0x20
+#define TRANSFER_DISABLE_MASK 0xdf
+#define ADDRESS_MASK 0xf0
+
+/*_____ Status bits */
+#define INTERRUPT_STATUS 0x01
+#define PLAYBACK_READY 0x02
+#define PLAYBACK_LEFT 0x04
+/*_____ pbright is not left */
+#define PLAYBACK_UPPER 0x08
+/*_____ bplower is not upper */
+
+#define SAMPLE_OVERRUN 0x10
+#define SAMPLE_UNDERRUN 0x10
+#define CAPTURE_READY 0x20
+#define CAPTURE_LEFT 0x40
+/*_____ cpright is not left */
+#define CAPTURE_UPPER 0x08
+/*_____ cplower is not upper */
+
+/*_____ Input & Output regs bits */
+#define LINE_INPUT 0x80
+#define AUX_INPUT 0x40
+#define MIC_INPUT 0x80
+#define MIXED_DAC_INPUT 0xC0
+#define INPUT_GAIN_MASK 0xf0
+#define INPUT_MIC_GAIN_ENABLE 0x20
+#define INPUT_MIC_GAIN_MASK 0xdf
+#define INPUT_SOURCE_MASK 0x3f
+#define AUX_INPUT_ATTEN_MASK 0xf0
+#define AUX_INPUT_MUTE 0x80
+#define AUX_INPUT_MUTE_MASK 0x7f
+#define OUTPUT_MUTE 0x80
+#define OUTPUT_MUTE_MASK 0x7f
+#define OUTPUT_ATTEN_MASK 0xc0
+
+/*_____ Clock and Data format reg bits */
+#define CLOCK_SELECT_MASK 0xfe
+#define CLOCK_XTAL2 0x01
+#define CLOCK_XTAL1 0x00
+#define CLOCK_FREQ_MASK 0xf1
+#define STEREO_MONO_MASK 0xef
+#define STEREO 0x10
+#define AUDIO_MONO 0x00
+#define LINEAR_COMP_MASK 0xdf
+#define LINEAR 0x00
+#define COMPANDED 0x20
+#define FORMAT_MASK 0xbf
+#define PCM 0x00
+#define ULAW 0x00
+#define TWOS_COMP 0x40
+#define ALAW 0x40
+
+/*_____ Interface Configuration reg bits */
+#define PLAYBACK_ENABLE 0x01
+#define PLAYBACK_ENABLE_MASK 0xfe
+#define CAPTURE_ENABLE 0x02
+#define CAPTURE_ENABLE_MASK 0xfd
+#define SINGLE_DMA 0x04
+#define SINGLE_DMA_MASK 0xfb
+#define DUAL_DMA 0x00
+#define AUTO_CAL_ENABLE 0x08
+#define AUTO_CAL_DISABLE_MASK 0xf7
+#define PLAYBACK_PIO_ENABLE 0x40
+#define PLAYBACK_DMA_MASK 0xbf
+#define CAPTURE_PIO_ENABLE 0x80
+#define CAPTURE_DMA_MASK 0x7f
+
+/*_____ Pin control bits */
+#define INTERRUPT_ENABLE 0x02
+#define INTERRUPT_MASK 0xfd
+
+/*_____ Test and init reg bits */
+#define OVERRANGE_LEFT_MASK 0xfc
+#define OVERRANGE_RIGHT_MASK 0xf3
+#define DATA_REQUEST_STATUS 0x10
+#define AUTO_CAL_IN_PROG 0x20
+#define PLAYBACK_UNDERRUN 0x40
+#define CAPTURE_UNDERRUN 0x80
+
+/*_____ Miscellaneous Control reg bits */
+#define ID_MASK 0xf0
+
+/*_____ Digital Mix Control reg bits */
+#define DIGITAL_MIX1_MUTE_MASK 0xfe
+#define MIX_ATTEN_MASK 0x03
+
+/*_____ 1848 Sound Port reg defines */
+
+#define SP_LEFT_INPUT_CONTROL 0x0
+#define SP_RIGHT_INPUT_CONTROL 0x1
+#define SP_LEFT_AUX1_CONTROL 0x2
+#define SP_RIGHT_AUX1_CONTROL 0x3
+#define SP_LEFT_AUX2_CONTROL 0x4
+#define SP_RIGHT_AUX2_CONTROL 0x5
+#define SP_LEFT_OUTPUT_CONTROL 0x6
+#define SP_RIGHT_OUTPUT_CONTROL 0x7
+#define SP_CLOCK_DATA_FORMAT 0x8
+#define SP_INTERFACE_CONFIG 0x9
+#define SP_PIN_CONTROL 0xA
+#define SP_TEST_AND_INIT 0xB
+#define SP_MISC_INFO 0xC
+#define SP_DIGITAL_MIX 0xD
+#define SP_UPPER_BASE_COUNT 0xE
+#define SP_LOWER_BASE_COUNT 0xF
+
+#define HOST_SP_ADDR (0x534)
+#define HOST_SP_DATA (0x535)
+
+
+/******************************************************************************
+
+ phillips.h
+
+ Version 1.2 9/27/93
+
+ Copyright (c) 1993 Analog Devices Inc. All rights reserved
+
+******************************************************************************/
+/*_____ Phillips control SW defines */
+
+/*_____ Settings and ranges */
+#define VOLUME_MAX 6
+#define VOLUME_MIN (-64)
+#define VOLUME_RANGE 70
+#define VOLUME_STEP 2
+#define BASS_MAX 15
+#define BASS_MIN (-12)
+#define BASS_STEP 2
+#define BASS_RANGE 27
+#define TREBLE_MAX 12
+#define TREBLE_MIN (-12)
+#define TREBLE_STEP 2
+#define TREBLE_RANGE 24
+
+#define VOLUME_CONSTANT 252
+#define BASS_CONSTANT 246
+#define TREBLE_CONSTANT 246
+
+/*_____ Software commands */
+#define SET_MASTER_COMMAND 0x0010
+#define MASTER_VOLUME_LEFT 0x0000
+#define MASTER_VOLUME_RIGHT 0x0100
+#define MASTER_BASS 0x0200
+#define MASTER_TREBLE 0x0300
+#define MASTER_SWITCH 0x0800
+
+#define STEREO_MODE 0x00ce
+#define PSEUDO_MODE 0x00d6
+#define SPATIAL_MODE 0x00de
+#define MONO_MODE 0x00c6
+
+
+#define PSS_STEREO 0x00ce
+#define PSS_PSEUDO 0x00d6
+#define PSS_SPATIAL 0x00de
+#define PSS_MONO 0x00c6
+
+#define PHILLIPS_VOL_MIN -64
+#define PHILLIPS_VOL_MAX 6
+#define PHILLIPS_VOL_DELTA 70
+#define PHILLIPS_VOL_INITIAL -20
+#define PHILLIPS_VOL_CONSTANT 252
+#define PHILLIPS_VOL_STEP 2
+#define PHILLIPS_BASS_MIN -12
+#define PHILLIPS_BASS_MAX 15
+#define PHILLIPS_BASS_DELTA 27
+#define PHILLIPS_BASS_INITIAL 0
+#define PHILLIPS_BASS_CONSTANT 246
+#define PHILLIPS_BASS_STEP 2
+#define PHILLIPS_TREBLE_MIN -12
+#define PHILLIPS_TREBLE_MAX 12
+#define PHILLIPS_TREBLE_DELTA 24
+#define PHILLIPS_TREBLE_INITIAL 0
+#define PHILLIPS_TREBLE_CONSTANT 246
+#define PHILLIPS_TREBLE_STEP 2
+
diff --git a/sys/i386/isa/sound/sound_timer.c b/sys/i386/isa/sound/sound_timer.c
new file mode 100644
index 000000000000..c6544c82a7ee
--- /dev/null
+++ b/sys/i386/isa/sound/sound_timer.c
@@ -0,0 +1,406 @@
+/*
+ * sound/sound_timer.c
+ *
+ * Timer for the level 2 interface of the /dev/sequencer. Uses the
+ * 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812)))
+
+static volatile int initialized = 0, opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+static volatile int select_addr, data_addr;
+static volatile int curr_timer = 0;
+static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
+
+
+static void
+timer_command (unsigned int addr, unsigned int val)
+{
+ int i;
+
+ OUTB ((unsigned char) (addr & 0xff), select_addr);
+
+ for (i = 0; i < 2; i++)
+ INB (select_addr);
+
+ OUTB ((unsigned char) (val & 0xff), data_addr);
+
+ for (i = 0; i < 2; i++)
+ INB (select_addr);
+}
+
+static void
+arm_timer (int timer, unsigned int interval)
+{
+
+ curr_timer = timer;
+
+ if (timer == 1)
+ {
+ gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */
+ gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */
+ timer_command (0x04, 0x01); /* Start timer 1 */
+ }
+ else
+ {
+ gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */
+ gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */
+ timer_command (0x04, 0x02); /* Start timer 2 */
+ }
+}
+
+static unsigned long
+tmr2ticks (int tmr_value)
+{
+ /*
+ * Convert timer ticks to MIDI ticks
+ */
+
+ unsigned long tmp;
+ unsigned long scale;
+
+ tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
+
+ scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
+
+ return (tmp + (scale / 2)) / scale;
+}
+
+static void
+reprogram_timer (void)
+{
+ unsigned long usecs_per_tick;
+ int timer_no, resolution;
+ int divisor;
+
+ usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+
+ /*
+ * Don't kill the system by setting too high timer rate
+ */
+ if (usecs_per_tick < 2000)
+ usecs_per_tick = 2000;
+
+ if (usecs_per_tick > (256 * 80))
+ {
+ timer_no = 2;
+ resolution = 320; /* usec */
+ }
+ else
+ {
+ timer_no = 1;
+ resolution = 80; /* usec */
+ }
+
+ divisor = (usecs_per_tick + (resolution / 2)) / resolution;
+ usecs_per_tmr = divisor * resolution;
+
+ arm_timer (timer_no, divisor);
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ RESTORE_INTR (flags);
+}
+
+static int
+timer_open (int dev, int mode)
+{
+ if (opened)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 60;
+ curr_timebase = HZ;
+ opened = 1;
+ reprogram_timer ();
+
+ return 0;
+}
+
+static void
+timer_close (int dev)
+{
+ opened = tmr_running = 0;
+ gus_write8 (0x45, 0); /* Disable both timers */
+}
+
+static int
+timer_event (int dev, unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ reprogram_timer ();
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ reprogram_timer ();
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ reprogram_timer ();
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+timer_get_time (int dev)
+{
+ if (!opened)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+timer_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return IOCTL_OUT (arg, TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ reprogram_timer ();
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+timer_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+static struct sound_timer_operations sound_timer =
+{
+ {"OPL-3/GUS Timer", 0},
+ 1, /* Priority */
+ 0, /* Local device link */
+ timer_open,
+ timer_close,
+ timer_event,
+ timer_get_time,
+ timer_ioctl,
+ timer_arm
+};
+
+void
+sound_timer_interrupt (void)
+{
+ gus_write8 (0x45, 0); /* Ack IRQ */
+ timer_command (4, 0x80); /* Reset IRQ flags */
+
+ if (!opened)
+ return;
+
+ if (curr_timer == 1)
+ gus_write8 (0x45, 0x04); /* Start timer 1 again */
+ else
+ gus_write8 (0x45, 0x08); /* Start timer 2 again */
+
+ if (!tmr_running)
+ return;
+
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+}
+
+void
+sound_timer_init (int io_base)
+{
+ int n;
+
+ if (initialized)
+ return; /* There is already a similar timer */
+
+ select_addr = io_base;
+ data_addr = io_base + 1;
+
+ initialized = 1;
+
+#if 1
+ if (num_sound_timers >= MAX_TIMER_DEV)
+ n = 0; /* Overwrite the system timer */
+ else
+ n = num_sound_timers++;
+#else
+ n = 0;
+#endif
+
+ sound_timer_devs[n] = &sound_timer;
+}
+
+#endif
+#endif
diff --git a/sys/i386/isa/sound/sys_timer.c b/sys/i386/isa/sound/sys_timer.c
new file mode 100644
index 000000000000..1000045a42d5
--- /dev/null
+++ b/sys/i386/isa/sound/sys_timer.c
@@ -0,0 +1,304 @@
+/*
+ * sound/sys_timer.c
+ *
+ * The default timer for the Level 2 sequencer interface
+ * Uses the (100HZ) timer of kernel.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static volatile int opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+
+static void poll_def_tmr (unsigned long dummy);
+
+DEFINE_TIMER (def_tmr, poll_def_tmr);
+
+static unsigned long
+tmr2ticks (int tmr_value)
+{
+ /*
+ * Convert system timer ticks (HZ) to MIDI ticks
+ */
+
+ unsigned long tmp;
+ unsigned long scale;
+
+ tmp = (tmr_value * 1000) / HZ;/* Convert to msecs */
+
+ scale = (60 * 1000) / (curr_tempo * curr_timebase); /* msecs per MIDI tick */
+
+ return (tmp + (scale / 2)) / scale;
+}
+
+static void
+poll_def_tmr (unsigned long dummy)
+{
+
+ if (opened)
+ {
+ ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
+
+ if (tmr_running)
+ {
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+ }
+ }
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ RESTORE_INTR (flags);
+}
+
+static int
+def_tmr_open (int dev, int mode)
+{
+ if (opened)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 60;
+ curr_timebase = HZ;
+ opened = 1;
+
+ ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
+
+ return 0;
+}
+
+static void
+def_tmr_close (int dev)
+{
+ opened = tmr_running = 0;
+}
+
+static int
+def_tmr_event (int dev, unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+def_tmr_get_time (int dev)
+{
+ if (!opened)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+def_tmr_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return IOCTL_OUT (arg, TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:;
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+def_tmr_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+struct sound_timer_operations default_sound_timer =
+{
+ {"System Timer", 0},
+ 0, /* Priority */
+ 0, /* Local device link */
+ def_tmr_open,
+ def_tmr_close,
+ def_tmr_event,
+ def_tmr_get_time,
+ def_tmr_ioctl,
+ def_tmr_arm
+};
+
+#endif
+#endif
diff --git a/sys/i386/isa/sound/uart6850.c b/sys/i386/isa/sound/uart6850.c
new file mode 100644
index 000000000000..ca6313521270
--- /dev/null
+++ b/sys/i386/isa/sound/uart6850.c
@@ -0,0 +1,323 @@
+/*
+ * sound/uart6850.c
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
+ * added 6850 support, used with COVOX SoundMaster II and custom cards.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (uart6850_base) /*
+ * * * Midi6850 Data I/O Port on IBM
+ * */
+#define COMDPORT (uart6850_base+1) /*
+ * * * Midi6850 Command Port on IBM */
+#define STATPORT (uart6850_base+1) /*
+ * * * Midi6850 Status Port on IBM */
+
+#define uart6850_status() INB(STATPORT)
+#define input_avail() ((uart6850_status()&INPUT_AVAIL))
+#define output_ready() ((uart6850_status()&OUTPUT_READY))
+#define uart6850_cmd(cmd) OUTB(cmd, COMDPORT)
+#define uart6850_read() INB(DATAPORT)
+#define uart6850_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x02 /*
+ * * * Mask for Data Read Ready Bit */
+#define INPUT_AVAIL 0x01 /*
+ * * * Mask for Data Send Ready Bit */
+
+#define UART_RESET 0x95 /*
+ * * * 6850 Total Reset Command */
+#define UART_MODE_ON 0x03 /*
+ * * * 6850 Send/Receive UART Mode */
+
+static int uart6850_opened = 0;
+static int uart6850_base = 0x330;
+static int uart6850_irq;
+static int uart6850_detected = 0;
+static int my_dev;
+
+static int reset_uart6850 (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+uart6850_input_loop (void)
+{
+ int count;
+
+ count = 10;
+
+ while (count) /*
+ * Not timed out
+ */
+ if (input_avail ())
+ {
+ unsigned char c = uart6850_read ();
+
+ count = 100;
+
+ if (uart6850_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+ else
+ while (!input_avail () && count)
+ count--;
+}
+
+void
+m6850intr (int unit)
+{
+ printk ("M");
+ if (input_avail ())
+ uart6850_input_loop ();
+}
+
+/*
+ * It looks like there is no input interrupts in the UART mode. Let's try
+ * polling.
+ */
+
+static void
+poll_uart6850 (unsigned long dummy)
+{
+ unsigned long flags;
+
+ DEFINE_TIMER (uart6850_timer, poll_uart6850);
+
+ if (!(uart6850_opened & OPEN_READ))
+ return; /*
+ * No longer required
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ uart6850_input_loop ();
+
+ ACTIVATE_TIMER (uart6850_timer, poll_uart6850, 1); /*
+ * Come back later
+ */
+
+ RESTORE_INTR (flags);
+}
+
+static int
+uart6850_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (uart6850_opened)
+ {
+ printk ("Midi6850: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ uart6850_cmd (UART_RESET);
+
+ uart6850_input_loop ();
+
+ midi_input_intr = input;
+ uart6850_opened = mode;
+ poll_uart6850 (0); /*
+ * Enable input polling
+ */
+
+ return 0;
+}
+
+static void
+uart6850_close (int dev)
+{
+ uart6850_cmd (UART_MODE_ON);
+
+ uart6850_opened = 0;
+}
+
+static int
+uart6850_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ uart6850_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+
+ if (!output_ready ())
+ {
+ printk ("Midi6850: Timeout\n");
+ return 0;
+ }
+
+ uart6850_write (midi_byte);
+ return 1;
+}
+
+static int
+uart6850_command (int dev, unsigned char *midi_byte)
+{
+ return 1;
+}
+
+static int
+uart6850_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+uart6850_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+uart6850_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+uart6850_kick (int dev)
+{
+}
+
+static int
+uart6850_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+#define MIDI_SYNTH_NAME "6850 UART Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations uart6850_operations =
+{
+ {"6850 UART", 0, 0, SNDCARD_UART6850},
+ &std_midi_synth,
+ uart6850_open,
+ uart6850_close,
+ uart6850_ioctl,
+ uart6850_out,
+ uart6850_start_read,
+ uart6850_end_read,
+ uart6850_kick,
+ uart6850_command,
+ uart6850_buffer_status
+};
+
+
+long
+attach_uart6850 (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
+
+ if (!uart6850_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ uart6850_cmd (UART_MODE_ON);
+
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+ printk (" <6850 Midi Interface>");
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &uart6850_operations;
+ return mem_start;
+}
+
+static int
+reset_uart6850 (void)
+{
+ uart6850_read ();
+ return 1; /*
+ * OK
+ */
+}
+
+
+int
+probe_uart6850 (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
+
+ if (snd_set_irq_handler (uart6850_irq, m6850intr) < 0)
+ return 0;
+
+ ok = reset_uart6850 ();
+
+ uart6850_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif