diff options
| author | Steven Wallace <swallace@FreeBSD.org> | 1994-10-01 01:33:47 +0000 |
|---|---|---|
| committer | Steven Wallace <swallace@FreeBSD.org> | 1994-10-01 01:33:47 +0000 |
| commit | 8f25169ce64c84f2254305ced3738be611861bd3 (patch) | |
| tree | b0ab422841f5c2ef086c0ba888b0be8a372904f9 /sys | |
| parent | e902866cf755930157f2094d6fb5fc8a90fd9adf (diff) | |
| parent | fbb347fe32427a1b3fb420db2f83b7cfefaa7890 (diff) | |
Notes
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/i386/isa/sound/ad1848.c | 953 | ||||
| -rw-r--r-- | sys/i386/isa/sound/configure.c | 830 | ||||
| -rw-r--r-- | sys/i386/isa/sound/midi_ctrl.h | 22 | ||||
| -rw-r--r-- | sys/i386/isa/sound/midi_synth.c | 474 | ||||
| -rw-r--r-- | sys/i386/isa/sound/midi_synth.h | 45 | ||||
| -rw-r--r-- | sys/i386/isa/sound/pss.c | 924 | ||||
| -rw-r--r-- | sys/i386/isa/sound/pss.h | 371 | ||||
| -rw-r--r-- | sys/i386/isa/sound/sound_timer.c | 406 | ||||
| -rw-r--r-- | sys/i386/isa/sound/sys_timer.c | 304 | ||||
| -rw-r--r-- | sys/i386/isa/sound/uart6850.c | 323 |
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 (¶ms, (char *) arg, 0, sizeof (struct pss_speaker)); + + pss_setspeaker (¶ms); + 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 |
