commit fbb347fe32427a1b3fb420db2f83b7cfefaa7890 Author: Steven Wallace Date: Sat Oct 1 01:33:47 1994 +0000 Hannu Savolainen's VoxWare original sound drivers, version 2.9. diff --git a/sys/i386/isa/sound/CHANGELOG b/sys/i386/isa/sound/CHANGELOG new file mode 100644 index 00000000000..fcdbb8e3eae --- /dev/null +++ b/sys/i386/isa/sound/CHANGELOG @@ -0,0 +1,110 @@ +Changelog for version 2.90 +------------------------------------ + +This is an intermediate release (v3.0 prototype with some experimental +features disabled). See experimental.txt for more info. + +Since pre-3.0-949712 +- GUS MAX support +- Partially working MSS/WSS support (could work with some cards). +- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs + (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and + GUS MAX, but it doesn't work yet. +Since pre-3.0-940426 +- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). +This codec chip is used in various soundcards. This version is developed +for the 16 bit daughtercard of GUS. It should work with other cards also +if the following requirements are met: + - The I/O, IRQ and DMA settings are jumper selectable or + the card is initialized by booting DOS before booting Linux (etc.). + - You add the IO, IRQ and DMA settings manually to the local.h. + (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that + the base address bust be the base address of the codec chip not the + card itself. For the GUS16 these are the same but most MSS compatible + cards have the codec located at card_base+4. +- Some minor changes + +Since 2.5 (******* MAJOR REWRITE ***********) + +This version is based on v2.3. I have tried to maintain two versions +together so that this one should have the same features than v2.5. +Something may still be missing. If you notice such things, please let me +know. + +The Readme.v30 contains more details. + +- /dev/midi## devices. +- /dev/sequencer2 + +Since 2.5-beta2 +- Some fine tuning to the GUS v3.7 mixer code. +- Fixed speed limits for the plain SB (1.0 to 2.0). + +Since 2.5-beta +- Fixed OPL-3 detection with SB. Caused problems with PAS16. +- GUS v3.7 mixer support. + +Since 2.4 +- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). +- Fixed truncated sound on /dev/dsp when the device is closed. +- Linear volume mode for GUS +- Pitch bends larger than +/- 2 octaves. +- MIDI recording for SB and SB Pro. (Untested). +- Some other fixes. +- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. +- Implemented better detection for OPL-3. This should be usefull if you + have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. +- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). + +Since 2.3b +- Fixed bug which made it impossible to make long recordings to disk. + Recording was not restarted after a buffer overflow situation. +- Limited mixer support for GUS. +- Numerous improvements to the GUS driver by Andrew Robinson. Including + some click removal etc. + +Since 2.3 +- Fixed some minor bugs in the SB16 driver. + +Since 2.2b +- Full SB16 DSP support. 8/16 bit, mono/stereo +- The SCO and FreeBSD versions should be in sync now. There are some + problems with SB16 and GUS in the freebsd versions. + The DMA buffer allocation of the SCO version has been polished but + there could still be some problems. At least it hogs memory. + The DMA channel + configuration method used in the sco/System is a hack. +- Support for the MPU emulation of the SB16. +- Some big arrays are now allocated boot time. This makes the bss segment + smaller which makes it possible to use the full driver with + NetBSD. These arrays are not allocated if no suitable soundcard is available. +- Fixed a bug in the compute_and_set_volume in gus_wave.c +- Fixed the too fast mono playback problem of SB Pro and PAS16. + +Since 2.2 +- Stereo recording for SB Pro. Somehow it was missing and nobody + had noticed it earlier. +- Minor polishing. +- Interpreting of boot time arguments (sound=) for Linux. +- Breakup of sb_dsp.c. Parts of the code has been moved to + sb_mixer.c and sb_midi.c + +Since 2.1 +- Preliminary support for SB16. + - The SB16 mixer is supported in it's native mode. + - Digitized voice capability up to 44.1 kHz/8 bit/mono + (16 bit and stereo support coming in the next release). +- Fixed some bugs in the digitized voice driver for PAS16. +- Proper initialization of the SB emulation of latest PAS16 models. + +- Significantly improved /dev/dsp and /dev/audio support. + - Now supports half duplex mode. It's now possible to record and + playback without closing and reopening the device. + - It's possible to use smaller buffers than earlier. There is a new + ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. + This call instructs the driver to use smaller buffers. The default + buffer size (0.5 to 1.0 seconds) is divided by n. Should be called + immediately after opening the device. + +Since 2.0 +Just cosmetic changes. diff --git a/sys/i386/isa/sound/COPYING b/sys/i386/isa/sound/COPYING new file mode 100644 index 00000000000..d1509c50025 --- /dev/null +++ b/sys/i386/isa/sound/COPYING @@ -0,0 +1,25 @@ +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ diff --git a/sys/i386/isa/sound/README b/sys/i386/isa/sound/README new file mode 100644 index 00000000000..a4a084706e5 --- /dev/null +++ b/sys/i386/isa/sound/README @@ -0,0 +1,80 @@ +VoxWare v2.90 release notes +-------------------------- + + + This version includes some hidden features which + are described in the file experimental.txt + Some of these features are not enabled by default. Look at + experimental.txt for more info. + + I just decided to release this version with some + incompletely implemented features disabled since + there are some new features required by a popular + application. In addition there is also support + for the GUS MAX and the 16 bit sampling option of GUS. + + The MSS/WSS support works now. At least with SG NX Pro 16. + +********* IMPORTANT ***************************************** +Linux 1.0 or later is required to by this driver version. + +Don't distribute binaries which use /dev/sequencer and are +compiled with the soundcard.h of this version. They will +not work with version 2.x of the driver. +************************************************************* + + +You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z +packages to use this driver. They should be in the same +ftp site or BBS from where you got this driver. For +example at nic.funet.fi:pub/OS/Linux/*. + +If you are looking for the installation instructions, please +look at linux/Readme. + +Compatibility with the earlier versions +--------------------------------------- + +This version is backward compatible with the version 2.X. All programs +compiled with sys/soundcard.h of v2.X should work without problems. +PROGRAMS COMPILED WITH THE sys/soundcard.h OF THIS VERSION WILL NOT +WORK WITH v2.X DRIVER. BE CAREFULL WHEN DISTRIBUTING BINARIES COMPILED +FOR THIS VERSION. + +Contributors +------------ + +This driver contains code by several contributors. In addition several other +persons have given usefull suggestions. The following is a list of major +contributors. (I could have forgotten some names.) + + Craig Metz 1/2 of the PAS16 Mixer and PCM support + Rob Hooft Volume computation algorithm for the FM synth. + Mika Liljeberg uLaw encoding and decoding routines + Greg Lee Volume computation algorithm for the GUS and + lot's of valuable suggestions. + Andy Warner ISC port + Jim Lowe FreeBSD port + Anders Baekgaard Bughunting and valuable suggestions. + Joerg Schubert SB16 DSP support. + Andrew Robinson Improvements to the GUS driver + Megens SA MIDI recording for SB and SB Pro. + Mikael Nordqvist Linear volume support for GUS. + Mikael Nordqvist Linear volume support for GUS. + Ian Hartas SVR4.2 port + Markus Aroharju and + Risto Kankkunen Major contributions to the mixer support + of GUS v3.7. + Hunyue Yau Mixer support for SG NX Pro. + Marc Hoffman PSS support. + +Regards, + +Hannu Savolainen +hannu@voxware.pp.fi + +Snail mail: Hannu Savolainen + Pallaksentie 4 A 2 + 00970 Helsinki + Finland +FAX: +358 0 395 1968 (usually not connected) diff --git a/sys/i386/isa/sound/ad1848.c b/sys/i386/isa/sound/ad1848.c new file mode 100644 index 00000000000..b08f5f76cc6 --- /dev/null +++ b/sys/i386/isa/sound/ad1848.c @@ -0,0 +1,953 @@ +/* + * sound/ad1848.c + * + * The low level driver for the AD1848/CS4248 codec chip which + * is used for example in the MS Sound System. + * + * The CS4231 which is used in the GUS MAX and some other cards is + * upwards compatible with AD1848 and this driver is able to drive it. + * + * Copyright by Hannu Savolainen 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define DEB(x) +#define DEB1(x) +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848) + +#define IMODE_NONE 0 +#define IMODE_OUTPUT 1 +#define IMODE_INPUT 2 +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +typedef struct +{ + int base; + int irq; + int dma_capture, dma_playback; + unsigned char MCE_bit; + + int speed; + unsigned char speed_bits; + int channels; + int audio_format; + unsigned char format_bits; + + int xfer_count; + int irq_mode; + int intr_active; + int opened; + char *chip_name; + int mode; +} + +ad1848_info; + +static int nr_ad1848_devs = 0; +static char irq2dev[16] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}; + +static int ad_format_mask[2 /*devc->mode*/ ] = +{ + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM +}; + +static ad1848_info dev_info[MAX_AUDIO_DEV]; + +#define io_Index_Addr(d) ((d)->base) +#define io_Indexed_Data(d) ((d)->base+1) +#define io_Status(d) ((d)->base+2) +#define io_Polled_IO(d) ((d)->base+3) + +static int ad1848_open (int dev, int mode); +static void ad1848_close (int dev); +static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local); +static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); +static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); +static int ad1848_prepare_for_IO (int dev, int bsize, int bcount); +static void ad1848_reset (int dev); +static void ad1848_halt (int dev); +void ad1848_interrupt (int dev); + +static int +ad_read (ad1848_info * devc, int reg) +{ + unsigned long flags; + int x; + + DISABLE_INTR (flags); + OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); + x = INB (io_Indexed_Data (devc)); + /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */ + RESTORE_INTR (flags); + + return x; +} + +static void +ad_write (ad1848_info * devc, int reg, int data) +{ + unsigned long flags; + + DISABLE_INTR (flags); + OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); + OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc)); + /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */ + RESTORE_INTR (flags); +} + +static void +ad_set_MCE (ad1848_info * devc, int state) +{ + unsigned long flags; + + DISABLE_INTR (flags); + if (state) + devc->MCE_bit = 0x40; + else + devc->MCE_bit = 0x00; + OUTB (devc->MCE_bit, io_Index_Addr (devc)); + RESTORE_INTR (flags); +} + +static void +wait_for_calibration (ad1848_info * devc) +{ + int timeout = 0; + + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on and then off. + */ + + timeout = 100000; + while (timeout > 0 && INB (devc->base) == 0x80) + timeout--; + if (INB (devc->base) == 0x80) + printk ("ad1848: Auto calibration timed out(1).\n"); + + timeout = 100000; + while (timeout > 0 && !(ad_read (devc, 11) & 0x20)) + timeout--; + if (!(ad_read (devc, 11) & 0x20)) + printk ("ad1848: Auto calibration timed out(2).\n"); + + timeout = 100000; + while (timeout > 0 && ad_read (devc, 11) & 0x20) + timeout--; + if (ad_read (devc, 11) & 0x20) + printk ("ad1848: Auto calibration timed out(3).\n"); +} + +static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] = +{ + { + "Generic AD1848 codec", + DMA_AUTOMODE, + AFMT_U8, /* Will be set later */ + NULL, + ad1848_open, + ad1848_close, + ad1848_output_block, + ad1848_start_input, + ad1848_ioctl, + ad1848_prepare_for_IO, + ad1848_prepare_for_IO, + ad1848_reset, + ad1848_halt, + NULL, + NULL + }}; + +static int +ad1848_open (int dev, int mode) +{ + int err; + ad1848_info *devc = NULL; + unsigned long flags; + + DEB (printk ("ad1848_open(int mode = %X)\n", mode)); + + if (dev < 0 || dev >= num_audiodevs) + return RET_ERROR (ENXIO); + + devc = (ad1848_info *) audio_devs[dev]->devc; + + DISABLE_INTR (flags); + if (devc->opened) + { + RESTORE_INTR (flags); + printk ("ad1848: Already opened\n"); + return RET_ERROR (EBUSY); + } + + if (devc->irq) /* Not managed by another driver */ + if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt)) < 0) + { + printk ("ad1848: IRQ in use\n"); + RESTORE_INTR (flags); + return err; + } + + if (DMAbuf_open_dma (dev) < 0) + { + RESTORE_INTR (flags); + printk ("ad1848: DMA in use\n"); + return RET_ERROR (EBUSY); + } + + devc->intr_active = 0; + devc->opened = 1; + RESTORE_INTR (flags); + + return 0; +} + +static void +ad1848_close (int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + DEB (printk ("ad1848_close(void)\n")); + + DISABLE_INTR (flags); + + devc->intr_active = 0; + if (devc->irq) /* Not managed by another driver */ + snd_release_irq (devc->irq); + ad1848_reset (dev); + DMAbuf_close_dma (dev); + devc->opened = 0; + + RESTORE_INTR (flags); +} + +static int +set_speed (ad1848_info * devc, int arg) +{ + /* + * The sampling speed is encoded in the least significant nible of I8. The + * LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other + * three bits select the divisor (indirectly): + * + * The available speeds are in the following table. Keep the speeds in + * the increasing order. + */ + typedef struct + { + int speed; + unsigned char bits; + } + speed_struct; + + static speed_struct speed_table[] = + { + {5510, (0 << 1) | 1}, + {5510, (0 << 1) | 1}, + {6620, (7 << 1) | 1}, + {8000, (0 << 1) | 0}, + {9600, (7 << 1) | 0}, + {11025, (1 << 1) | 1}, + {16000, (1 << 1) | 0}, + {18900, (2 << 1) | 1}, + {22050, (3 << 1) | 1}, + {27420, (2 << 1) | 0}, + {32000, (3 << 1) | 0}, + {33075, (6 << 1) | 1}, + {37800, (4 << 1) | 1}, + {44100, (5 << 1) | 1}, + {48000, (6 << 1) | 0} + }; + + int i, n, selected = -1; + + n = sizeof (speed_table) / sizeof (speed_struct); + + if (arg < speed_table[0].speed) + selected = 0; + if (arg > speed_table[n - 1].speed) + selected = n - 1; + + for (i = 1 /*really*/ ; selected == -1 && i < n; i++) + if (speed_table[i].speed == arg) + selected = i; + else if (speed_table[i].speed > arg) + { + int diff1, diff2; + + diff1 = arg - speed_table[i - 1].speed; + diff2 = speed_table[i].speed - arg; + + if (diff1 < diff2) + selected = i - 1; + else + selected = i; + } + + if (selected == -1) + { + printk ("ad1848: Can't find speed???\n"); + selected = 3; + } + + devc->speed = speed_table[selected].speed; + devc->speed_bits = speed_table[selected].bits; + return devc->speed; +} + +static int +set_channels (ad1848_info * devc, int arg) +{ + if (arg != 1 && arg != 2) + return devc->channels; + + devc->channels = arg; + return arg; +} + +static int +set_format (ad1848_info * devc, int arg) +{ + + static struct format_tbl + { + int format; + unsigned char bits; + } + format2bits [] = + { + { + 0, 0 + } + , + { + AFMT_MU_LAW, 1 + } + , + { + AFMT_A_LAW, 3 + } + , + { + AFMT_IMA_ADPCM, 5 + } + , + { + AFMT_U8, 0 + } + , + { + AFMT_S16_LE, 2 + } + , + { + AFMT_S16_BE, 6 + } + , + { + AFMT_S8, 0 + } + , + { + AFMT_U16_LE, 0 + } + , + { + AFMT_U16_BE, 0 + } + }; + int i, n = sizeof (format2bits) / sizeof (struct format_tbl); + + if (!(arg & ad_format_mask[devc->mode])) + arg = AFMT_U8; + + devc->audio_format = arg; + + for (i = 0; i < n; i++) + if (format2bits[i].format == arg) + { + if ((devc->format_bits = format2bits[i].bits) == 0) + return devc->audio_format = AFMT_U8; /* Was not supported */ + + return arg; + } + + /* Still hanging here. Something must be terribly wrong */ + devc->format_bits = 0; + return devc->audio_format = AFMT_U8; +} + +static int +ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return set_speed (devc, arg); + return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg))); + + case SOUND_PCM_READ_RATE: + if (local) + return devc->speed; + return IOCTL_OUT (arg, devc->speed); + + case SNDCTL_DSP_STEREO: + if (local) + return set_channels (devc, arg + 1) - 1; + return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return set_channels (devc, arg); + return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg))); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return devc->channels; + return IOCTL_OUT (arg, devc->channels); + + case SNDCTL_DSP_SAMPLESIZE: + if (local) + return set_format (devc, arg); + return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg))); + + case SOUND_PCM_READ_BITS: + if (local) + return devc->audio_format; + return IOCTL_OUT (arg, devc->audio_format); + + default:; + } + return RET_ERROR (EINVAL); +} + +static void +ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +{ + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + cnt = count; + + if (devc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (devc->channels > 1) + cnt >>= 1; + cnt--; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == devc->xfer_count) + { + devc->irq_mode = IMODE_OUTPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + DISABLE_INTR (flags); + + if (dma_restart) + { + ad1848_halt (dev); + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + } + + ad_set_MCE (devc, 1); + + ad_write (devc, 15, (unsigned char) (cnt & 0xff)); + ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + + + ad_write (devc, 9, 0x0d); /* + * Playback enable, single DMA channel mode, + * auto calibration on. + */ + + ad_set_MCE (devc, 0); /* + * Starts the calibration process and + * enters playback mode after it. + */ + wait_for_calibration (devc); + + devc->xfer_count = cnt; + devc->irq_mode = IMODE_OUTPUT; + devc->intr_active = 1; + RESTORE_INTR (flags); +} + +static void +ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +{ + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + /* int count_reg = (devc->mode == 1) ? 14 : 30; */ + + cnt = count; + if (devc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (devc->channels > 1) + cnt >>= 1; + cnt--; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == devc->xfer_count) + { + devc->irq_mode = IMODE_INPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + DISABLE_INTR (flags); + + if (dma_restart) + { + ad1848_halt (dev); + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + } + + ad_set_MCE (devc, 1); +#if 0 + ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff)); + ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff)); +#else + ad_write (devc, 15, (unsigned char) (cnt & 0xff)); + ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + if (devc->mode == 2) + { + ad_write (devc, 31, (unsigned char) (cnt & 0xff)); + ad_write (devc, 32, (unsigned char) ((cnt >> 8) & 0xff)); + } +#endif + + ad_write (devc, 9, 0x0e); /* + * Capture enable, single DMA channel mode, + * auto calibration on. + */ + + ad_set_MCE (devc, 0); /* + * Starts the calibration process and + * enters playback mode after it. + */ + wait_for_calibration (devc); + + devc->xfer_count = cnt; + devc->irq_mode = IMODE_INPUT; + devc->intr_active = 1; + RESTORE_INTR (flags); +} + +static int +ad1848_prepare_for_IO (int dev, int bsize, int bcount) +{ + int timeout; + unsigned char fs; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + DISABLE_INTR (flags); + ad_set_MCE (devc, 1); /* Enables changes to the format select reg */ + fs = devc->speed_bits | (devc->format_bits << 5); + + if (devc->channels > 1) + fs |= 0x10; + + ad_write (devc, 8, fs); + /* + * Write to I8 starts resyncronization. Wait until it completes. + */ + timeout = 10000; + while (timeout > 0 && INB (devc->base) == 0x80) + timeout--; + + ad_set_MCE (devc, 0); /* + * Starts the calibration process and + * enters playback mode after it. + */ + wait_for_calibration (devc); + RESTORE_INTR (flags); + + /* + * If mode == 2 (CS4231), set I28 also. It's the capture format register. + */ + if (devc->mode == 2) + { + ad_set_MCE (devc, 1); + ad_write (devc, 28, fs); + + /* + * Write to I28 starts resyncronization. Wait until it completes. + */ + timeout = 10000; + while (timeout > 0 && INB (devc->base) == 0x80) + timeout--; + + ad_set_MCE (devc, 0); /* + * Starts the calibration process and + * enters playback mode after it. + */ + wait_for_calibration (devc); + RESTORE_INTR (flags); + } + devc->xfer_count = 0; + return 0; +} + +static void +ad1848_reset (int dev) +{ + ad1848_halt (dev); +} + +static void +ad1848_halt (int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + ad_write (devc, 9, 0); /* Clear the PEN and CEN bits (among others) */ + OUTB (0, io_Status (devc)); /* Clear interrupt status */ +} + +int +ad1848_detect (int io_base) +{ + +#define DDB(x) x + + unsigned char tmp; + int i; + ad1848_info *devc = &dev_info[nr_ad1848_devs]; + unsigned char tmp1 = 0xff, tmp2 = 0xff; + + if (nr_ad1848_devs >= MAX_AUDIO_DEV) + return 0; + + devc->base = io_base; + devc->MCE_bit = 0x40; + devc->irq = 0; + devc->dma_capture = 0; + devc->dma_playback = 0; + devc->opened = 0; + devc->chip_name = "AD1848"; + devc->mode = 1; /* MODE1 = original AD1848 */ + + /* + * Check that the I/O address is in use. + * + * The bit 0x80 of the base I/O port is known to be 0 after the + * chip has performed it's power on initialization. Just assume + * this has happened before the OS is starting. + * + * If the I/O address is unused, it typically returns 0xff. + */ + + if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */ + { + DDB (printk ("ad_detect_A\n")); + return 0; + } + + /* + * Test if it's possible to change contents of the indirect registers. + * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only + * so try to avoid using it. +*/ + + ad_write (devc, 0, 0xaa); + ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ + + if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45) + { + DDB (printk ("ad_detect_B (%x/%x)\n", tmp1, tmp2)); + return 0; + } + + ad_write (devc, 0, 0x45); + ad_write (devc, 1, 0xaa); + + if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa) + { + DDB (printk ("ad_detect_C (%x/%x)\n", tmp1, tmp2)); + return 0; + } + + /* + * The indirect register I12 has some read only bits. Lets + * try to change them. + */ + + tmp = ad_read (devc, 12); + ad_write (devc, 12, (~tmp) & 0x0f); + + if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f)) + { + DDB (printk ("ad_detect_D (%x)\n", tmp1)); + return 0; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB and 0x0A=RevC. + */ + + /* + * The original AD1848/CS4248 has just 15 indirect registers. This means + * that I0 and I16 should return the same value (etc.). + * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails + * with CS4231. + */ + + ad_write (devc, 12, 0); /* Mode2=disabled */ + + for (i = 0; i < 16; i++) + if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16))) + { + DDB (printk ("ad_detect_F(%d/%x/%x)\n", i, tmp1, tmp2)); + return 0; + } + + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). + * The bit 0x80 is always 1 in CS4248 and CS4231. + */ + + ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */ + + tmp1 = ad_read (devc, 12); + if (tmp1 & 0x80) + devc->chip_name = "CS4248"; + + if ((tmp1 & 0xc0) == (0x80 | 0x40)) + { + /* + * CS4231 detected - is it? + * + * Verify that setting I0 doesn't change I16. + */ + ad_write (devc, 16, 0); /* Set I16 to known value */ + + ad_write (devc, 0, 0x45); + if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */ + { + + ad_write (devc, 0, 0xaa); + if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */ + { + DDB (printk ("ad_detect_H(%x)\n", tmp1)); + return 0; + } + + /* + * It's a CS4231 - So what! + * (Mode2 will be supported later) + */ + devc->chip_name = "CS4231"; + devc->mode = 2; + } + } + + return 1; +} + +void +ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture) +{ + /* + * NOTE! If irq < 0, there is another driver which has allocated the IRQ + * so that this driver doesn't need to allocate/deallocate it. + * The actually used IRQ is ABS(irq). + */ + + /* + * Initial values for the indirect registers of CS4248/AD1848. + */ + static int init_values[] = + { + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x00, 0xca, 0x00, 0x00, 0x00 + }; + int i, my_dev; + ad1848_info *devc = &dev_info[nr_ad1848_devs]; + + if (!ad1848_detect (io_base)) + return; + + devc->irq = (irq > 0) ? irq : 0; + devc->dma_capture = dma_playback; + devc->dma_playback = dma_capture; + devc->opened = 0; + + if (nr_ad1848_devs != 0) + { + memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs], + (char *) &ad1848_pcm_operations[0], + sizeof (struct audio_operations)); + } + + for (i = 0; i < 16; i++) + ad_write (devc, i, init_values[i]); + + OUTB (0, io_Status (devc)); /* Clear pending interrupts */ + +#ifndef SCO + sprintf (ad1848_pcm_operations[nr_ad1848_devs].name, + "%s (%s)", name, devc->chip_name); +#endif + + if (irq > 0) + printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); + + if (num_audiodevs < MAX_AUDIO_DEV) + { + audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs]; + if (irq > 0) + irq2dev[irq] = my_dev; + else if (irq < 0) + irq2dev[-irq] = my_dev; + + audio_devs[my_dev]->dmachan = dma_playback; + audio_devs[my_dev]->buffcount = 1; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE * 2; + audio_devs[my_dev]->devc = devc; + audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode]; + nr_ad1848_devs++; + } + else + printk ("AD1848: Too many PCM devices available\n"); +} + +void +ad1848_interrupt (int irq) +{ + unsigned char status; + ad1848_info *devc; + int dev; + + if (irq < 0 || irq > 15) + return; /* Bogus irq */ + dev = irq2dev[irq]; + if (dev < 0 || dev >= num_audiodevs) + return; /* Bogus dev */ + + devc = (ad1848_info *) audio_devs[dev]->devc; + status = INB (io_Status (devc)); + + if (status & 0x01) + { + if (devc->opened && devc->irq_mode == IMODE_OUTPUT) + { + DMAbuf_outputintr (dev, 1); + } + + if (devc->opened && devc->irq_mode == IMODE_INPUT) + DMAbuf_inputintr (dev); + } + + OUTB (0, io_Status (devc)); /* Clear interrupt status */ +} + +#endif +/* + * Some extra code for the MS Sound System + */ +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MSS) + +int +probe_ms_sound (struct address_info *hw_config) +{ + if ((INB (hw_config->io_base + 3) & 0x04) == 0) + return 0; /* WSS ID test failed */ + + if (hw_config->irq > 11) + return 0; + + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + return 0; + + return ad1848_detect (hw_config->io_base + 4); +} + +long +attach_ms_sound (long mem_start, struct address_info *hw_config) +{ + static unsigned char interrupt_bits[12] = + {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20}; + char bits; + + static unsigned char dma_bits[4] = {1, 2, 0, 3}; + + int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; + + if (!ad1848_detect (hw_config->io_base + 4)) + return mem_start; + + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + return mem_start; + + OUTB (bits | 0x40, config_port); /* Verify IRQ (I guess) */ + if ((INB (version_port) & 0x40) == 0) + printk ("[IRQ?]"); + + OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */ + + ad1848_init ("MS Sound System", hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma); + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c new file mode 100644 index 00000000000..6365069384a --- /dev/null +++ b/sys/i386/isa/sound/adlib_card.c @@ -0,0 +1,51 @@ +/* + * sound/adlib_card.c + * + * Detection routine for the AdLib card. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812) + +long +attach_adlib_card (long mem_start, struct address_info *hw_config) +{ + + if (opl3_detect (FM_MONO)) + { + mem_start = opl3_init (mem_start); + } + return mem_start; +} + +int +probe_adlib (struct address_info *hw_config) +{ + return opl3_detect (FM_MONO); +} + +#endif diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c new file mode 100644 index 00000000000..d5a4acce44e --- /dev/null +++ b/sys/i386/isa/sound/audio.c @@ -0,0 +1,424 @@ +/* + * sound/audio.c + * + * Device file manager for /dev/audio + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD +#ifndef EXCLUDE_AUDIO + +#include "ulaw.h" + +#define ON 1 +#define OFF 0 + +static int wr_buff_no[MAX_AUDIO_DEV]; /* + * != -1, if there is + * a incomplete output + * block in the queue. + */ +static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV]; + +static int audio_mode[MAX_AUDIO_DEV]; + +#define AM_NONE 0 +#define AM_WRITE 1 +#define AM_READ 2 + +static char *wr_dma_buf[MAX_AUDIO_DEV]; +static int audio_format[MAX_AUDIO_DEV]; +static int local_conversion[MAX_AUDIO_DEV]; + +static int +set_format (int dev, int fmt) +{ + if (fmt != AFMT_QUERY) + { + + local_conversion[dev] = 0; + + if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ + if (fmt == AFMT_MU_LAW) + { + fmt = AFMT_U8; + local_conversion[dev] = AFMT_MU_LAW; + } + else + fmt = AFMT_U8; /* This is always supported */ + + audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, fmt, 1); + } + + if (local_conversion[dev]) /* This shadows the HW format */ + return local_conversion[dev]; + + return audio_format[dev]; +} + +int +audio_open (int dev, struct fileinfo *file) +{ + int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + if (dev_type == SND_DEV_DSP16) + bits = 16; + else + bits = 8; + + if ((ret = DMAbuf_open (dev, mode)) < 0) + return ret; + + local_conversion[dev] = 0; + + if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, bits, 1) != bits) + { + audio_release (dev, file); + return RET_ERROR (ENXIO); + } + + if (dev_type == SND_DEV_AUDIO) + { + set_format (dev, AFMT_MU_LAW); + } + else + set_format (dev, bits); + + wr_buff_no[dev] = -1; + audio_mode[dev] = AM_NONE; + + return ret; +} + +void +audio_release (int dev, struct fileinfo *file) +{ + int mode; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + + DMAbuf_release (dev, mode); +} + +#ifdef NO_INLINE_ASM +static void +translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n) +{ + unsigned long i; + + for (i = 0; i < n; ++i) + buff[i] = table[buff[i]]; +} + +#else +extern inline void +translate_bytes (const void *table, void *buff, unsigned long n) +{ + __asm__ ("cld\n" + "1:\tlodsb\n\t" + "xlatb\n\t" + "stosb\n\t" + "loop 1b\n\t": + :"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff) + :"bx", "cx", "di", "si", "ax"); +} + +#endif + +int +audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c, p, l; + int err; + + dev = dev >> 4; + + p = 0; + c = count; + + if (audio_mode[dev] == AM_READ) /* + * Direction changed + */ + { + wr_buff_no[dev] = -1; + } + + audio_mode[dev] = AM_WRITE; + + if (!count) /* + * Flush output + */ + { + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return 0; + } + + while (c) + { /* + * Perform output blocking + */ + if (wr_buff_no[dev] < 0) /* + * There is no incomplete buffers + */ + { + if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0) + { + return wr_buff_no[dev]; + } + wr_buff_ptr[dev] = 0; + } + + l = c; + if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) + l = (wr_buff_size[dev] - wr_buff_ptr[dev]); + + if (!audio_devs[dev]->copy_from_user) + { /* + * No device specific copy routine + */ + COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l); + } + else + audio_devs[dev]->copy_from_user (dev, + wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l); + + + /* + * Insert local processing here + */ + + if (local_conversion[dev] == AFMT_MU_LAW) + { +#ifdef linux + /* + * This just allows interrupts while the conversion is running + */ + __asm__ ("sti"); +#endif + translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l); + } + + c -= l; + p += l; + wr_buff_ptr[dev] += l; + + if (wr_buff_ptr[dev] >= wr_buff_size[dev]) + { + if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0) + { + return err; + } + + wr_buff_no[dev] = -1; + } + + } + + return count; +} + +int +audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c, p, l; + char *dmabuf; + int buff_no; + + dev = dev >> 4; + p = 0; + c = count; + + if (audio_mode[dev] == AM_WRITE) + { + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + } + + audio_mode[dev] = AM_READ; + + while (c) + { + if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0) + return buff_no; + + if (l > c) + l = c; + + /* + * Insert any local processing here. + */ + + if (local_conversion[dev] == AFMT_MU_LAW) + { +#ifdef linux + /* + * This just allows interrupts while the conversion is running + */ + __asm__ ("sti"); +#endif + + translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l); + } + + COPY_TO_USER (buf, p, dmabuf, l); + + DMAbuf_rmchars (dev, buff_no, l); + + p += l; + c -= l; + } + + return count - c; +} + +int +audio_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + + dev = dev >> 4; + + switch (cmd) + { + case SNDCTL_DSP_SYNC: + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + + case SNDCTL_DSP_POST: + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return 0; + break; + + case SNDCTL_DSP_RESET: + wr_buff_no[dev] = -1; + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + + case SNDCTL_DSP_GETFMTS: + return IOCTL_OUT (arg, audio_devs[dev]->format_mask); + break; + + case SNDCTL_DSP_SETFMT: + return IOCTL_OUT (arg, set_format (dev, IOCTL_IN (arg))); + + default: + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + } +} + +long +audio_init (long mem_start) +{ + /* + * NOTE! This routine could be called several times during boot. + */ + return mem_start; +} + +#else +/* + * Stub versions + */ + +int +audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +audio_open (int dev, struct fileinfo *file) +{ + return RET_ERROR (ENXIO); +} + +void +audio_release (int dev, struct fileinfo *file) +{ +}; +int +audio_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + return RET_ERROR (EIO); +} + +int +audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig) +{ + return RET_ERROR (EIO); +} + +long +audio_init (long mem_start) +{ + return mem_start; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/configure.c b/sys/i386/isa/sound/configure.c new file mode 100644 index 00000000000..5b7217bccb4 --- /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 + +#define B(x) (1 << (x)) + +/* + * Option numbers + */ + +#define OPT_PAS 0 +#define OPT_SB 1 +#define OPT_ADLIB 2 +#define OPT_LAST_MUTUAL 2 + +#define OPT_GUS 3 +#define OPT_MPU401 4 +#define OPT_UART6850 5 +#define OPT_PSS 6 +#define OPT_GUS16 7 +#define OPT_GUSMAX 8 +#define OPT_MSS 9 + +#define OPT_HIGHLEVEL 10 /* This must be same than the next one */ +#define OPT_SBPRO 10 +#define OPT_SB16 11 +#define OPT_AUDIO 12 +#define OPT_MIDI_AUTO 13 +#define OPT_MIDI 14 +#define OPT_YM3812_AUTO 15 +#define OPT_YM3812 16 +#define OPT_SEQUENCER 17 +#define OPT_LAST 17 /* Must be the same than the defined OPT */ + +#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)| \ + B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)|B(OPT_MSS)) +/* + * Options that have been disabled for some reason (incompletely implemented + * and/or tested). Don't remove from this list before looking at file + * experimental.txt for further info. + */ +#define DISABLED_OPTIONS (B(OPT_PSS)) + +typedef struct + { + unsigned long conditions; + unsigned long exclusive_options; + char macro[20]; + int verify; + int alias; + int default_answ; + } + +hw_entry; + + +/* + * The rule table for the driver options. The first field defines a set of + * options which must be selected before this entry can be selected. The + * second field is a set of options which are not allowed with this one. If + * the fourth field is zero, the option is selected without asking + * confirmation from the user. + * + * With this version of the rule table it is possible to select just one type of + * hardware. + * + * NOTE! Keep the following table and the questions array in sync with the + * option numbering! + */ + +hw_entry hw_table[] = +{ +/* + * 0 + */ + {0, 0, "PAS", 1, 0, 0}, + {0, 0, "SB", 1, 0, 0}, + {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0}, + + {0, 0, "GUS", 1, 0, 0}, + {0, 0, "MPU401", 1, 0, 0}, + {0, 0, "UART6850", 1, 0, 0}, + {0, 0, "PSS", 1, 0, 0}, + {B (OPT_GUS), 0, "GUS16", 1, 0, 0}, + {B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0}, + {0, 0, "MSS", 1, 0, 0}, + + {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1}, + {B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1}, +{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0, 1}, + {B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI, 0}, + {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0, 1}, + {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0}, + {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1}, + {B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0, 1} +}; + +char *questions[] = +{ + "ProAudioSpectrum 16 support", + "SoundBlaster support", + "AdLib support", + "Gravis Ultrasound support", + "MPU-401 support (NOT for SB16)", + "6850 UART Midi support", + "PSS (ECHO-ADI2111) support", + "16 bit sampling option of GUS (_NOT_ GUS MAX)", + "GUS MAX support", + "Microsoft Sound System support", + + "SoundBlaster Pro support", + "SoundBlaster 16 support", + "digitized voice support", + "This should not be asked", + "MIDI interface support", + "This should not be asked", + "FM synthesizer (YM3812/OPL-3) support", + "/dev/sequencer support", + "Should I die" +}; + +unsigned long selected_options = 0; +int sb_dma = 0; + +int +can_select_option (int nr) +{ + switch (nr) + { + case 0: + fprintf (stderr, "The SoundBlaster, AdLib and ProAudioSpectrum\n" + "CARDS cannot be installed at the same time.\n\n" + "However the PAS16 has a SB emulator so you could select" + "the SoundBlaster DRIVER with it.\n"); + fprintf (stderr, " - ProAudioSpectrum 16\n"); + fprintf (stderr, " - SoundBlaster / SB Pro\n"); + fprintf (stderr, " (Could be selected with a PAS16 also)\n"); + fprintf (stderr, " - AdLib\n"); + fprintf (stderr, "\nDon't enable SoundBlaster if you have GUS at 0x220!\n\n"); + break; + + case OPT_LAST_MUTUAL + 1: + fprintf (stderr, "\nThe following cards should work with any other cards.\n" + "CAUTION! Don't enable MPU-401 if you don't have it.\n"); + break; + + case OPT_HIGHLEVEL: + fprintf (stderr, "\nSelect one or more of the following options\n"); + break; + + + } + + if (hw_table[nr].conditions) + if (!(hw_table[nr].conditions & selected_options)) + return 0; + + if (hw_table[nr].exclusive_options) + if (hw_table[nr].exclusive_options & selected_options) + return 0; + + if (DISABLED_OPTIONS & B (nr)) + return 0; + + return 1; +} + +int +think_positively (int def_answ) +{ + char answ[512]; + int len; + + if ((len = read (0, &answ, sizeof (answ))) < 1) + { + fprintf (stderr, "\n\nERROR! Cannot read stdin\n"); + + perror ("stdin"); + printf ("#undef CONFIGURE_SOUNDCARD\n"); + printf ("#undef KERNEL_SOUNDCARD\n"); + exit (-1); + } + + if (len < 2) /* + * There is an additional LF at the end + */ + return def_answ; + + answ[len - 1] = 0; + + if (!strcmp (answ, "y") || !strcmp (answ, "Y")) + return 1; + + return 0; +} + +int +ask_value (char *format, int default_answer) +{ + char answ[512]; + int len, num; + +play_it_again_Sam: + + if ((len = read (0, &answ, sizeof (answ))) < 1) + { + fprintf (stderr, "\n\nERROR! Cannot read stdin\n"); + + perror ("stdin"); + printf ("#undef CONFIGURE_SOUNDCARD\n"); + printf ("#undef KERNEL_SOUNDCARD\n"); + exit (-1); + } + + if (len < 2) /* + * There is an additional LF at the end + */ + return default_answer; + + answ[len - 1] = 0; + + if (sscanf (answ, format, &num) != 1) + { + fprintf (stderr, "Illegal format. Try again: "); + goto play_it_again_Sam; + } + + return num; +} + +int +main (int argc, char *argv[]) +{ + int i, num, def_size, full_driver = 1; + char answ[10]; + + printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n"); + + fprintf (stderr, "\nConfiguring the sound support\n\n"); + + fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? "); + + if (think_positively (0)) + { + /* + * Select all but some most dangerous cards. These cards are difficult to + * detect reliably or conflict with some other cards (SCSI, Mitsumi) + */ + selected_options = 0xffffffff & + ~(B (OPT_MPU401) | B (OPT_UART6850) | B (OPT_PSS)) & + ~DISABLED_OPTIONS; + + fprintf (stderr, "Note! MPU-401, PSS and 6850 UART drivers not enabled\n"); + full_driver = 1; + } + else + { + fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?"); + if (think_positively (0)) + { + printf ("#undef CONFIGURE_SOUNDCARD\n"); + printf ("#undef KERNEL_SOUNDCARD\n"); + exit (0); + } + /* + * Partial driver + */ + + full_driver = 0; + + for (i = 0; i <= OPT_LAST; i++) + if (can_select_option (i)) + { + if (!(selected_options & B (i))) /* + * Not selected yet + */ + if (!hw_table[i].verify) + { + if (hw_table[i].alias) + selected_options |= B (hw_table[i].alias); + else + selected_options |= B (i); + } + else + { + int def_answ = hw_table[i].default_answ; + + fprintf (stderr, + def_answ ? " %s (y/n) ? " : " %s (n/y) ? ", + questions[i]); + if (think_positively (def_answ)) + if (hw_table[i].alias) + selected_options |= B (hw_table[i].alias); + else + selected_options |= B (i); + } + } + } + + if (selected_options & B (OPT_SBPRO)) + { + fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? "); + if (think_positively (0)) + printf("#define __SGNXPRO__\n"); + } + + if (selected_options & B (OPT_SB16)) + selected_options |= B (OPT_SBPRO); + + if (selected_options & B (OPT_PSS)) + { + genld_again: + fprintf + (stderr, + "if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n" + "then you must include the LD in the kernel.\n" + "(do you wish to include a LD) ? "); + if (think_positively (0)) + { + char path[512]; + + fprintf (stderr, + "Enter the path to your LD file (pwd is sound): "); + scanf ("%s", path); + fprintf (stderr, "including LD file %s\n", path); + selected_options |= B (OPT_SB) | B (OPT_MPU401) | B (OPT_ADLIB); + + /* Gen LD header */ + { + int fd; + int count; + char c; + int i = 0; + + if ((fd = open (path, 0)) > 0) + { + FILE *sf = fopen ("synth-ld.h", "w"); + + fprintf (sf, "/* automaticaly generated by configure */\n"); + fprintf (sf, "unsigned char pss_synth[] = {\n"); + while (1) + { + count = read (fd, &c, 1); + if (count == 0) + break; + if (i != 0 && (i % 10) == 0) + fprintf (sf, "\n"); + fprintf (sf, "0x%02x,", c & 0xFFL); + i++; + } + fprintf (sf, "};\n" + "#define pss_synthLen %d\n", i); + fclose (sf); + close (fd); + } + else + { + fprintf (stderr, "couldn't open %s as the ld file\n", + path); + fprintf (stderr, "try again with correct path? "); + if (think_positively (1)) + goto genld_again; + } + } + } + else + { + FILE *sf = fopen ("synth-ld.h", "w"); + + fprintf (sf, "/* automaticaly generated by configure */\n"); + fprintf (sf, "unsigned char pss_synth[1];\n" + "#define pss_synthLen 0\n"); + fclose (sf); + } + } + + if (!(selected_options & ANY_DEVS)) + { + printf ("#undef CONFIGURE_SOUNDCARD\n"); + printf ("#undef KERNEL_SOUNDCARD\n"); + fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n\n"); + exit (0); + } + else + printf ("#define KERNEL_SOUNDCARD\n"); + + for (i = 0; i <= OPT_LAST; i++) + if (!hw_table[i].alias) + if (selected_options & B (i)) + printf ("#undef EXCLUDE_%s\n", hw_table[i].macro); + else + printf ("#define EXCLUDE_%s\n", hw_table[i].macro); + + + /* + * IRQ and DMA settings + */ + printf ("\n"); + +#if defined(linux) + if ((selected_options & B (OPT_SB)) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI))) + { + fprintf (stderr, "\nI/O base for SB?\n" + "The factory default is 220\n" + "Enter the SB I/O base: "); + + num = ask_value ("%x", 0x220); + fprintf (stderr, "SB I/O base set to %03x\n", num); + printf ("#define SBC_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for SoundBlaster?\n" + "The IRQ address is defined by the jumpers on your card.\n" + "The factory default is either 5 or 7 (depending on the model).\n" + "Valid values are 9(=2), 5, 7 and 10.\n" + "Enter the value: "); + + num = ask_value ("%d", 7); + if (num != 9 && num != 5 && num != 7 && num != 10) + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 7; + } + fprintf (stderr, "SoundBlaster IRQ set to %d\n", num); + + printf ("#define SBC_IRQ %d\n", num); + + if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_PSS))) + { + fprintf (stderr, "\nDMA channel for SoundBlaster?\n" + "For SB 1.0, 1.5 and 2.0 this MUST be 1\n" + "SB Pro supports DMA channels 0, 1 and 3 (jumper)\n" + "For SB16 give the 8 bit DMA# here\n" + "The default value is 1\n" + "Enter the value: "); + + num = ask_value ("%d", 1); + if (num < 0 || num > 3) + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 1; + } + fprintf (stderr, "SoundBlaster DMA set to %d\n", num); + printf ("#define SBC_DMA %d\n", num); + sb_dma = num; + } + + if (selected_options & B (OPT_SB16)) + { + + fprintf (stderr, "\n16 bit DMA channel for SoundBlaster 16?\n" + "Possible values are 5, 6 or 7\n" + "The default value is 6\n" + "Enter the value: "); + + num = ask_value ("%d", 6); + if ((num < 5 || num > 7) && (num != sb_dma)) + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 6; + } + fprintf (stderr, "SoundBlaster DMA set to %d\n", num); + printf ("#define SB16_DMA %d\n", num); + + fprintf (stderr, "\nI/O base for SB16 Midi?\n" + "Possible values are 300 and 330\n" + "The factory default is 330\n" + "Enter the SB16 Midi I/O base: "); + + num = ask_value ("%x", 0x330); + fprintf (stderr, "SB16 Midi I/O base set to %03x\n", num); + printf ("#define SB16MIDI_BASE 0x%03x\n", num); + } + } + + if (selected_options & B (OPT_PAS)) + { + if (selected_options & (B (OPT_AUDIO) | B (OPT_MIDI))) + { + fprintf (stderr, "\nIRQ number for ProAudioSpectrum?\n" + "The recommended value is the IRQ used under DOS.\n" + "Please refer to the ProAudioSpectrum User's Guide.\n" + "The default value is 10.\n" + "Enter the value: "); + + num = ask_value ("%d", 10); + if (num == 6 || num < 3 || num > 15 || num == 2) /* + * Illegal + */ + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 10; + } + fprintf (stderr, "ProAudioSpectrum IRQ set to %d\n", num); + printf ("#define PAS_IRQ %d\n", num); + } + + if (selected_options & B (OPT_AUDIO)) + { + fprintf (stderr, "\nDMA number for ProAudioSpectrum?\n" + "The recommended value is the DMA channel under DOS.\n" + "Please refer to the ProAudioSpectrum User's Guide.\n" + "The default value is 3\n" + "Enter the value: "); + + num = ask_value ("%d", 3); + if (num == 4 || num < 0 || num > 7) + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 3; + } + fprintf (stderr, "\nProAudioSpectrum DMA set to %d\n", num); + printf ("#define PAS_DMA %d\n", num); + } + } + + if (selected_options & B (OPT_GUS)) + { + fprintf (stderr, "\nI/O base for Gravis Ultrasound?\n" + "Valid choices are 210, 220, 230, 240, 250 or 260\n" + "The factory default is 220\n" + "Enter the GUS I/O base: "); + + num = ask_value ("%x", 0x220); + if ((num > 0x260) || ((num & 0xf0f) != 0x200) || ((num & 0x0f0) > 0x060)) + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 0x220; + } + + if ((selected_options & B (OPT_SB)) && (num == 0x220)) + { + fprintf (stderr, "FATAL ERROR!!!!!!!!!!!!!!\n" + "\t0x220 cannot be used if SoundBlaster is enabled.\n" + "\tRun the config again.\n"); + printf ("#undef CONFIGURE_SOUNDCARD\n"); + printf ("#undef KERNEL_SOUNDCARD\n"); + exit (-1); + } + fprintf (stderr, "GUS I/O base set to %03x\n", num); + printf ("#define GUS_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for Gravis UltraSound?\n" + "The recommended value is the IRQ used under DOS.\n" + "Please refer to the Gravis Ultrasound User's Guide.\n" + "The default value is 15.\n" + "Enter the value: "); + + num = ask_value ("%d", 15); + if (num == 6 || num < 3 || num > 15 || num == 2) /* + * Invalid + */ + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 15; + } + fprintf (stderr, "Gravis UltraSound IRQ set to %d\n", num); + printf ("#define GUS_IRQ %d\n", num); + + fprintf (stderr, "\nDMA number for Gravis UltraSound?\n" + "The recommended value is the DMA channel under DOS.\n" + "Please refer to the Gravis Ultrasound User's Guide.\n" + "The default value is 6\n" + "Enter the value: "); + + num = ask_value ("%d", 6); + if (num == 4 || num < 0 || num > 7) + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 6; + } + fprintf (stderr, "\nGravis UltraSound DMA set to %d\n", num); + printf ("#define GUS_DMA %d\n", num); + } + + if (selected_options & B (OPT_MPU401)) + { + fprintf (stderr, "\nI/O base for MPU-401?\n" + "The factory default is 330\n" + "Enter the MPU-401 I/O base: "); + + num = ask_value ("%x", 0x330); + fprintf (stderr, "MPU-401 I/O base set to %03x\n", num); + printf ("#define MPU_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for MPU-401?\n" + "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n" + "The default value is 9.\n" + "Enter the value: "); + + num = ask_value ("%d", 9); + if (num == 6 || num < 3 || num > 15) /* + * Used for floppy + */ + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 5; + } + fprintf (stderr, "MPU-401 IRQ set to %d\n", num); + printf ("#define MPU_IRQ %d\n", num); + } + + if (selected_options & B (OPT_UART6850)) + { + fprintf (stderr, "\nI/O base for 6850 UART Midi?\n" + "Be carefull. No defaults.\n" + "Enter the 6850 UART I/O base: "); + + num = ask_value ("%x", 0); + if (num == 0) + { + /* + * Invalid value entered + */ + printf ("#define EXCLUDE_UART6850\n"); + } + else + { + fprintf (stderr, "6850 UART I/O base set to %03x\n", num); + printf ("#define U6850_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for 6850 UART?\n" + "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n" + "The default value is 5.\n" + "Enter the value: "); + + num = ask_value ("%d", 5); + if (num == 6 || num < 3 || num > 15) /* + * Used for floppy + */ + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = 5; + } + fprintf (stderr, "6850 UART IRQ set to %d\n", num); + printf ("#define U6850_IRQ %d\n", num); + } + } + + if (selected_options & B (OPT_PSS)) + { + fprintf (stderr, "\nI/O base for PSS?\n" + "The factory default is 220\n" + "Enter the PSS I/O base: "); + + num = ask_value ("%x", 0x220); + fprintf (stderr, "PSS I/O base set to %03x\n", num); + printf ("#define PSS_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for PSS?\n" + "Valid numbers are: 3, 4, 5, 7, 9(=2) or 10.\n" + "The default value is 10.\n" + "Enter the value: "); + + num = ask_value ("%d", 10); + if (num == 6 || num < 3 || num > 15) /* Used for floppy */ + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 7; + } + fprintf (stderr, "PSS IRQ set to %d\n", num); + printf ("#define PSS_IRQ %d\n", num); + + fprintf (stderr, "\nDMA number for ECHO-PSS?\n" + "The default value is 3\n" + "Enter the value: "); + + num = ask_value ("%d", 3); + if (num == 4 || num < 0 || num > 7) + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 3; + } + fprintf (stderr, "\nECHO-PSS DMA set to %d\n", num); + printf ("#define PSS_DMA %d\n", num); + } + + if (selected_options & B (OPT_MSS)) + { + fprintf (stderr, "\nI/O base for MSS (MS Sound System)?\n" + "The factory default is 530\n" + "Other possible values are 604, E80 or F40\n" + "Enter the MSS I/O base: "); + + num = ask_value ("%x", 0x530); + fprintf (stderr, "MSS I/O base set to %03x\n", num); + printf ("#define MSS_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for MSS?\n" + "Valid numbers are: 7, 9(=2), 10 and 11.\n" + "The default value is 10.\n" + "Enter the value: "); + + num = ask_value ("%d", 10); + if (num == 6 || num < 3 || num > 15) /* Used for floppy */ + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 7; + } + fprintf (stderr, "MSS IRQ set to %d\n", num); + printf ("#define MSS_IRQ %d\n", num); + + fprintf (stderr, "\nDMA number for MSS?\n" + "Valid values are 1 and 3 (sometimes 0)" + "The default value is 3\n" + "Enter the value: "); + + num = ask_value ("%d", 3); + if (num == 4 || num < 0 || num > 7) + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 3; + } + fprintf (stderr, "\nMSS DMA set to %d\n", num); + printf ("#define MSS_DMA %d\n", num); + } + + if (selected_options & B (OPT_GUS16)) + { + fprintf (stderr, "\nI/O base for GUS16 (GUS 16 bit sampling option)?\n" + "The factory default is 530\n" + "Other possible values are 604, E80 or F40\n" + "Enter the GUS16 I/O base: "); + + num = ask_value ("%x", 0x530); + fprintf (stderr, "GUS16 I/O base set to %03x\n", num); + printf ("#define GUS16_BASE 0x%03x\n", num); + + fprintf (stderr, "\nIRQ number for GUS16?\n" + "Valid numbers are: 3, 4, 5, 7, or 9(=2).\n" + "The default value is 7.\n" + "Enter the value: "); + + num = ask_value ("%d", 7); + if (num == 6 || num < 3 || num > 15) /* Used for floppy */ + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 7; + } + fprintf (stderr, "GUS16 IRQ set to %d\n", num); + printf ("#define GUS16_IRQ %d\n", num); + + fprintf (stderr, "\nDMA number for GUS16?\n" + "The default value is 3\n" + "Enter the value: "); + + num = ask_value ("%d", 3); + if (num < 0 || num > 3) + { + fprintf (stderr, "*** Illegal input! ***\n"); + num = 3; + } + fprintf (stderr, "\nGUS16 DMA set to %d\n", num); + printf ("#define GUS16_DMA %d\n", num); + } +#endif + + if (selected_options & B (OPT_AUDIO)) + { + def_size = 16384; + + if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_SB16))) + def_size = 32768; + +#ifndef __386BSD__ + if ((selected_options & (B (OPT_PAS) | B (OPT_PAS) | B (OPT_GUS16) | B (OPT_GUSMAX) | + B (OPT_MSS) | B (OPT_PSS))) && + !full_driver) + def_size = 65536; /* + * PAS16 or SB16 + */ +#endif + + fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n" + "%d is recommended value for this configuration.\n" + "Enter the value: ", def_size); + + num = ask_value ("%d", def_size); + if (num != 4096 && num != 16384 && num != 32768 && num != 65536) + { + + fprintf (stderr, "*** Illegal input! ***\n"); + num = def_size; + } + fprintf (stderr, "The DMA buffer size set to %d\n", num); + printf ("#define DSP_BUFFSIZE %d\n", num); + } + + printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options); + fprintf (stderr, "The sound driver is now configured.\n"); + +#if defined(SCO) || defined(ISC) || defined(SYSV) + fprintf (stderr, "Remember to update the System file\n"); +#endif + + exit (0); +} diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c new file mode 100644 index 00000000000..1eb57bbb08c --- /dev/null +++ b/sys/i386/isa/sound/dev_table.c @@ -0,0 +1,269 @@ +/* + * sound/dev_table.c + * + * Device call tables. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define _DEV_TABLE_C_ +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +int +snd_find_driver (int type) +{ + int i, n = sizeof (sound_drivers) / sizeof (struct driver_info); + + for (i = 0; i < (n - 1); i++) + if (sound_drivers[i].card_type == type) + return i; + + return -1; /* + * Not found + */ +} + +long +sndtable_init (long mem_start) +{ + int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); + int drv; + + for (i = 0; i < (n - 1); i++) + if (snd_installed_cards[i].enabled) + if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + else if (sound_drivers[drv].probe (&snd_installed_cards[i].config)) + { +#ifndef SHORT_BANNERS + printk ("snd%d", + snd_installed_cards[i].card_type); +#endif + + mem_start = sound_drivers[drv].attach (mem_start, &snd_installed_cards[i].config); +#ifndef SHORT_BANNERS + printk (" at 0x%x irq %d drq %d\n", + snd_installed_cards[i].config.io_base, + snd_installed_cards[i].config.irq, + snd_installed_cards[i].config.dma); +#endif + } + else + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + return mem_start; +} + +int +sndtable_probe (int unit, struct address_info *hw_config) + { + int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); + + if (!unit) + return TRUE; + + for (i = 0; i < (n - 1); i++) + if (snd_installed_cards[i].enabled) + if (snd_installed_cards[i].card_type == unit) + { + int drv; + + snd_installed_cards[i].config.io_base = hw_config->io_base; + snd_installed_cards[i].config.irq = hw_config->irq; + snd_installed_cards[i].config.dma = hw_config->dma; + if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) + snd_installed_cards[i].enabled = 0; /* + * Mark as not + * detected + */ + else if (sound_drivers[drv].probe (hw_config)) + return 1; + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + return 0; + } + + return FALSE; + } + +int +sndtable_init_card (int unit, struct address_info *hw_config) + { + int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); + + if (!unit) + { + if (sndtable_init (0) != 0) + panic ("snd: Invalid memory allocation\n"); + return TRUE; + } + + for (i = 0; i < (n - 1); i++) + if (snd_installed_cards[i].card_type == unit) + { + int drv; + + snd_installed_cards[i].config.io_base = hw_config->io_base; + snd_installed_cards[i].config.irq = hw_config->irq; + snd_installed_cards[i].config.dma = hw_config->dma; + + if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + else if (sound_drivers[drv].attach (0, hw_config) != 0) + panic ("snd#: Invalid memory allocation\n"); + return TRUE; + } + + return FALSE; + } + +int +sndtable_get_cardcount (void) +{ + return num_audiodevs + num_mixers + num_synths + num_midis; +} + +#ifdef linux +void +sound_setup (char *str, int *ints) +{ + int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); + + /* + * First disable all drivers + */ + + for (i = 0; i < n; i++) + snd_installed_cards[i].enabled = 0; + + if (ints[0] == 0 || ints[1] == 0) + return; + /* + * Then enable them one by time + */ + + for (i = 1; i <= ints[0]; i++) + { + int card_type, ioaddr, irq, dma, ptr, j; + unsigned int val; + + val = (unsigned int) ints[i]; + + card_type = (val & 0x0ff00000) >> 20; + + if (card_type > 127) + { + /* + * Add any future extensions here + */ + return; + } + + ioaddr = (val & 0x000fff00) >> 8; + irq = (val & 0x000000f0) >> 4; + dma = (val & 0x0000000f); + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + if (snd_installed_cards[j].card_type == card_type && + !snd_installed_cards[j].enabled) /* + * Not already found + */ + ptr = j; + + if (ptr == -1) + printk ("Sound: Invalid setup parameter 0x%08x\n", val); + else + { + snd_installed_cards[ptr].enabled = 1; + snd_installed_cards[ptr].config.io_base = ioaddr; + snd_installed_cards[ptr].config.irq = irq; + snd_installed_cards[ptr].config.dma = dma; + } + } +} + +#else +void +sound_chconf (int card_type, int ioaddr, int irq, int dma) +{ + int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); + + int ptr, j; + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + if (snd_installed_cards[j].card_type == card_type && + !snd_installed_cards[j].enabled) /* + * Not already found + */ + ptr = j; + + if (ptr != -1) + { + snd_installed_cards[ptr].enabled = 1; + if (ioaddr) + snd_installed_cards[ptr].config.io_base = ioaddr; + if (irq) + snd_installed_cards[ptr].config.irq = irq; + if (dma) + snd_installed_cards[ptr].config.dma = dma; + } +} + +#endif + +struct address_info * +sound_getconf (int card_type) +{ + int j, ptr; + int n = sizeof (snd_installed_cards) / sizeof (struct card_info); + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + if (snd_installed_cards[j].card_type == card_type) + ptr = j; + + if (ptr == -1) + return (struct address_info *) NULL; + + return &snd_installed_cards[ptr].config; +} + +#else + +void +sound_setup (char *str, int *ints) +{ +} + +#endif diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h new file mode 100644 index 00000000000..12b20baf7d8 --- /dev/null +++ b/sys/i386/isa/sound/dev_table.h @@ -0,0 +1,345 @@ +/* + * dev_table.h + * + * Global definitions for device call tables + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + +*/ + +#ifndef _DEV_TABLE_H_ +#define _DEV_TABLE_H_ + +/* + * NOTE! NOTE! NOTE! NOTE! + * + * If you modify this file, please check the dev_table.c also. + * + * NOTE! NOTE! NOTE! NOTE! + */ + +struct driver_info { + int card_type; /* From soundcard.h */ + char *name; + long (*attach) (long mem_start, struct address_info *hw_config); + int (*probe) (struct address_info *hw_config); +}; + +struct card_info { + int card_type; /* Link (search key) to the driver list */ + struct address_info config; + int enabled; +}; + +/* + * Device specific parameters (used only by dmabuf.c) + */ +#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) + +#define DMODE_NONE 0 +#define DMODE_OUTPUT 1 +#define DMODE_INPUT 2 + +struct dma_buffparms { + int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ + + /* + * Pointers to raw buffers + */ + + char *raw_buf[DSP_BUFFCOUNT]; + unsigned long raw_buf_phys[DSP_BUFFCOUNT]; + int raw_count; + + /* + * Device state tables + */ + + unsigned long flags; +#define DMA_BUSY 0x00000001 +#define DMA_RESTART 0x00000002 +#define DMA_ACTIVE 0x00000004 +#define DMA_STARTED 0x00000008 +#define DMA_ALLOC_DONE 0x00000020 + + int open_mode; + + /* + * Queue parameters. + */ + int qlen; + int qhead; + int qtail; + + int nbufs; + int counts[MAX_SUB_BUFFERS]; + int subdivision; + char *buf[MAX_SUB_BUFFERS]; + unsigned long buf_phys[MAX_SUB_BUFFERS]; + + int fragment_size; + int max_fragments; + + int bytes_in_use; + + int underrun_count; +}; + +struct audio_operations { + char name[32]; + int flags; +#define NOTHING_SPECIAL 0 +#define NEEDS_RESTART 1 +#define DMA_AUTOMODE 2 + int format_mask; /* Bitmask for supported audio formats */ + void *devc; /* Driver specific info */ + int (*open) (int dev, int mode); + void (*close) (int dev); + void (*output_block) (int dev, unsigned long buf, + int count, int intrflag, int dma_restart); + void (*start_input) (int dev, unsigned long buf, + int count, int intrflag, int dma_restart); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local); + int (*prepare_for_input) (int dev, int bufsize, int nbufs); + int (*prepare_for_output) (int dev, int bufsize, int nbufs); + void (*reset) (int dev); + void (*halt_xfer) (int dev); + int (*local_qlen)(int dev); + void (*copy_from_user)(int dev, char *localbuf, int localoffs, + snd_rw_buf *userbuf, int useroffs, int len); + int buffcount; + long buffsize; + int dmachan; + struct dma_buffparms *dmap; +}; + +struct mixer_operations { + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); +}; + +struct synth_operations { + struct synth_info *info; + int midi_dev; + int synth_type; + int synth_subtype; + + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); + int (*kill_note) (int dev, int voice, int note, int velocity); + int (*start_note) (int dev, int voice, int note, int velocity); + int (*set_instr) (int dev, int voice, int instr); + void (*reset) (int dev); + void (*hw_control) (int dev, unsigned char *event); + int (*load_patch) (int dev, int format, snd_rw_buf *addr, + int offs, int count, int pmgr_flag); + void (*aftertouch) (int dev, int voice, int pressure); + void (*controller) (int dev, int voice, int ctrl_num, int value); + void (*panning) (int dev, int voice, int value); + void (*volume_method) (int dev, int mode); + int (*pmgr_interface) (int dev, struct patmgr_info *info); + void (*bender) (int dev, int chn, int value); + int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc); + + struct voice_alloc_info alloc; + struct channel_info chn_info[16]; +}; + +struct midi_operations { + struct midi_info info; + struct synth_operations *converter; + int (*open) (int dev, int mode, + void (*inputintr)(int dev, unsigned char data), + void (*outputintr)(int dev) + ); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); + int (*putc) (int dev, unsigned char data); + int (*start_read) (int dev); + int (*end_read) (int dev); + void (*kick)(int dev); + int (*command) (int dev, unsigned char *data); + int (*buffer_status) (int dev); + int (*prefix_cmd) (int dev, unsigned char status); +}; + +struct sound_timer_operations { + struct sound_timer_info info; + int priority; + int devlink; + int (*open)(int dev, int mode); + void (*close)(int dev); + int (*event)(int dev, unsigned char *ev); + unsigned long (*get_time)(int dev); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); + void (*arm_timer)(int dev, long time); +}; + +#ifdef _DEV_TABLE_C_ + struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0; + struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0; + struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0; + struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0; + +#ifndef EXCLUDE_SEQUENCER + extern struct sound_timer_operations default_sound_timer; + struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = + {&default_sound_timer, NULL}; + int num_sound_timers = 1; +#else + struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = + {NULL}; + int num_sound_timers = 0; +#endif + +/* + * List of low level drivers compiled into the kernel. + */ + + struct driver_info sound_drivers[] = { +#ifndef EXCLUDE_PSS + {SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss}, +#endif +#ifndef EXCLUDE_YM3812 + {SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib}, +#endif +#ifndef EXCLUDE_PAS + {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas}, +#endif +#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI) + {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401}, +#endif +#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI) + {SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850}, +#endif +#ifndef EXCLUDE_SB + {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb}, +#endif +#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) +#ifndef EXCLUDE_AUDIO + {SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect}, +#endif +#ifndef EXCLUDE_MIDI + {SNDCARD_SB16MIDI,"SB16 MIDI", attach_sb16midi, probe_sb16midi}, +#endif +#endif +#ifndef EXCLUDE_GUS16 + {SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16}, +#endif +#ifndef EXCLUDE_MSS + {SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound}, +#endif +#ifndef EXCLUDE_GUS + {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus}, +#endif + {0, "*?*", NULL, NULL} + }; + +/* + * List of devices actually configured in the system. + * + * Note! The detection order is significant. Don't change it. + */ + + struct card_info snd_installed_cards[] = { +#ifndef EXCLUDE_PSS + {SNDCARD_PSS, {PSS_BASE, PSS_IRQ, PSS_DMA}, SND_DEFAULT_ENABLE}, +#endif +#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI) + {SNDCARD_MPU401, {MPU_BASE, MPU_IRQ, 0}, SND_DEFAULT_ENABLE}, +#ifdef MPU2_BASE + {SNDCARD_MPU401, {MPU2_BASE, MPU2_IRQ, 0}, SND_DEFAULT_ENABLE}, +#endif +#ifdef MPU3_BASE + {SNDCARD_MPU401, {MPU3_BASE, MPU2_IRQ, 0}, SND_DEFAULT_ENABLE}, +#endif +#endif +#ifndef EXCLUDE_MSS + {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA}, SND_DEFAULT_ENABLE}, +# ifdef MSS2_BASE + {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA}, SND_DEFAULT_ENABLE}, +# endif +#endif + +#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI) + {SNDCARD_UART6850, {U6850_BASE, U6850_IRQ, 0}, SND_DEFAULT_ENABLE}, +#endif + +#ifndef EXCLUDE_PAS + {SNDCARD_PAS, {PAS_BASE, PAS_IRQ, PAS_DMA}, SND_DEFAULT_ENABLE}, +#endif + +#ifndef EXCLUDE_SB + {SNDCARD_SB, {SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE}, +#endif + +#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) +#ifndef EXCLUDE_AUDIO + {SNDCARD_SB16, {SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE}, +#endif +#ifndef EXCLUDE_MIDI + {SNDCARD_SB16MIDI,{SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE}, +#endif +#endif + +#ifndef EXCLUDE_GUS +#ifndef EXCLUDE_GUS16 + {SNDCARD_GUS16, {GUS16_BASE, GUS16_IRQ, GUS16_DMA}, SND_DEFAULT_ENABLE}, +#endif + {SNDCARD_GUS, {GUS_BASE, GUS_IRQ, GUS_DMA}, SND_DEFAULT_ENABLE}, +#endif + +#ifndef EXCLUDE_YM3812 + {SNDCARD_ADLIB, {FM_MONO, 0, 0}, SND_DEFAULT_ENABLE}, +#endif + {0, {0}, 0} + }; + + int num_sound_drivers = + sizeof(sound_drivers) / sizeof (struct driver_info); + int num_sound_cards = + sizeof(snd_installed_cards) / sizeof (struct card_info); + + +#else + extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; int num_audiodevs; + extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers; + extern struct synth_operations * synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_synths; + extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis; + extern struct sound_timer_operations * sound_timer_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_sound_timers; + + extern struct driver_info sound_drivers[]; + extern int num_sound_drivers; + extern struct card_info snd_installed_cards[]; + extern int num_sound_cards; + +long sndtable_init(long mem_start); +int sndtable_get_cardcount (void); +struct address_info *sound_getconf(int card_type); +void sound_chconf(int card_type, int ioaddr, int irq, int dma); +int snd_find_driver(int type); + +#endif /* _DEV_TABLE_C_ */ +#endif /* _DEV_TABLE_H_ */ diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c new file mode 100644 index 00000000000..0e275b6a3ae --- /dev/null +++ b/sys/i386/isa/sound/dmabuf.c @@ -0,0 +1,964 @@ +/* + * sound/dmabuf.c + * + * The DMA buffer manager for digitized voice applications + * + * Copyright by Hannu Savolainen 1993, 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "sound_calls.h" + +#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS) + +DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]); + +static struct dma_buffparms dmaps[MAX_AUDIO_DEV] = +{0}; /* + * Primitive way to allocate + * such a large array. + * Needs dynamic run-time alloction. + */ + +static void +reorganize_buffers (int dev) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ + + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + struct audio_operations *dsp_dev = audio_devs[dev]; + + unsigned i, p, n; + unsigned sr, nc, sz, bsz; + + if (dmap->fragment_size == 0) + { /* Compute the fragment size using the default algorithm */ + + sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1); + nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1); + sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1); + + if (sr < 1 || nc < 1 || sz < 1) + { + printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", + dev, sr, nc, sz); + sr = DSP_DEFAULT_SPEED; + nc = 1; + sz = 8; + } + + sz /= 8; /* #bits -> #bytes */ + + sz = sr * nc * sz; + + /* + * Compute a buffer size for time not exeeding 1 second. + * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds + * of sound (using the current speed, sample size and #channels). + */ + + bsz = dsp_dev->buffsize; + while (bsz > sz) + bsz /= 2; + + if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize) + bsz /= 2; /* Needs at least 2 buffers */ + + if (dmap->subdivision == 0) /* Not already set */ + dmap->subdivision = 1; /* Init to default value */ + + bsz /= dmap->subdivision; + + if (bsz < 64) + bsz = 4096; /* Just a sanity check */ + + while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS) + bsz *= 2; + + dmap->fragment_size = bsz; + } + else + { + /* + * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or + * the buffer sice computation has already been done. + */ + if (dmap->fragment_size > audio_devs[dev]->buffsize) + dmap->fragment_size = audio_devs[dev]->buffsize; + bsz = dmap->fragment_size; + } + + /* + * Now computing addresses for the logical buffers + */ + + n = 0; + for (i = 0; i < dmap->raw_count && + n < dmap->max_fragments && + n < MAX_SUB_BUFFERS; i++) + { + p = 0; + + while ((p + bsz) <= dsp_dev->buffsize && + n < dmap->max_fragments && + n < MAX_SUB_BUFFERS) + { + dmap->buf[n] = dmap->raw_buf[i] + p; + dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p; + p += bsz; + n++; + } + } + + dmap->nbufs = n; + dmap->bytes_in_use = n * bsz; + + for (i = 0; i < dmap->nbufs; i++) + { + dmap->counts[i] = 0; + } + + dmap->flags |= DMA_ALLOC_DONE; +} + +static void +dma_init_buffers (int dev) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev]; + + RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]); + + dmap->flags = DMA_BUSY; /* Other flags off */ + dmap->qlen = dmap->qhead = dmap->qtail = 0; + + dmap->qlen = dmap->qtail = dmap->qhead = 0; + dmap->dma_mode = DMODE_NONE; +} + +int +DMAbuf_open (int dev, int mode) +{ + int retval; + struct dma_buffparms *dmap = NULL; + + if (dev >= num_audiodevs) + { + printk ("PCM device %d not installed.\n", dev); + return RET_ERROR (ENXIO); + } + + if (!audio_devs[dev]) + { + printk ("PCM device %d not initialized\n", dev); + return RET_ERROR (ENXIO); + } + + dmap = audio_devs[dev]->dmap = &dmaps[dev]; + + if (dmap->flags & DMA_BUSY) + return RET_ERROR (EBUSY); + +#ifdef USE_RUNTIME_DMAMEM + dmap->raw_buf[0] = NULL; + sound_dma_malloc (dev); +#endif + + if (dmap->raw_buf[0] == NULL) + return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */ + + if ((retval = audio_devs[dev]->open (dev, mode)) < 0) + return retval; + + dmap->open_mode = mode; + dmap->subdivision = dmap->underrun_count = 0; + dmap->fragment_size = 0; + dmap->max_fragments = 65536; /* Just a large value */ + + dma_init_buffers (dev); + audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1); + audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1); + audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1); + + return 0; +} + +static void +dma_reset (int dev) +{ + int retval; + unsigned long flags; + + DISABLE_INTR (flags); + + audio_devs[dev]->reset (dev); + audio_devs[dev]->close (dev); + + if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0) + printk ("Sound: Reset failed - Can't reopen device\n"); + RESTORE_INTR (flags); + + dma_init_buffers (dev); + reorganize_buffers (dev); +} + +static int +dma_sync (int dev) +{ + unsigned long flags; + + if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT) + { + DISABLE_INTR (flags); + + while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) + && audio_devs[dev]->dmap->qlen) + { + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ); + if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) + { + RESTORE_INTR (flags); + return audio_devs[dev]->dmap->qlen; + } + } + RESTORE_INTR (flags); + + /* + * Some devices such as GUS have huge amount of on board RAM for the + * audio data. We have to wait util the device has finished playing. + */ + + DISABLE_INTR (flags); + if (audio_devs[dev]->local_qlen) /* Device has hidden buffers */ + { + while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) + && audio_devs[dev]->local_qlen (dev)) + { + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ); + } + } + RESTORE_INTR (flags); + } + return audio_devs[dev]->dmap->qlen; +} + +int +DMAbuf_release (int dev, int mode) +{ + unsigned long flags; + + if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) + && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)) + { + dma_sync (dev); + } + +#ifdef USE_RUNTIME_DMAMEM + sound_dma_free (dev); +#endif + + DISABLE_INTR (flags); + audio_devs[dev]->reset (dev); + + audio_devs[dev]->close (dev); + + audio_devs[dev]->dmap->dma_mode = DMODE_NONE; + audio_devs[dev]->dmap->flags &= ~DMA_BUSY; + RESTORE_INTR (flags); + + return 0; +} + +int +DMAbuf_getrdbuffer (int dev, char **buf, int *len) +{ + unsigned long flags; + int err = EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + DISABLE_INTR (flags); + if (!dmap->qlen) + { + if (dmap->flags & DMA_RESTART) + { + dma_reset (dev); + dmap->flags &= ~DMA_RESTART; + } + + if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ + { + dma_sync (dev); + dma_reset (dev); + dmap->dma_mode = DMODE_NONE; + } + + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers (dev); + + if (dmap->dma_mode) + { + int err; + + if ((err = audio_devs[dev]->prepare_for_input (dev, + dmap->fragment_size, dmap->nbufs)) < 0) + { + RESTORE_INTR (flags); + return err; + } + dmap->dma_mode = DMODE_INPUT; + } + + if (!(dmap->flags & DMA_ACTIVE)) + { + audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail], + dmap->fragment_size, 0, + !(audio_devs[dev]->flags & DMA_AUTOMODE) || + !(dmap->flags & DMA_STARTED)); + dmap->flags |= DMA_ACTIVE | DMA_STARTED; + } + + /* Wait for the next block */ + + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); + if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) + { + printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); + err = EIO; + SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); + } + else + err = EINTR; + } + RESTORE_INTR (flags); + + if (!dmap->qlen) + return RET_ERROR (err); + + *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]]; + *len = dmap->fragment_size - dmap->counts[dmap->qhead]; + + return dmap->qhead; +} + +int +DMAbuf_rmchars (int dev, int buff_no, int c) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + int p = dmap->counts[dmap->qhead] + c; + + if (p >= dmap->fragment_size) + { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + } + else + dmap->counts[dmap->qhead] = p; + + return 0; +} + +int +DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + switch (cmd) + { + case SNDCTL_DSP_RESET: + dma_reset (dev); + return 0; + break; + + case SNDCTL_DSP_SYNC: + dma_sync (dev); + dma_reset (dev); + return 0; + break; + + case SNDCTL_DSP_GETBLKSIZE: + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers (dev); + + return IOCTL_OUT (arg, dmap->fragment_size); + break; + + case SNDCTL_DSP_SUBDIVIDE: + { + int fact = IOCTL_IN (arg); + + if (fact == 0) + { + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return IOCTL_OUT (arg, fact); + } + + if (dmap->subdivision != 0 || + dmap->fragment_size)/* Loo late to change */ + return RET_ERROR (EINVAL); + + if (fact > MAX_REALTIME_FACTOR) + return RET_ERROR (EINVAL); + + if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) + return RET_ERROR (EINVAL); + + dmap->subdivision = fact; + return IOCTL_OUT (arg, fact); + } + break; + + case SNDCTL_DSP_SETFRAGMENT: + { + int fact = IOCTL_IN (arg); + int bytes, count; + + if (fact == 0) + return RET_ERROR (EIO); + + if (dmap->subdivision != 0 || + dmap->fragment_size)/* Loo late to change */ + return RET_ERROR (EINVAL); + + bytes = fact & 0xffff; + count = (fact >> 16) & 0xffff; + + if (count == 0) + count = MAX_SUB_BUFFERS; + + if (bytes < 7 || bytes > 17) /* <64 || > 128k */ + return RET_ERROR (EINVAL); + + if (count < 2) + return RET_ERROR (EINVAL); + + dmap->fragment_size = (1 << bytes); + dmap->max_fragments = count; + + if (dmap->fragment_size > audio_devs[dev]->buffsize) + dmap->fragment_size = audio_devs[dev]->buffsize; + + if (dmap->fragment_size == audio_devs[dev]->buffsize && + audio_devs[dev]->flags & DMA_AUTOMODE) + dmap->fragment_size /= 2; /* Needs at least 2 buffers */ + + dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ + return IOCTL_OUT (arg, bytes | (count << 16)); + } + break; + + default: + return audio_devs[dev]->ioctl (dev, cmd, arg, local); + } + + return RET_ERROR (EIO); +} + +static int +space_in_queue (int dev) +{ + int len, max, tmp; + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + if (dmap->qlen == dmap->nbufs)/* No space at all */ + return 0; + + /* + * Verify that there are no more pending buffers than the limit + * defined by the process. + */ + + max = dmap->max_fragments; + len = dmap->qlen; + + if (audio_devs[dev]->local_qlen) + { + tmp = audio_devs[dev]->local_qlen (dev); + if (tmp & len) + tmp--; /* + * This buffer has been counted twice + */ + len += tmp; + } + + if (len >= max) + return 0; + return 1; +} + +int +DMAbuf_getwrbuffer (int dev, char **buf, int *size) +{ + unsigned long flags; + int abort, err = EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ + { + dma_reset (dev); + dmap->dma_mode = DMODE_NONE; + } + else if (dmap->flags & DMA_RESTART) /* Restart buffering */ + { + dma_sync (dev); + dma_reset (dev); + } + + dmap->flags &= ~DMA_RESTART; + + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers (dev); + + if (!dmap->dma_mode) + { + int err; + + dmap->dma_mode = DMODE_OUTPUT; + if ((err = audio_devs[dev]->prepare_for_output (dev, + dmap->fragment_size, dmap->nbufs)) < 0) + return err; + } + + + DISABLE_INTR (flags); + + abort = 0; + while (!space_in_queue (dev) && + !abort) + { + /* + * Wait for free space + */ + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); + if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) + { + printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); + err = EIO; + abort = 1; + SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); + } + else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) + { + err = EINTR; + abort = 1; + } + } + RESTORE_INTR (flags); + + if (!space_in_queue (dev)) + { + return RET_ERROR (err); /* Caught a signal ? */ + } + + *buf = dmap->buf[dmap->qtail]; + *size = dmap->fragment_size; + dmap->counts[dmap->qtail] = 0; + + return dmap->qtail; +} + +int +DMAbuf_start_output (int dev, int buff_no, int l) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + if (buff_no != dmap->qtail) + printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail); + + dmap->qlen++; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + + dmap->counts[dmap->qtail] = l; + + if ((l != dmap->fragment_size) && + ((audio_devs[dev]->flags & DMA_AUTOMODE) && + audio_devs[dev]->flags & NEEDS_RESTART)) + dmap->flags |= DMA_RESTART; + else + dmap->flags &= ~DMA_RESTART; + + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + + if (!(dmap->flags & DMA_ACTIVE)) + { + dmap->flags |= DMA_ACTIVE; + audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead], + dmap->counts[dmap->qhead], 0, + !(audio_devs[dev]->flags & DMA_AUTOMODE) || + !(dmap->flags & DMA_STARTED)); + dmap->flags |= DMA_STARTED; + } + + return 0; +} + +int +DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + int chan = audio_devs[dev]->dmachan; + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + unsigned long flags; + + /* + * This function is not as portable as it should be. + */ + + /* + * The count must be one less than the actual size. This is handled by + * set_dma_addr() + */ + + if (audio_devs[dev]->flags & DMA_AUTOMODE) + { /* + * Auto restart mode. Transfer the whole * + * buffer + */ +#ifdef linux + DISABLE_INTR (flags); + disable_dma (chan); + clear_dma_ff (chan); + set_dma_mode (chan, dma_mode | DMA_AUTOINIT); + set_dma_addr (chan, dmap->raw_buf_phys[0]); + set_dma_count (chan, dmap->bytes_in_use); + enable_dma (chan); + RESTORE_INTR (flags); +#else + +#ifdef __386BSD__ + printk ("sound: Invalid DMA mode for device %d\n", dev); + + isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, + dmap->raw_buf_phys[0], + dmap->bytes_in_use, + chan); +#else +#if defined(GENERIC_SYSV) +#ifndef DMAMODE_AUTO + printk ("sound: Invalid DMA mode for device %d\n", dev); +#endif + dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) +#ifdef DMAMODE_AUTO + | DMAMODE_AUTO +#endif + , + dmap->raw_buf_phys[0], dmap->bytes_in_use); + dma_enable (chan); +#else +#error This routine is not valid for this OS. +#endif +#endif + +#endif + } + else + { +#ifdef linux + DISABLE_INTR (flags); + disable_dma (chan); + clear_dma_ff (chan); + set_dma_mode (chan, dma_mode); + set_dma_addr (chan, physaddr); + set_dma_count (chan, count); + enable_dma (chan); + RESTORE_INTR (flags); +#else +#ifdef __386BSD__ + isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, + physaddr, + count, + chan); +#else + +#if defined(GENERIC_SYSV) + dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode), + physaddr, count); + dma_enable (chan); +#else +#error This routine is not valid for this OS. +#endif /* GENERIC_SYSV */ +#endif + +#endif + } + + return count; +} + +long +DMAbuf_init (long mem_start) +{ + int dev; + + /* + * NOTE! This routine could be called several times. + */ + + for (dev = 0; dev < num_audiodevs; dev++) + audio_devs[dev]->dmap = &dmaps[dev]; + return mem_start; +} + +void +DMAbuf_outputintr (int dev, int event_type) +{ + /* + * Event types: + * 0 = DMA transfer done. Device still has more data in the local + * buffer. + * 1 = DMA transfer done. Device doesn't have local buffer or it's + * empty now. + * 2 = No DMA transfer but the device has now more space in it's local + * buffer. + */ + + unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + if (event_type != 2) + { + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + { + printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + return; + } + + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->flags &= ~DMA_ACTIVE; + + if (dmap->qlen) + { + audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead], + dmap->counts[dmap->qhead], 1, + !(audio_devs[dev]->flags & DMA_AUTOMODE)); + dmap->flags |= DMA_ACTIVE; + } + else if (event_type == 1) + { + dmap->underrun_count++; + audio_devs[dev]->halt_xfer (dev); + if ((audio_devs[dev]->flags & DMA_AUTOMODE) && + audio_devs[dev]->flags & NEEDS_RESTART) + dmap->flags |= DMA_RESTART; + else + dmap->flags &= ~DMA_RESTART; + } + } /* event_type != 2 */ + + DISABLE_INTR (flags); + if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) + { + WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); + } + RESTORE_INTR (flags); +} + +void +DMAbuf_inputintr (int dev) +{ + unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap; + + if (dmap->qlen == (dmap->nbufs - 1)) + { + printk ("Sound: Recording overrun\n"); + dmap->underrun_count++; + audio_devs[dev]->halt_xfer (dev); + dmap->flags &= ~DMA_ACTIVE; + if (audio_devs[dev]->flags & DMA_AUTOMODE) + dmap->flags |= DMA_RESTART; + else + dmap->flags &= ~DMA_RESTART; + } + else + { + dmap->qlen++; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + + audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail], + dmap->fragment_size, 1, + !(audio_devs[dev]->flags & DMA_AUTOMODE)); + dmap->flags |= DMA_ACTIVE; + } + + DISABLE_INTR (flags); + if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) + { + WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); + } + RESTORE_INTR (flags); +} + +int +DMAbuf_open_dma (int dev) +{ + unsigned long flags; + int chan = audio_devs[dev]->dmachan; + + if (ALLOC_DMA_CHN (chan)) + { + printk ("Unable to grab DMA%d for the audio driver\n", chan); + return RET_ERROR (EBUSY); + } + + DISABLE_INTR (flags); +#ifdef linux + disable_dma (chan); + clear_dma_ff (chan); +#endif + RESTORE_INTR (flags); + + return 0; +} + +void +DMAbuf_close_dma (int dev) +{ + int chan = audio_devs[dev]->dmachan; + + DMAbuf_reset_dma (chan); + RELEASE_DMA_CHN (chan); +} + +void +DMAbuf_reset_dma (int chan) +{ +} + +/* + * The sound_mem_init() is called by mem_init() immediately after mem_map is + * initialized and before free_page_list is created. + * + * This routine allocates DMA buffers at the end of available physical memory ( + * <16M) and marks pages reserved at mem_map. + */ + +#else +/* + * Stub versions if audio services not included + */ + +int +DMAbuf_open (int dev, int mode) +{ + return RET_ERROR (ENXIO); +} + +int +DMAbuf_release (int dev, int mode) +{ + return 0; +} + +int +DMAbuf_getwrbuffer (int dev, char **buf, int *size) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_getrdbuffer (int dev, char **buf, int *len) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_rmchars (int dev, int buff_no, int c) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_start_output (int dev, int buff_no, int l) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + return RET_ERROR (EIO); +} + +long +DMAbuf_init (long mem_start) +{ + return mem_start; +} + +int +DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_open_dma (int chan) +{ + return RET_ERROR (ENXIO); +} + +void +DMAbuf_close_dma (int chan) +{ + return; +} + +void +DMAbuf_reset_dma (int chan) +{ + return; +} + +void +DMAbuf_inputintr (int dev) +{ + return; +} + +void +DMAbuf_outputintr (int dev, int underrun_flag) +{ + return; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/finetune.h b/sys/i386/isa/sound/finetune.h new file mode 100644 index 00000000000..b86a0eb15a1 --- /dev/null +++ b/sys/i386/isa/sound/finetune.h @@ -0,0 +1,49 @@ +#ifdef SEQUENCER_C +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + + unsigned short finetune_table[128] = + { +/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499, +/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567, +/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637, +/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707, +/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777, +/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848, +/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919, +/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991, +/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063, +/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136, +/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210, +/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284, +/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358, +/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433, +/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509, +/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585 + }; +#else + extern unsigned short finetune_table[128]; +#endif diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c new file mode 100644 index 00000000000..614532c7d11 --- /dev/null +++ b/sys/i386/isa/sound/gus_card.c @@ -0,0 +1,193 @@ +/* + * sound/gus_card.c + * + * Detection routine for the Gravis Ultrasound. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) + +#include "gus_hw.h" + +void gusintr (int); + +int gus_base, gus_irq, gus_dma; +extern int gus_wave_volume; +extern int gus_pcm_volume; +extern int have_gus_max; + +long +attach_gus_card (long mem_start, struct address_info *hw_config) +{ + int io_addr; + + snd_set_irq_handler (hw_config->irq, gusintr); + + if (gus_wave_detect (hw_config->io_base)) /* + * Try first the default + */ + { + mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma); +#ifndef EXCLUDE_MIDI + mem_start = gus_midi_init (mem_start); +#endif +#ifndef EXCLUDE_SEQUENCER + sound_timer_init (hw_config->io_base + 8); +#endif + return mem_start; + } + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* + * Already tested + */ + if (gus_wave_detect (io_addr)) + { + printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base); + mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma); +#ifndef EXCLUDE_MIDI + mem_start = gus_midi_init (mem_start); +#endif +#ifndef EXCLUDE_SEQUENCER + sound_timer_init (io_addr + 8); +#endif + return mem_start; + } + +#endif + + return mem_start; /* + * Not detected + */ +} + +int +probe_gus (struct address_info *hw_config) +{ + int io_addr; + + if (gus_wave_detect (hw_config->io_base)) + return 1; + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* + * Already tested + */ + if (gus_wave_detect (io_addr)) + return 1; + +#endif + + return 0; +} + +void +gusintr (int irq) +{ + unsigned char src; + +#ifdef linux + sti (); +#endif + +#ifndef EXCLUDE_GUSMAX + if (have_gus_max) + ad1848_interrupt (irq); +#endif + + while (1) + { + if (!(src = INB (u_IrqStatus))) + return; + + if (src & DMA_TC_IRQ) + { + guswave_dma_irq (); + } + + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + { +#ifndef EXCLUDE_MIDI + gus_midi_interrupt (0); +#endif + } + + if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) + { +#ifndef EXCLUDE_SEQUENCER + sound_timer_interrupt (); +#else + gus_write8 (0x45, 0); /* Stop timers */ +#endif + } + + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + { + gus_voice_irq (); + } + } +} + +#endif + +/* + * Some extra code for the 16 bit sampling option + */ +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS16) + +int +probe_gus_db16 (struct address_info *hw_config) +{ + return ad1848_detect (hw_config->io_base); +} + +long +attach_gus_db16 (long mem_start, struct address_info *hw_config) +{ + gus_pcm_volume = 100; + gus_wave_volume = 90; + + ad1848_init ("GUS 16 bit sampling", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma); + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h new file mode 100644 index 00000000000..f97a0b8670e --- /dev/null +++ b/sys/i386/isa/sound/gus_hw.h @@ -0,0 +1,50 @@ + +/* + * I/O addresses + */ + +#define u_Base (gus_base + 0x000) +#define u_Mixer u_Base +#define u_Status (gus_base + 0x006) +#define u_TimerControl (gus_base + 0x008) +#define u_TimerData (gus_base + 0x009) +#define u_IRQDMAControl (gus_base + 0x00b) +#define u_MidiControl (gus_base + 0x100) +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 +#define u_MidiStatus u_MidiControl +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 +#define u_MidiData (gus_base + 0x101) +#define u_Voice (gus_base + 0x102) +#define u_Command (gus_base + 0x103) +#define u_DataLo (gus_base + 0x104) +#define u_DataHi (gus_base + 0x105) +#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ +#define u_MixSelect (gus_base + 0x506) /* registers. */ +#define u_IrqStatus u_Status +# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + +#define ICS2101 1 +# define ICS_MIXDEVS 6 +# define DEV_MIC 0 +# define DEV_LINE 1 +# define DEV_CD 2 +# define DEV_GF1 3 +# define DEV_UNUSED 4 +# define DEV_VOL 5 + +# define CHN_LEFT 0 +# define CHN_RIGHT 1 +#define CS4231 2 +#define u_DRAMIO (gus_base + 0x107) diff --git a/sys/i386/isa/sound/gus_linearvol.h b/sys/i386/isa/sound/gus_linearvol.h new file mode 100644 index 00000000000..7ad0c30d4fd --- /dev/null +++ b/sys/i386/isa/sound/gus_linearvol.h @@ -0,0 +1,18 @@ +static unsigned short gus_linearvol[128] = { + 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, + 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, + 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, + 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, + 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, + 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, + 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, + 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, + 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, + 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, + 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, + 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc +}; diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c new file mode 100644 index 00000000000..87aea6251da --- /dev/null +++ b/sys/i386/isa/sound/gus_midi.c @@ -0,0 +1,309 @@ +/* + * sound/gus2_midi.c + * + * The low level driver for the GUS Midi Interface. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "gus_hw.h" + +#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI) + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static int output_used = 0; +static volatile unsigned char gus_midi_control; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; +extern int gus_base, gus_irq, gus_dma; + +#define GUS_MIDI_STATUS() INB(u_MidiStatus) + +static int +gus_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + + if (midi_busy) + { + printk ("GUS: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + OUTB (MIDI_RESET, u_MidiControl); + gus_delay (); + + gus_midi_control = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + { + gus_midi_control |= MIDI_ENABLE_XMIT; + } + + OUTB (gus_midi_control, u_MidiControl); /* + * Enable + */ + + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + midi_input_intr = input; + + return 0; +} + +static int +dump_to_midi (unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; + + output_used = 1; + + DISABLE_INTR (flags); + + if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY) + { + ok = 1; + OUTB (midi_byte, u_MidiData); + } + else + { + /* + * Enable Midi xmit interrupts (again) + */ + gus_midi_control |= MIDI_ENABLE_XMIT; + OUTB (gus_midi_control, u_MidiControl); + } + + RESTORE_INTR (flags); + return ok; +} + +static void +gus_midi_close (int dev) +{ + /* + * Reset FIFO pointers, disable intrs + */ + + OUTB (MIDI_RESET, u_MidiControl); + midi_busy = 0; +} + +static int +gus_midi_out (int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + DISABLE_INTR (flags); + + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi (midi_byte)) + return 1; /* + * OK + */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* + * Local queue full + */ + + DISABLE_INTR (flags); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + RESTORE_INTR (flags); + + return 1; +} + +static int +gus_midi_start_read (int dev) +{ + return 0; +} + +static int +gus_midi_end_read (int dev) +{ + return 0; +} + +static int +gus_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +gus_midi_kick (int dev) +{ +} + +static int +gus_midi_buffer_status (int dev) +{ + unsigned long flags; + + if (!output_used) + return 0; + + DISABLE_INTR (flags); + + if (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + + return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY); +} + +#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations gus_midi_operations = +{ + {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, + &std_midi_synth, + gus_midi_open, + gus_midi_close, + gus_midi_ioctl, + gus_midi_out, + gus_midi_start_read, + gus_midi_end_read, + gus_midi_kick, + NULL, /* + * command + */ + gus_midi_buffer_status, + NULL +}; + +long +gus_midi_init (long mem_start) +{ + if (num_midis >= MAX_MIDI_DEV) + { + printk ("Sound: Too many midi devices detected\n"); + return mem_start; + } + + OUTB (MIDI_RESET, u_MidiControl); + + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &gus_midi_operations; + return mem_start; +} + +void +gus_midi_interrupt (int dummy) +{ + unsigned char stat, data; + unsigned long flags; + + DISABLE_INTR (flags); + + stat = GUS_MIDI_STATUS (); + + if (stat & MIDI_RCV_FULL) + { + data = INB (u_MidiData); + if (input_opened) + midi_input_intr (my_dev, data); + } + + if (stat & MIDI_XMIT_EMPTY) + { + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + if (!qlen) + { + /* + * Disable Midi output interrupts, since no data in the buffer + */ + gus_midi_control &= ~MIDI_ENABLE_XMIT; + OUTB (gus_midi_control, u_MidiControl); + } + } + + if (stat & MIDI_FRAME_ERR) + printk ("Midi framing error\n"); + if (stat & MIDI_OVERRUN && input_opened) + printk ("GUS: Midi input overrun\n"); + + RESTORE_INTR (flags); +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c new file mode 100644 index 00000000000..055a1170e9f --- /dev/null +++ b/sys/i386/isa/sound/gus_vol.c @@ -0,0 +1,147 @@ +/* + * gus_vol.c - Compute volume for GUS. + * + * Greg Lee 1993. + */ +#include "sound_config.h" +#ifndef EXCLUDE_GUS +#include "gus_linearvol.h" + +#define GUS_VOLUME gus_wave_volume + + +extern int gus_wave_volume; + +/* + * Calculate gus volume from note velocity, main volume, expression, and + * intrinsic patch volume given in patch library. Expression is multiplied + * in, so it emphasizes differences in note velocity, while main volume is + * added in -- I don't know whether this is right, but it seems reasonable to + * me. (In the previous stage, main volume controller messages were changed + * to expression controller messages, if they were found to be used for + * dynamic volume adjustments, so here, main volume can be assumed to be + * constant throughout a song.) + * + * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so + * we can give a big boost to very weak voices like nylon guitar and the + * basses. The normal value is 64. Strings are assigned lower values. + */ +unsigned short +gus_adagio_vol (int vel, int mainv, int xpn, int voicev) +{ + int i, m, n, x; + + + /* + * A voice volume of 64 is considered neutral, so adjust the main volume if + * something other than this neutral value was assigned in the patch + * library. + */ + x = 256 + 6 * (voicev - 64); + + /* + * Boost expression by voice volume above neutral. + */ + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; + + /* + * Combine multiplicative and level components. + */ + x = vel * xpn * 6 + (voicev / 4) * x; + +#ifdef GUS_VOLUME + /* + * Further adjustment by installation-specific master volume control + * (default 60). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + /* + * Experimental support for the channel main volume + */ + + mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ + x = (x * mainv * mainv) / 16384; +#endif + + if (x < 2) + return (0); + else if (x >= 65535) + return ((15 << 8) | 255); + + /* + * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit + * mantissa m. + */ + n = x; + i = 7; + if (n < 128) + { + while (i > 0 && n < (1 << i)) + i--; + } + else + while (n > 255) + { + n >>= 1; + i++; + } + /* + * Mantissa is part of linear volume not expressed in exponent. (This is + * not quite like real logs -- I wonder if it's right.) + */ + m = x - (1 << i); + + /* + * Adjust mantissa to 8 bits. + */ + if (m > 0) + { + if (i > 8) + m >>= i - 8; + else if (i < 8) + m <<= 8 - i; + } + + return ((i << 8) + m); +} + +/* + * Volume-values are interpreted as linear values. Volume is based on the + * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) + * and the volume set by the mixer-device (default 60%). + */ + +unsigned short +gus_linear_vol (int vol, int mainvol) +{ + int mixer_mainvol; + + if (vol <= 0) + vol = 0; + else if (vol >= 127) + vol = 127; + +#ifdef GUS_VOLUME + mixer_mainvol = GUS_VOLUME; +#else + mixer_mainvol = 100; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + if (mainvol <= 0) + mainvol = 0; + else if (mainvol >= 127) + mainvol = 127; +#else + mainvol = 128; +#endif + + return gus_linearvol[(((vol * mainvol) / 128) * mixer_mainvol) / 100]; +} + +#endif diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c new file mode 100644 index 00000000000..5f8e39c8ae5 --- /dev/null +++ b/sys/i386/isa/sound/gus_wave.c @@ -0,0 +1,3246 @@ +/* + * sound/gus_wave.c + * + * Driver for the Gravis UltraSound wave table synth. + * + * Copyright by Hannu Savolainen 1993, 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" +#include "ultrasound.h" +#include "gus_hw.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) + +#define MAX_SAMPLE 128 +#define MAX_PATCH 256 + +struct voice_info + { + unsigned long orig_freq; + unsigned long current_freq; + unsigned long mode; + int bender; + int bender_range; + int panning; + int midi_volume; + unsigned int initial_volume; + unsigned int current_volume; + int loop_irq_mode, loop_irq_parm; +#define LMODE_FINISH 1 +#define LMODE_PCM 2 +#define LMODE_PCM_STOP 3 + int volume_irq_mode, volume_irq_parm; +#define VMODE_HALT 1 +#define VMODE_ENVELOPE 2 +#define VMODE_START_NOTE 3 + + int env_phase; + unsigned char env_rate[6]; + unsigned char env_offset[6]; + + /* + * Volume computation parameters for gus_adagio_vol() + */ + int main_vol, expression_vol, patch_vol; + + /* Variables for "Ultraclick" removal */ + int dev_pending, note_pending, volume_pending, sample_pending; + char kill_pending; + long offset_pending; + + }; + +static struct voice_alloc_info *voice_alloc; + +extern int gus_base; +extern int gus_irq, gus_dma; +static long gus_mem_size = 0; +static long free_mem_ptr = 0; +static int gus_busy = 0; +static int nr_voices = 0; +static int gus_devnum = 0; +static int volume_base, volume_scale, volume_method; +static int gus_recmask = SOUND_MASK_MIC; +static int recording_active = 0; + +int gus_wave_volume = 60; +int gus_pcm_volume = 80; +int have_gus_max = 0; +static int gus_line_vol = 100, gus_mic_vol = 0; +static unsigned char mix_image = 0x00; + +/* + * Current version of this driver doesn't allow synth and PCM functions + * at the same time. The active_device specifies the active driver + */ +static int active_device = 0; + +#define GUS_DEV_WAVE 1 /* Wave table synth */ +#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ + +static int gus_sampling_speed; +static int gus_sampling_channels; +static int gus_sampling_bits; + +DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); + +/* + * Variables and buffers for PCM output + */ +#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */ + +static int pcm_bsize, pcm_nblk, pcm_banksize; +static int pcm_datasize[MAX_PCM_BUFFERS]; +static volatile int pcm_head, pcm_tail, pcm_qlen; +static volatile int pcm_active; +static volatile int dma_active; +static int pcm_opened = 0; +static int pcm_current_dev; +static int pcm_current_block; +static unsigned long pcm_current_buf; +static int pcm_current_count; +static int pcm_current_intrflag; + +struct voice_info voices[32]; + +static int freq_div_table[] = +{ + 44100, /* 14 */ + 41160, /* 15 */ + 38587, /* 16 */ + 36317, /* 17 */ + 34300, /* 18 */ + 32494, /* 19 */ + 30870, /* 20 */ + 29400, /* 21 */ + 28063, /* 22 */ + 26843, /* 23 */ + 25725, /* 24 */ + 24696, /* 25 */ + 23746, /* 26 */ + 22866, /* 27 */ + 22050, /* 28 */ + 21289, /* 29 */ + 20580, /* 30 */ + 19916, /* 31 */ + 19293 /* 32 */ +}; + +static struct patch_info *samples; +static long sample_ptrs[MAX_SAMPLE + 1]; +static int sample_map[32]; +static int free_sample; + + +static int patch_table[MAX_PATCH]; +static int patch_map[32]; + +static struct synth_info gus_info = +{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; + +static void gus_poke (long addr, unsigned char data); +static void compute_and_set_volume (int voice, int volume, int ramp_time); +extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); +extern unsigned short gus_linear_vol (int vol, int mainvol); +static void compute_volume (int voice, int volume); +static void do_volume_irq (int voice); +static void set_input_volumes (void); + +#define INSTANT_RAMP -1 /* Instant change. No ramping */ +#define FAST_RAMP 0 /* Fastest possible ramp */ + +static void +reset_sample_memory (void) +{ + int i; + + for (i = 0; i <= MAX_SAMPLE; i++) + sample_ptrs[i] = -1; + for (i = 0; i < 32; i++) + sample_map[i] = -1; + for (i = 0; i < 32; i++) + patch_map[i] = -1; + + gus_poke (0, 0); /* Put a silent sample to the beginning */ + gus_poke (1, 0); + free_mem_ptr = 2; + + free_sample = 0; + + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = -1; +} + +void +gus_delay (void) +{ + int i; + + for (i = 0; i < 7; i++) + INB (u_DRAMIO); +} + +static void +gus_poke (long addr, unsigned char data) +{ /* Writes a byte to the DRAM */ + unsigned long flags; + + DISABLE_INTR (flags); + OUTB (0x43, u_Command); + OUTB (addr & 0xff, u_DataLo); + OUTB ((addr >> 8) & 0xff, u_DataHi); + + OUTB (0x44, u_Command); + OUTB ((addr >> 16) & 0xff, u_DataHi); + OUTB (data, u_DRAMIO); + RESTORE_INTR (flags); +} + +static unsigned char +gus_peek (long addr) +{ /* Reads a byte from the DRAM */ + unsigned long flags; + unsigned char tmp; + + DISABLE_INTR (flags); + OUTB (0x43, u_Command); + OUTB (addr & 0xff, u_DataLo); + OUTB ((addr >> 8) & 0xff, u_DataHi); + + OUTB (0x44, u_Command); + OUTB ((addr >> 16) & 0xff, u_DataHi); + tmp = INB (u_DRAMIO); + RESTORE_INTR (flags); + + return tmp; +} + +void +gus_write8 (int reg, unsigned int data) +{ /* Writes to an indirect register (8 bit) */ + unsigned long flags; + + DISABLE_INTR (flags); + + OUTB (reg, u_Command); + OUTB ((unsigned char) (data & 0xff), u_DataHi); + + RESTORE_INTR (flags); +} + +unsigned char +gus_read8 (int reg) +{ /* Reads from an indirect register (8 bit). Offset 0x80. */ + unsigned long flags; + unsigned char val; + + DISABLE_INTR (flags); + OUTB (reg | 0x80, u_Command); + val = INB (u_DataHi); + RESTORE_INTR (flags); + + return val; +} + +unsigned char +gus_look8 (int reg) +{ /* Reads from an indirect register (8 bit). No additional offset. */ + unsigned long flags; + unsigned char val; + + DISABLE_INTR (flags); + OUTB (reg, u_Command); + val = INB (u_DataHi); + RESTORE_INTR (flags); + + return val; +} + +void +gus_write16 (int reg, unsigned int data) +{ /* Writes to an indirect register (16 bit) */ + unsigned long flags; + + DISABLE_INTR (flags); + + OUTB (reg, u_Command); + + OUTB ((unsigned char) (data & 0xff), u_DataLo); + OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi); + + RESTORE_INTR (flags); +} + +unsigned short +gus_read16 (int reg) +{ /* Reads from an indirect register (16 bit). Offset 0x80. */ + unsigned long flags; + unsigned char hi, lo; + + DISABLE_INTR (flags); + + OUTB (reg | 0x80, u_Command); + + lo = INB (u_DataLo); + hi = INB (u_DataHi); + + RESTORE_INTR (flags); + + return ((hi << 8) & 0xff00) | lo; +} + +void +gus_write_addr (int reg, unsigned long address, int is16bit) +{ /* Writes an 24 bit memory address */ + unsigned long hold_address; + unsigned long flags; + + DISABLE_INTR (flags); + if (is16bit) + { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + + gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); + /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */ + gus_delay (); + gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); + RESTORE_INTR (flags); +} + +static void +gus_select_voice (int voice) +{ + if (voice < 0 || voice > 31) + return; + + OUTB (voice, u_Voice); +} + +static void +gus_select_max_voices (int nvoices) +{ + if (nvoices < 14) + nvoices = 14; + if (nvoices > 32) + nvoices = 32; + + voice_alloc->max_voice = nr_voices = nvoices; + + gus_write8 (0x0e, (nvoices - 1) | 0xc0); +} + +static void +gus_voice_on (unsigned int mode) +{ + gus_write8 (0x00, (unsigned char) (mode & 0xfc)); + gus_delay (); + gus_write8 (0x00, (unsigned char) (mode & 0xfc)); +} + +static void +gus_voice_off (void) +{ + gus_write8 (0x00, gus_read8 (0x00) | 0x03); +} + +static void +gus_voice_mode (unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | + (mode & 0xfc)); /* Don't touch last two bits */ + gus_delay (); + gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); +} + +static void +gus_voice_freq (unsigned long freq) +{ + unsigned long divisor = freq_div_table[nr_voices - 14]; + unsigned short fc; + + fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; + + gus_write16 (0x01, fc); +} + +static void +gus_voice_volume (unsigned int vol) +{ + gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16 (0x09, (unsigned short) (vol << 4)); +} + +static void +gus_voice_balance (unsigned int balance) +{ + gus_write8 (0x0c, (unsigned char) (balance & 0xff)); +} + +static void +gus_ramp_range (unsigned int low, unsigned int high) +{ + gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff)); + gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff)); +} + +static void +gus_ramp_rate (unsigned int scale, unsigned int rate) +{ + gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); +} + +static void +gus_rampon (unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8 (0x0d, mode & 0xfc); + gus_delay (); + gus_write8 (0x0d, mode & 0xfc); +} + +static void +gus_ramp_mode (unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | + (mode & 0xfc)); /* Leave the last 2 bits alone */ + gus_delay (); + gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); +} + +static void +gus_rampoff (void) +{ + gus_write8 (0x0d, 0x03); +} + +static void +gus_set_voice_pos (int voice, long position) +{ + int sample_no; + + if ((sample_no = sample_map[voice]) != -1) + if (position < samples[sample_no].len) + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].offset_pending = position; + else + gus_write_addr (0x0a, sample_ptrs[sample_no] + position, + samples[sample_no].mode & WAVE_16_BITS); +} + +static void +gus_voice_init (int voice) +{ + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_volume (0); + gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */ + gus_write8 (0x00, 0x03); /* Voice off */ + gus_write8 (0x0d, 0x03); /* Ramping off */ + voice_alloc->map[voice] = 0; + RESTORE_INTR (flags); + +} + +static void +gus_voice_init2 (int voice) +{ + voices[voice].panning = 0; + voices[voice].mode = 0; + voices[voice].orig_freq = 20000; + voices[voice].current_freq = 20000; + voices[voice].bender = 0; + voices[voice].bender_range = 200; + voices[voice].initial_volume = 0; + voices[voice].current_volume = 0; + voices[voice].loop_irq_mode = 0; + voices[voice].loop_irq_parm = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].volume_irq_parm = 0; + voices[voice].env_phase = 0; + voices[voice].main_vol = 127; + voices[voice].patch_vol = 127; + voices[voice].expression_vol = 127; + voices[voice].sample_pending = -1; +} + +static void +step_envelope (int voice) +{ + unsigned vol, prev_vol, phase; + unsigned char rate; + long int flags; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) + { + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_rampoff (); + RESTORE_INTR (flags); + return; + /* + * Sustain phase begins. Continue envelope after receiving note off. + */ + } + + if (voices[voice].env_phase >= 5) + { /* Envelope finished. Shoot the voice down */ + gus_voice_init (voice); + return; + } + + prev_vol = voices[voice].current_volume; + phase = ++voices[voice].env_phase; + compute_volume (voice, voices[voice].midi_volume); + vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; + rate = voices[voice].env_rate[phase]; + + DISABLE_INTR (flags); + gus_select_voice (voice); + + gus_voice_volume (prev_vol); + + + gus_write8 (0x06, rate); /* Ramping rate */ + + voices[voice].volume_irq_mode = VMODE_ENVELOPE; + + if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + { + RESTORE_INTR (flags); + step_envelope (voice); /* Continue the envelope on the next step */ + return; + } + + if (vol > prev_vol) + { + if (vol >= (4096 - 64)) + vol = 4096 - 65; + gus_ramp_range (0, vol); + gus_rampon (0x20); /* Increasing volume, with IRQ */ + } + else + { + if (vol <= 64) + vol = 65; + gus_ramp_range (vol, 4030); + gus_rampon (0x60); /* Decreasing volume, with IRQ */ + } + voices[voice].current_volume = vol; + RESTORE_INTR (flags); +} + +static void +init_envelope (int voice) +{ + voices[voice].env_phase = -1; + voices[voice].current_volume = 64; + + step_envelope (voice); +} + +static void +start_release (int voice, long int flags) +{ + if (gus_read8 (0x00) & 0x03) + return; /* Voice already stopped */ + + voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ + + voices[voice].current_volume = + voices[voice].initial_volume = + gus_read16 (0x09) >> 4; /* Get current volume */ + + voices[voice].mode &= ~WAVE_SUSTAIN_ON; + gus_rampoff (); + RESTORE_INTR (flags); + step_envelope (voice); +} + +static void +gus_voice_fade (int voice) +{ + int instr_no = sample_map[voice], is16bits; + long int flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + + if (instr_no < 0 || instr_no > MAX_SAMPLE) + { + gus_write8 (0x00, 0x03); /* Hard stop */ + voice_alloc->map[voice] = 0; + RESTORE_INTR (flags); + return; + } + + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + start_release (voice, flags); + return; + } + + /* + * Ramp the volume down but not too quickly. + */ + if ((int) (gus_read16 (0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off (); + gus_rampoff (); + gus_voice_init (voice); + return; + } + + gus_ramp_range (65, 4030); + gus_ramp_rate (2, 4); + gus_rampon (0x40 | 0x20); /* Down, once, with IRQ */ + voices[voice].volume_irq_mode = VMODE_HALT; + RESTORE_INTR (flags); +} + +static void +gus_reset (void) +{ + int i; + + gus_select_max_voices (24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; + + for (i = 0; i < 32; i++) + { + gus_voice_init (i); /* Turn voice off */ + gus_voice_init2 (i); + } + + INB (u_Status); /* Touch the status register */ + + gus_look8 (0x41); /* Clear any pending DMA IRQs */ + gus_look8 (0x49); /* Clear any pending sample IRQs */ + + gus_read8 (0x0f); /* Clear pending IRQs */ + +} + +static void +gus_initialize (void) +{ + unsigned long flags; + unsigned char dma_image, irq_image, tmp; + + static unsigned char gus_irq_map[16] = + {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7}; + + static unsigned char gus_dma_map[8] = + {0, 1, 0, 2, 0, 3, 4, 5}; + + DISABLE_INTR (flags); + gus_write8 (0x4c, 0); /* Reset GF1 */ + gus_delay (); + gus_delay (); + + gus_write8 (0x4c, 1); /* Release Reset */ + gus_delay (); + gus_delay (); + + /* + * Clear all interrupts + */ + + gus_write8 (0x41, 0); /* DMA control */ + gus_write8 (0x45, 0); /* Timer control */ + gus_write8 (0x49, 0); /* Sample control */ + + gus_select_max_voices (24); + + INB (u_Status); /* Touch the status register */ + + gus_look8 (0x41); /* Clear any pending DMA IRQs */ + gus_look8 (0x49); /* Clear any pending sample IRQs */ + gus_read8 (0x0f); /* Clear pending IRQs */ + + gus_reset (); /* Resets all voices */ + + gus_look8 (0x41); /* Clear any pending DMA IRQs */ + gus_look8 (0x49); /* Clear any pending sample IRQs */ + gus_read8 (0x0f); /* Clear pending IRQs */ + + gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + + /* + * Set up for Digital ASIC + */ + + OUTB (0x05, gus_base + 0x0f); + + mix_image |= 0x02; /* Disable line out */ + OUTB (mix_image, u_Mixer); + + OUTB (0x00, u_IRQDMAControl); + + OUTB (0x00, gus_base + 0x0f); + + /* + * Now set up the DMA and IRQ interface + * + * The GUS supports two IRQs and two DMAs. + * + * Just one DMA channel is used. This prevents simultaneous ADC and DAC. + * Adding this support requires significant changes to the dmabuf.c, dsp.c + * and audio.c also. + */ + + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!tmp) + printk ("Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + + dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printk ("Warning! GUS DMA not selected\n"); + dma_image |= tmp; + + /* + * For some reason the IRQ and DMA addresses must be written twice + */ + + /* + * Doing it first time + */ + + OUTB (mix_image, u_Mixer); /* Select DMA control */ + OUTB (dma_image | 0x80, u_IRQDMAControl); /* Set DMA address */ + + OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ + OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ + + /* + * Doing it second time + */ + + OUTB (mix_image, u_Mixer); /* Select DMA control */ + OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */ + + OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ + OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ + + gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + OUTB (mix_image, u_Mixer); /* + * Turn mixer channels on + * Note! Mic in is left off. + */ + + gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + + gusintr (0); /* Serve pending interrupts */ + RESTORE_INTR (flags); +} + +int +gus_wave_detect (int baseaddr) +{ + unsigned long i; + unsigned long loc; + + gus_base = baseaddr; + + gus_write8 (0x4c, 0); /* Reset GF1 */ + gus_delay (); + gus_delay (); + + gus_write8 (0x4c, 1); /* Release Reset */ + gus_delay (); + gus_delay (); + + /* See if there is first block there.... */ + gus_poke (0L, 0xaa); + if (gus_peek (0L) != 0xaa) + return (0); + + /* Now zero it out so that I can check for mirroring .. */ + gus_poke (0L, 0x00); + for (i = 1L; i < 1024L; i++) + { + int n, failed; + + /* check for mirroring ... */ + if (gus_peek (0L) != 0) + break; + loc = i << 10; + + for (n = loc - 1, failed = 0; n <= loc; n++) + { + gus_poke (loc, 0xaa); + if (gus_peek (loc) != 0xaa) + failed = 1; + + gus_poke (loc, 0x55); + if (gus_peek (loc) != 0x55) + failed = 1; + } + + if (failed) + break; + } + gus_mem_size = i << 10; + return 1; +} + +static int +guswave_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory (); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return gus_mem_size - free_mem_ptr - 32; + + default: + return RET_ERROR (EINVAL); + } +} + +static int +guswave_set_instr (int dev, int voice, int instr_no) +{ + int sample_no; + + if (instr_no < 0 || instr_no > MAX_PATCH) + return RET_ERROR (EINVAL); + + if (voice < 0 || voice > 31) + return RET_ERROR (EINVAL); + + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].sample_pending = instr_no; + return 0; + } + + sample_no = patch_table[instr_no]; + patch_map[voice] = -1; + + if (sample_no < 0) + { + printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); + return RET_ERROR (EINVAL);/* Patch not defined */ + } + + if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + { + printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", + sample_no, instr_no, voice); + return RET_ERROR (EINVAL); + } + + sample_map[voice] = sample_no; + patch_map[voice] = instr_no; + return 0; +} + +static int +guswave_kill_note (int dev, int voice, int note, int velocity) +{ + unsigned long flags; + + DISABLE_INTR (flags); + voice_alloc->map[voice] = 0xffff; + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].kill_pending = 1; + RESTORE_INTR (flags); + } + else + { + RESTORE_INTR (flags); + gus_voice_fade (voice); + } + + return 0; +} + +static void +guswave_aftertouch (int dev, int voice, int pressure) +{ + short lo_limit, hi_limit; + unsigned long flags; + + return; /* Procedure currently disabled */ + + if (voice < 0 || voice > 31) + return; + + if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2) + return; /* Don't mix with envelopes */ + + if (pressure < 32) + { + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_rampoff (); + compute_and_set_volume (voice, 255, 0); /* Back to original volume */ + RESTORE_INTR (flags); + return; + } + + hi_limit = voices[voice].current_volume; + lo_limit = hi_limit * 99 / 100; + if (lo_limit < 65) + lo_limit = 65; + + DISABLE_INTR (flags); + gus_select_voice (voice); + if (hi_limit > (4095 - 65)) + { + hi_limit = 4095 - 65; + gus_voice_volume (hi_limit); + } + gus_ramp_range (lo_limit, hi_limit); + gus_ramp_rate (3, 8); + gus_rampon (0x58); /* Bidirectional, dow, loop */ + RESTORE_INTR (flags); +} + +static void +guswave_panning (int dev, int voice, int value) +{ + if (voice >= 0 || voice < 32) + voices[voice].panning = value; +} + +static void +guswave_volume_method (int dev, int mode) +{ + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; +} + +static void +compute_volume (int voice, int volume) +{ + if (volume < 128) + voices[voice].midi_volume = volume; + + switch (volume_method) + { + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; + + case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ + voices[voice].initial_volume = + gus_linear_vol (volume, voices[voice].main_vol); + break; + + default: + voices[voice].initial_volume = volume_base + + (voices[voice].midi_volume * volume_scale); + } + + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; +} + +static void +compute_and_set_volume (int voice, int volume, int ramp_time) +{ + int current, target, rate; + unsigned long flags; + + compute_volume (voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; + + DISABLE_INTR (flags); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ + + gus_select_voice (voice); + + current = gus_read16 (0x09) >> 4; + target = voices[voice].initial_volume; + + if (ramp_time == INSTANT_RAMP) + { + gus_rampoff (); + gus_voice_volume (target); + RESTORE_INTR (flags); + return; + } + + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate (0, rate); + + if ((target - current) / 64 == 0) /* Close enough to target. */ + { + gus_rampoff (); + gus_voice_volume (target); + RESTORE_INTR (flags); + return; + } + + if (target > current) + { + if (target > (4095 - 65)) + target = 4095 - 65; + gus_ramp_range (current, target); + gus_rampon (0x00); /* Ramp up, once, no IRQ */ + } + else + { + if (target < 65) + target = 65; + + gus_ramp_range (target, current); + gus_rampon (0x40); /* Ramp down, once, no irq */ + } + RESTORE_INTR (flags); +} + +static void +dynamic_volume_change (int voice) +{ + unsigned char status; + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + status = gus_read8 (0x00); /* Get voice status */ + RESTORE_INTR (flags); + + if (status & 0x03) + return; /* Voice was not running */ + + if (!(voices[voice].mode & WAVE_ENVELOPES)) + { + compute_and_set_volume (voice, voices[voice].midi_volume, 1); + return; + } + + /* + * Voice is running and has envelopes. + */ + + DISABLE_INTR (flags); + gus_select_voice (voice); + status = gus_read8 (0x0d); /* Ramping status */ + RESTORE_INTR (flags); + + if (status & 0x03) /* Sustain phase? */ + { + compute_and_set_volume (voice, voices[voice].midi_volume, 1); + return; + } + + if (voices[voice].env_phase < 0) + return; + + compute_volume (voice, voices[voice].midi_volume); + +} + +static void +guswave_controller (int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + unsigned long freq; + + if (voice < 0 || voice > 31) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + { + freq = compute_finetune (voices[voice].orig_freq, value, + voices[voice].bender_range); + voices[voice].current_freq = freq; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (freq); + RESTORE_INTR (flags); + } + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + if (volume_method == VOL_METHOD_ADAGIO) + { + voices[voice].expression_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change (voice); + } + break; + + case CTL_PAN: + voices[voice].panning = (value * 2) - 128; + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + voices[voice].main_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change (voice); + break; + + default: + break; + } +} + +static int +guswave_start_note2 (int dev, int voice, int note_num, int volume) +{ + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + unsigned long note_freq, base_note, freq, flags; + unsigned char mode = 0; + + if (voice < 0 || voice > 31) + { + printk ("GUS: Invalid voice\n"); + return RET_ERROR (EINVAL); + } + + if (note_num == 255) + { + if (voices[voice].mode & WAVE_ENVELOPES) + { + voices[voice].midi_volume = volume; + dynamic_volume_change (voice); + return 0; + } + + compute_and_set_volume (voice, volume, 1); + return 0; + } + + if ((patch = patch_map[voice]) == -1) + { + return RET_ERROR (EINVAL); + } + + if ((samplep = patch_table[patch]) == -1) + { + return RET_ERROR (EINVAL); + } + + note_freq = note_to_freq (note_num); + + /* + * Find a sample within a patch so that the note_freq is between low_note + * and high_note. + */ + sample = -1; + + best_sample = samplep; + best_delta = 1000000; + while (samplep >= 0 && sample == -1) + { + delta_freq = note_freq - samples[samplep].base_note; + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = samplep; + best_delta = delta_freq; + } + if (samples[samplep].low_note <= note_freq && + note_freq <= samples[samplep].high_note) + sample = samplep; + else + samplep = samples[samplep].key; /* + * Follow link + */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) + { + printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); + return 0; /* Should play default patch ??? */ + } + + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; + voices[voice].mode = samples[sample].mode; + voices[voice].patch_vol = samples[sample].volume; + + if (voices[voice].mode & WAVE_ENVELOPES) + { + int i; + + for (i = 0; i < 6; i++) + { + voices[voice].env_rate[i] = samples[sample].env_rate[i]; + voices[voice].env_offset[i] = samples[sample].env_offset[i]; + } + } + + sample_map[voice] = sample; + + base_note = samples[sample].base_note / 100; /* Try to avoid overflows */ + note_freq /= 100; + + freq = samples[sample].base_freq * note_freq / base_note; + + voices[voice].orig_freq = freq; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, + voices[voice].bender_range); + voices[voice].current_freq = freq; + + pan = (samples[sample].panning + voices[voice].panning) / 32; + pan += 7; + if (pan < 0) + pan = 0; + if (pan > 15) + pan = 15; + + if (samples[sample].mode & WAVE_16_BITS) + { + mode |= 0x04; /* 16 bits */ + if ((sample_ptrs[sample] >> 18) != + ((sample_ptrs[sample] + samples[sample].len) >> 18)) + printk ("GUS: Sample address error\n"); + } + + /************************************************************************* + * CAUTION! Interrupts disabled. Don't return before enabling + *************************************************************************/ + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_off (); + gus_rampoff (); + + RESTORE_INTR (flags); + + if (voices[voice].mode & WAVE_ENVELOPES) + { + compute_volume (voice, volume); + init_envelope (voice); + } + else + compute_and_set_volume (voice, volume, 0); + + DISABLE_INTR (flags); + gus_select_voice (voice); + + if (samples[sample].mode & WAVE_LOOP_BACK) + gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len - + voices[voice].offset_pending, is16bits); /* start=end */ + else + gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending, + is16bits); /* Sample start=begin */ + + if (samples[sample].mode & WAVE_LOOPING) + { + mode |= 0x08; + + if (samples[sample].mode & WAVE_BIDIR_LOOP) + mode |= 0x10; + + if (samples[sample].mode & WAVE_LOOP_BACK) + { + gus_write_addr (0x0a, + sample_ptrs[sample] + samples[sample].loop_end - + voices[voice].offset_pending, is16bits); + mode |= 0x40; + } + + gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, + is16bits);/* Loop start location */ + gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, + is16bits);/* Loop end location */ + } + else + { + mode |= 0x20; /* Loop IRQ at the end */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ + voices[voice].loop_irq_parm = 1; + gus_write_addr (0x02, sample_ptrs[sample], + is16bits);/* Loop start location */ + gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1, + is16bits);/* Loop end location */ + } + gus_voice_freq (freq); + gus_voice_balance (pan); + gus_voice_on (mode); + RESTORE_INTR (flags); + + return 0; +} + +/* + * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking + * when the note playing on the voice is changed. It uses volume + * ramping. + */ + +static int +guswave_start_note (int dev, int voice, int note_num, int volume) +{ + long int flags; + int mode; + int ret_val = 0; + + DISABLE_INTR (flags); + if (note_num == 255) + { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].volume_pending = volume; + else + { + RESTORE_INTR (flags); + ret_val = guswave_start_note2 (dev, voice, note_num, volume); + } + } + else + { + gus_select_voice (voice); + mode = gus_read8 (0x00); + if (mode & 0x20) + gus_write8 (0x00, mode & 0xdf); /* No interrupt! */ + + voices[voice].offset_pending = 0; + voices[voice].kill_pending = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].loop_irq_mode = 0; + + if (voices[voice].sample_pending >= 0) + { + RESTORE_INTR (flags); + guswave_set_instr (voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + DISABLE_INTR (flags); + } + + if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < 2065)) + { + ret_val = guswave_start_note2 (dev, voice, note_num, volume); + } + else + { + voices[voice].dev_pending = dev; + voices[voice].note_pending = note_num; + voices[voice].volume_pending = volume; + voices[voice].volume_irq_mode = VMODE_START_NOTE; + + gus_rampoff (); + gus_ramp_range (2000, 4065); + gus_ramp_rate (0, 63);/* Fastest possible rate */ + gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ + RESTORE_INTR (flags); + } + } + return ret_val; +} + +static void +guswave_reset (int dev) +{ + int i; + + for (i = 0; i < 32; i++) + { + gus_voice_init (i); + gus_voice_init2 (i); + } +} + +static int +guswave_open (int dev, int mode) +{ + int err; + + if (gus_busy) + return RET_ERROR (EBUSY); + + gus_initialize (); + + if ((err = DMAbuf_open_dma (gus_devnum)) < 0) + return err; + + RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); + gus_busy = 1; + active_device = GUS_DEV_WAVE; + + gus_reset (); + + return 0; +} + +static void +guswave_close (int dev) +{ + gus_busy = 0; + active_device = 0; + gus_reset (); + + DMAbuf_close_dma (gus_devnum); +} + +static int +guswave_load_patch (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + long sizeof_patch; + + unsigned long blk_size, blk_end, left, src_offs, target; + + sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ + + if (format != GUS_PATCH) + { + printk ("GUS Error: Invalid patch format (key) 0x%x\n", format); + return RET_ERROR (EINVAL); + } + + if (count < sizeof_patch) + { + printk ("GUS Error: Patch header too short\n"); + return RET_ERROR (EINVAL); + } + + count -= sizeof_patch; + + if (free_sample >= MAX_SAMPLE) + { + printk ("GUS: Sample table full\n"); + return RET_ERROR (ENOSPC); + } + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs); + + instr = patch.instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { + printk ("GUS: Invalid patch number %d\n", instr); + return RET_ERROR (EINVAL); + } + + if (count < patch.len) + { + printk ("GUS Warning: Patch record too short (%d<%d)\n", + count, (int) patch.len); + patch.len = count; + } + + if (patch.len <= 0 || patch.len > gus_mem_size) + { + printk ("GUS: Invalid sample length %d\n", (int) patch.len); + return RET_ERROR (EINVAL); + } + + if (patch.mode & WAVE_LOOPING) + { + if (patch.loop_start < 0 || patch.loop_start >= patch.len) + { + printk ("GUS: Invalid loop start\n"); + return RET_ERROR (EINVAL); + } + + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + { + printk ("GUS: Invalid loop end\n"); + return RET_ERROR (EINVAL); + } + } + + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ + +#define GUS_BANK_SIZE (256*1024) + + if (patch.mode & WAVE_16_BITS) + { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) + { + printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len); + return RET_ERROR (ENOSPC); + } + + if ((free_mem_ptr / GUS_BANK_SIZE) != + ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + { + unsigned long tmp_mem = /* Aling to 256K */ + ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; + + if ((tmp_mem + patch.len) > gus_mem_size) + return RET_ERROR (ENOSPC); + + free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + } + } + + if ((free_mem_ptr + patch.len) > gus_mem_size) + return RET_ERROR (ENOSPC); + + sample_ptrs[free_sample] = free_mem_ptr; + + /* + * Tremolo is not possible with envelopes + */ + + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; + + memcpy ((char *) &samples[free_sample], &patch, sizeof_patch); + + /* + * Link this_one sample to the list of samples for patch 'instr'. + */ + + samples[free_sample].key = patch_table[instr]; + patch_table[instr] = free_sample; + + /* + * Use DMA to transfer the wave data to the DRAM + */ + + left = patch.len; + src_offs = 0; + target = free_mem_ptr; + + while (left) /* Not completely transferred yet */ + { + blk_size = audio_devs[gus_devnum]->buffsize; + if (blk_size > left) + blk_size = left; + + /* + * DMA cannot cross 256k bank boundaries. Check for that. + */ + blk_end = target + blk_size; + + if ((target >> 18) != (blk_end >> 18)) + { /* Split the block */ + + blk_end &= ~(256 * 1024 - 1); + blk_size = blk_end - target; + } + +#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA) + /* + * For some reason the DMA is not possible. We have to use PIO. + */ + { + long i; + unsigned char data; + + for (i = 0; i < blk_size; i++) + { + GET_BYTE_FROM_USER (data, addr, sizeof_patch + i); + if (patch.mode & WAVE_UNSIGNED) + + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* Convert to signed */ + gus_poke (target + i, data); + } + } +#else /* GUS_NO_DMA */ + { + unsigned long address, hold_address; + unsigned char dma_command; + unsigned long flags; + + /* + * OK, move now. First in and then out. + */ + + COPY_FROM_USER (audio_devs[gus_devnum]->dmap->raw_buf[0], + addr, sizeof_patch + src_offs, + blk_size); + + DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/ + gus_write8 (0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma (gus_devnum, + audio_devs[gus_devnum]->dmap->raw_buf_phys[0], + blk_size, DMA_MODE_WRITE); + + /* + * Set the DRAM address for the wave data + */ + + address = target; + + if (audio_devs[gus_devnum]->dmachan > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + + gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + /* + * Start the DMA transfer + */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + if (patch.mode & WAVE_UNSIGNED) + dma_command |= 0x80; /* Invert MSB */ + if (patch.mode & WAVE_16_BITS) + dma_command |= 0x40; /* 16 bit _DATA_ */ + if (audio_devs[gus_devnum]->dmachan > 3) + dma_command |= 0x04; /* 16 bit DMA _channel_ */ + + gus_write8 (0x41, dma_command); /* Lets bo luteet (=bugs) */ + + /* + * Sleep here until the DRAM DMA done interrupt is served + */ + active_device = GUS_DEV_WAVE; + + DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ); + if (TIMED_OUT (dram_sleeper, dram_sleep_flag)) + printk ("GUS: DMA Transfer timed out\n"); + RESTORE_INTR (flags); + } +#endif /* GUS_NO_DMA */ + + /* + * Now the next part + */ + + left -= blk_size; + src_offs += blk_size; + target += blk_size; + + gus_write8 (0x41, 0); /* Stop DMA */ + } + + free_mem_ptr += patch.len; + + if (!pmgr_flag) + pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); + free_sample++; + return 0; +} + +static void +guswave_hw_control (int dev, unsigned char *event) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned long plong, flags; + + cmd = event[2]; + voice = event[3]; + p1 = *(unsigned short *) &event[4]; + p2 = *(unsigned short *) &event[6]; + plong = *(unsigned long *) &event[4]; + + if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && + (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) + do_volume_irq (voice); + + switch (cmd) + { + + case _GUS_NUMVOICES: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_select_max_voices (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr (dev, voice, p1); + break; + + case _GUS_VOICEON: + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_on (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEOFF: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_off (); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEFADE: + gus_voice_fade (voice); + break; + + case _GUS_VOICEMODE: + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_mode (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEBALA: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_balance (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEFREQ: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (plong); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEVOL: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_volume (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEVOL2: /* Just update the software voice level */ + voices[voice].initial_volume = + voices[voice].current_volume = p1; + break; + + case _GUS_RAMPRANGE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_ramp_range (p1, p2); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NJET-NJET */ + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_ramp_rate (p1, p2); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_ramp_mode (p1); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* EI-EI */ + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_rampon (p1); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NEJ-NEJ */ + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_rampoff (); + RESTORE_INTR (flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + case _GUS_VOICE_POS: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_set_voice_pos (voice, plong); + RESTORE_INTR (flags); + break; + + default:; + } +} + +static int +gus_sampling_set_speed (int speed) +{ + if (speed <= 0) + return gus_sampling_speed; + + if (speed > 44100) + speed = 44100; + + gus_sampling_speed = speed; + return speed; +} + +static int +gus_sampling_set_channels (int channels) +{ + if (!channels) + return gus_sampling_channels; + if (channels > 2) + channels = 2; + if (channels < 1) + channels = 1; + gus_sampling_channels = channels; + return channels; +} + +static int +gus_sampling_set_bits (int bits) +{ + if (!bits) + return gus_sampling_bits; + + if (bits != 8 && bits != 16) + bits = 8; + + gus_sampling_bits = bits; + return bits; +} + +static int +gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return gus_sampling_set_speed (arg); + return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return gus_sampling_speed; + return IOCTL_OUT (arg, gus_sampling_speed); + break; + + case SNDCTL_DSP_STEREO: + if (local) + return gus_sampling_set_channels (arg + 1) - 1; + return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1); + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return gus_sampling_set_channels (arg); + return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return gus_sampling_channels; + return IOCTL_OUT (arg, gus_sampling_channels); + break; + + case SNDCTL_DSP_SETFMT: + if (local) + return gus_sampling_set_bits (arg); + return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return gus_sampling_bits; + return IOCTL_OUT (arg, gus_sampling_bits); + + case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + break; + + case SOUND_PCM_READ_FILTER: + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + break; + + default: + return RET_ERROR (EINVAL); + } + return RET_ERROR (EINVAL); +} + +static void +gus_sampling_reset (int dev) +{ +} + +static int +gus_sampling_open (int dev, int mode) +{ +#ifdef GUS_NO_DMA + printk ("GUS: DMA mode not enabled. Device not supported\n"); + return RET_ERROR (ENXIO); +#endif + + if (gus_busy) + return RET_ERROR (EBUSY); + + gus_initialize (); + + gus_busy = 1; + active_device = 0; + + gus_reset (); + reset_sample_memory (); + gus_select_max_voices (14); + + pcm_active = 0; + dma_active = 0; + pcm_opened = 1; + if (mode & OPEN_READ) + { + recording_active = 1; + set_input_volumes (); + } + + return 0; +} + +static void +gus_sampling_close (int dev) +{ + gus_reset (); + gus_busy = 0; + pcm_opened = 0; + active_device = 0; + + if (recording_active) + set_input_volumes (); + + recording_active = 0; +} + +static void +gus_sampling_update_volume (void) +{ + unsigned long flags; + int voice; + + DISABLE_INTR (flags); + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_sampling_channels; voice++) + { + gus_select_voice (voice); + gus_rampoff (); + gus_voice_volume (1530 + (25 * gus_pcm_volume)); + gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); + } + RESTORE_INTR (flags); +} + +static void +play_next_pcm_block (void) +{ + unsigned long flags; + int speed = gus_sampling_speed; + int this_one, is16bits, chn; + unsigned long dram_loc; + unsigned char mode[2], ramp_mode[2]; + + if (!pcm_qlen) + return; + + this_one = pcm_head; + + for (chn = 0; chn < gus_sampling_channels; chn++) + { + mode[chn] = 0x00; + ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + + if (chn == 0) + { + mode[chn] |= 0x20; /* Loop IRQ */ + voices[chn].loop_irq_mode = LMODE_PCM; + } + + if (gus_sampling_bits != 8) + { + is16bits = 1; + mode[chn] |= 0x04; /* 16 bit data */ + } + else + is16bits = 0; + + dram_loc = this_one * pcm_bsize; + dram_loc += chn * pcm_banksize; + + if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ + { + mode[chn] |= 0x08; /* Enable loop */ + ramp_mode[chn] = 0x03;/* Disable rollover bit */ + } + else + { + if (chn == 0) + ramp_mode[chn] = 0x04; /* Enable rollover bit */ + } + + DISABLE_INTR (flags); + gus_select_voice (chn); + gus_voice_freq (speed); + + if (gus_sampling_channels == 1) + gus_voice_balance (7); /* mono */ + else if (chn == 0) + gus_voice_balance (0); /* left */ + else + gus_voice_balance (15); /* right */ + + if (!pcm_active) /* Playback not already active */ + { + /* + * The playback was not started yet (or there has been a pause). + * Start the voice (again) and ask for a rollover irq at the end of + * this_one block. If this_one one is last of the buffers, use just + * the normal loop with irq. + */ + + gus_voice_off (); + gus_rampoff (); + gus_voice_volume (1530 + (25 * gus_pcm_volume)); + gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); + + gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */ + gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start */ + + if (chn != 0) + gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk), + is16bits); /* Loop end location */ + } + + if (chn == 0) + gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], + is16bits); /* Loop end location */ + else + mode[chn] |= 0x08; /* Enable looping */ + + if (pcm_datasize[this_one] != pcm_bsize) + { + /* + * Incompletely filled block. Possibly the last one. + */ + if (chn == 0) + { + mode[chn] &= ~0x08; /* Disable looping */ + mode[chn] |= 0x20;/* Enable IRQ at the end */ + voices[0].loop_irq_mode = LMODE_PCM_STOP; + ramp_mode[chn] = 0x03; /* No rollover bit */ + } + else + { + gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], + is16bits); /* Loop end location */ + mode[chn] &= ~0x08; /* Disable looping */ + } + } + + RESTORE_INTR (flags); + } + + for (chn = 0; chn < gus_sampling_channels; chn++) + { + DISABLE_INTR (flags); + gus_select_voice (chn); + gus_write8 (0x0d, ramp_mode[chn]); + gus_voice_on (mode[chn]); + RESTORE_INTR (flags); + } + + pcm_active = 1; +} + +static void +gus_transfer_output_block (int dev, unsigned long buf, + int total_count, int intrflag, int chn) +{ + /* + * This routine transfers one block of audio data to the DRAM. In mono mode + * it's called just once. When in stereo mode, this_one routine is called + * once for both channels. + * + * The left/mono channel data is transferred to the beginning of dram and the + * right data to the area pointed by gus_page_size. + */ + + int this_one, count; + unsigned long flags; + unsigned char dma_command; + unsigned long address, hold_address; + + DISABLE_INTR (flags); + + count = total_count / gus_sampling_channels; + + if (chn == 0) + { + if (pcm_qlen >= pcm_nblk) + printk ("GUS Warning: PCM buffers out of sync\n"); + + this_one = pcm_current_block = pcm_tail; + pcm_qlen++; + pcm_tail = (pcm_tail + 1) % pcm_nblk; + pcm_datasize[this_one] = count; + } + else + this_one = pcm_current_block; + + gus_write8 (0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE); + + address = this_one * pcm_bsize; + address += chn * pcm_banksize; + + if (audio_devs[dev]->dmachan > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + + gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + + if (gus_sampling_bits != 8) + dma_command |= 0x40; /* 16 bit _DATA_ */ + else + dma_command |= 0x80; /* Invert MSB */ + + if (audio_devs[dev]->dmachan > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ + + gus_write8 (0x41, dma_command); /* Kickstart */ + + if (chn == (gus_sampling_channels - 1)) /* Last channel */ + { + /* + * Last (right or mono) channel data + */ + dma_active = 1; /* DMA started. There is a unacknowledged buffer */ + active_device = GUS_DEV_PCM_DONE; + if (!pcm_active && (pcm_qlen > 0 || count < pcm_bsize)) + { + play_next_pcm_block (); + } + } + else + { + /* + * Left channel data. The right channel + * is transferred after DMA interrupt + */ + active_device = GUS_DEV_PCM_CONTINUE; + } + + RESTORE_INTR (flags); +} + +static void +gus_sampling_output_block (int dev, unsigned long buf, int total_count, + int intrflag, int restart_dma) +{ + pcm_current_buf = buf; + pcm_current_count = total_count; + pcm_current_intrflag = intrflag; + pcm_current_dev = dev; + gus_transfer_output_block (dev, buf, total_count, intrflag, 0); +} + +static void +gus_sampling_start_input (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) +{ + unsigned long flags; + unsigned char mode; + + DISABLE_INTR (flags); + + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + + mode = 0xa0; /* DMA IRQ enabled, invert MSB */ + + if (audio_devs[dev]->dmachan > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_sampling_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ + + gus_write8 (0x49, mode); + + RESTORE_INTR (flags); +} + +static int +gus_sampling_prepare_for_input (int dev, int bsize, int bcount) +{ + unsigned int rate; + + rate = (9878400 / (gus_sampling_speed + 2)) / 16; + + gus_write8 (0x48, rate & 0xff); /* Set sampling rate */ + + if (gus_sampling_bits != 8) + { + printk ("GUS Error: 16 bit recording not supported\n"); + return RET_ERROR (EINVAL); + } + + return 0; +} + +static int +gus_sampling_prepare_for_output (int dev, int bsize, int bcount) +{ + int i; + + long mem_ptr, mem_size; + + mem_ptr = 0; + mem_size = gus_mem_size / gus_sampling_channels; + + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; + + pcm_bsize = bsize / gus_sampling_channels; + pcm_head = pcm_tail = pcm_qlen = 0; + + pcm_nblk = MAX_PCM_BUFFERS; + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; + + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; + + pcm_banksize = pcm_nblk * pcm_bsize; + + if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + + return 0; +} + +static int +gus_local_qlen (int dev) +{ + return pcm_qlen; +} + +static void +gus_copy_from_user (int dev, char *localbuf, int localoffs, + snd_rw_buf * userbuf, int useroffs, int len) +{ + if (gus_sampling_channels == 1) + { + COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); + } + else if (gus_sampling_bits == 8) + { + int in_left = useroffs; + int in_right = useroffs + 1; + char *out_left, *out_right; + int i; + + len /= 2; + localoffs /= 2; + out_left = &localbuf[localoffs]; + out_right = out_left + pcm_bsize; + + for (i = 0; i < len; i++) + { + GET_BYTE_FROM_USER (*out_left++, userbuf, in_left); + in_left += 2; + GET_BYTE_FROM_USER (*out_right++, userbuf, in_right); + in_right += 2; + } + } + else + { + int in_left = useroffs; + int in_right = useroffs + 1; + short *out_left, *out_right; + int i; + + len /= 4; + localoffs /= 4; + + out_left = (short *) &localbuf[localoffs]; + out_right = out_left + (pcm_bsize / 2); + + for (i = 0; i < len; i++) + { + GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left); + in_left += 2; + GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right); + in_right += 2; + } + } +} + +static struct audio_operations gus_sampling_operations = +{ + "Gravis UltraSound", + NEEDS_RESTART, + AFMT_U8 | AFMT_S16_LE, + NULL, + gus_sampling_open, + gus_sampling_close, + gus_sampling_output_block, + gus_sampling_start_input, + gus_sampling_ioctl, + gus_sampling_prepare_for_input, + gus_sampling_prepare_for_output, + gus_sampling_reset, + gus_sampling_reset, + gus_local_qlen, + gus_copy_from_user +}; + +static void +guswave_bender (int dev, int voice, int value) +{ + int freq; + unsigned long flags; + + voices[voice].bender = value - 8192; + freq = compute_finetune (voices[voice].orig_freq, value, + voices[voice].bender_range); + voices[voice].current_freq = freq; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (freq); + RESTORE_INTR (flags); +} + +static int +guswave_patchmgr (int dev, struct patmgr_info *rec) +{ + int i, n; + + switch (rec->command) + { + case PM_GET_DEVTYPE: + rec->parm1 = PMTYPE_WAVE; + return 0; + break; + + case PM_GET_NRPGM: + rec->parm1 = MAX_PATCH; + return 0; + break; + + case PM_GET_PGMMAP: + rec->parm1 = MAX_PATCH; + + for (i = 0; i < MAX_PATCH; i++) + { + int ptr = patch_table[i]; + + rec->data.data8[i] = 0; + + while (ptr >= 0 && ptr < free_sample) + { + rec->data.data8[i]++; + ptr = samples[ptr].key; /* Follow link */ + } + } + return 0; + break; + + case PM_GET_PGM_PATCHES: + { + int ptr = patch_table[rec->parm1]; + + n = 0; + + while (ptr >= 0 && ptr < free_sample) + { + rec->data.data32[n++] = ptr; + ptr = samples[ptr].key; /* Follow link */ + } + } + rec->parm1 = n; + return 0; + break; + + case PM_GET_PATCH: + { + int ptr = rec->parm1; + struct patch_info *pat; + + if (ptr < 0 || ptr >= free_sample) + return RET_ERROR (EINVAL); + + memcpy (rec->data.data8, (char *) &samples[ptr], + sizeof (struct patch_info)); + + pat = (struct patch_info *) rec->data.data8; + + pat->key = GUS_PATCH; /* Restore patch type */ + rec->parm1 = sample_ptrs[ptr]; /* DRAM location */ + rec->parm2 = sizeof (struct patch_info); + } + return 0; + break; + + case PM_SET_PATCH: + { + int ptr = rec->parm1; + struct patch_info *pat; + + if (ptr < 0 || ptr >= free_sample) + return RET_ERROR (EINVAL); + + pat = (struct patch_info *) rec->data.data8; + + if (pat->len > samples[ptr].len) /* Cannot expand sample */ + return RET_ERROR (EINVAL); + + pat->key = samples[ptr].key; /* Ensure the link is correct */ + + memcpy ((char *) &samples[ptr], rec->data.data8, + sizeof (struct patch_info)); + + pat->key = GUS_PATCH; + } + return 0; + break; + + case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */ + { + int sample = rec->parm1; + int n; + long offs = rec->parm2; + int l = rec->parm3; + + if (sample < 0 || sample >= free_sample) + return RET_ERROR (EINVAL); + + if (offs < 0 || offs >= samples[sample].len) + return RET_ERROR (EINVAL); /* Invalid offset */ + + n = samples[sample].len - offs; /* Num of bytes left */ + + if (l > n) + l = n; + + if (l > sizeof (rec->data.data8)) + l = sizeof (rec->data.data8); + + if (l <= 0) + return RET_ERROR (EINVAL); /* + * Was there a bug? + */ + + offs += sample_ptrs[sample]; /* + * Begin offsess + offset to DRAM + */ + + for (n = 0; n < l; n++) + rec->data.data8[n] = gus_peek (offs++); + rec->parm1 = n; /* + * Nr of bytes copied + */ + } + return 0; + break; + + case PM_WRITE_PATCH: /* + * Writes a block of wave data to the DRAM + */ + { + int sample = rec->parm1; + int n; + long offs = rec->parm2; + int l = rec->parm3; + + if (sample < 0 || sample >= free_sample) + return RET_ERROR (EINVAL); + + if (offs < 0 || offs >= samples[sample].len) + return RET_ERROR (EINVAL); /* + * Invalid offset + */ + + n = samples[sample].len - offs; /* + * Nr of bytes left + */ + + if (l > n) + l = n; + + if (l > sizeof (rec->data.data8)) + l = sizeof (rec->data.data8); + + if (l <= 0) + return RET_ERROR (EINVAL); /* + * Was there a bug? + */ + + offs += sample_ptrs[sample]; /* + * Begin offsess + offset to DRAM + */ + + for (n = 0; n < l; n++) + gus_poke (offs++, rec->data.data8[n]); + rec->parm1 = n; /* + * Nr of bytes copied + */ + } + return 0; + break; + + default: + return RET_ERROR (EINVAL); + } +} + +static int +guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p; + + p = alloc->ptr; + /* + * First look for a completely stopped voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0) + { + alloc->ptr = p; + return p; + } + p = (p + 1) % alloc->max_voice; + } + + /* + * Then look for a releasing voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + return p; + } + p = (p + 1) % alloc->max_voice; + } + printk ("GUS: Out of free voices\n"); + + alloc->ptr = p; + return p; +} + +static struct synth_operations guswave_operations = +{ + &gus_info, + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_GUS, + guswave_open, + guswave_close, + guswave_ioctl, + guswave_kill_note, + guswave_start_note, + guswave_set_instr, + guswave_reset, + guswave_hw_control, + guswave_load_patch, + guswave_aftertouch, + guswave_controller, + guswave_panning, + guswave_volume_method, + guswave_patchmgr, + guswave_bender, + guswave_alloc +}; + +static void +set_input_volumes (void) +{ + unsigned long flags; + unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ + + DISABLE_INTR (flags); + + /* + * Enable channels having vol > 10% + * Note! bit 0x01 means line in DISABLED while 0x04 means + * mic in ENABLED. + */ + if (gus_line_vol > 10) + mask &= ~0x01; + if (gus_mic_vol > 10) + mask |= 0x04; + + if (recording_active) + { + /* + * Disable channel, if not selected for recording + */ + if (!(gus_recmask & SOUND_MASK_LINE)) + mask |= 0x01; + if (!(gus_recmask & SOUND_MASK_MIC)) + mask &= ~0x04; + } + + mix_image &= ~0x07; + mix_image |= mask & 0x07; + OUTB (mix_image, u_Mixer); + + RESTORE_INTR (flags); +} + +int +gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH|SOUND_MASK_PCM) + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + gus_recmask = IOCTL_IN (arg) & MIX_DEVS; + if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) + gus_recmask = SOUND_MASK_MIC; + /* Note! Input volumes are updated during next open for recording */ + return IOCTL_OUT (arg, gus_recmask); + break; + + case SOUND_MIXER_MIC: + { + int vol = IOCTL_IN (arg) & 0xff; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes (); + return IOCTL_OUT (arg, vol | (vol << 8)); + } + break; + + case SOUND_MIXER_LINE: + { + int vol = IOCTL_IN (arg) & 0xff; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes (); + return IOCTL_OUT (arg, vol | (vol << 8)); + } + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = IOCTL_IN (arg) & 0xff; + if (gus_pcm_volume < 0) + gus_pcm_volume = 0; + if (gus_pcm_volume > 100) + gus_pcm_volume = 100; + gus_sampling_update_volume (); + return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); + break; + + case SOUND_MIXER_SYNTH: + { + int voice; + + gus_wave_volume = IOCTL_IN (arg) & 0xff; + + if (gus_wave_volume < 0) + gus_wave_volume = 0; + if (gus_wave_volume > 100) + gus_wave_volume = 100; + + if (active_device == GUS_DEV_WAVE) + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change (voice); /* Apply the new vol */ + + return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); + } + break; + + default: + return RET_ERROR (EINVAL); + } + else + switch (cmd & 0xff) /* + * Return parameters + */ + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, gus_recmask); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, MIX_DEVS); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, 0); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, 0); + break; + + case SOUND_MIXER_MIC: + return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8)); + break; + + case SOUND_MIXER_LINE: + return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8)); + break; + + case SOUND_MIXER_PCM: + return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); + break; + + case SOUND_MIXER_SYNTH: + return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); + break; + + default: + return RET_ERROR (EINVAL); + } + } + else + return RET_ERROR (EINVAL); +} + +static struct mixer_operations gus_mixer_operations = +{ + gus_default_mixer_ioctl +}; + +static long +gus_default_mixer_init (long mem_start) +{ + if (num_mixers < MAX_MIXER_DEV) /* + * Don't install if there is another + * mixer + */ + mixer_devs[num_mixers++] = &gus_mixer_operations; + + return mem_start; +} + +long +gus_wave_init (long mem_start, int irq, int dma) +{ + unsigned long flags; + unsigned char val; + char *model_num = "2.4"; + int gus_type = 0x24; /* 2.4 */ + int mixer_type = 0; + + /* + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + */ + + DISABLE_INTR (flags); + OUTB (0x20, gus_base + 0x0f); + val = INB (gus_base + 0x0f); + RESTORE_INTR (flags); + + if (val != 0xff && (val & 0x06)) /* Should be 0x02?? */ + { + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. + */ + + val = INB (u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + * Sorry. No GUS max support yet but it should be available + * soon after the SDK for GUS MAX is available. + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + } + else + { + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; +#ifndef EXCLUDE_GUSMAX + { + unsigned char max_config = 0x40; /* Codec enable */ + + if (dma > 3) + max_config |= 0x30; /* 16 bit playback and capture DMAs */ + + max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + + OUTB (max_config, gus_base + 0x106); /* UltraMax control */ + } + + if (ad1848_detect (gus_base + 0x10c)) + { + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + ad1848_init ("GUS MAX", gus_base + 0x10c, + -irq, + dma, + dma); + } + else + printk ("[Where's the CS4231?]"); +#endif + } + } + else + { + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + * It has the same codec/mixer than MAX. + * At this time there is no support for it but it will appear soon. + */ + } + + + printk (" ", model_num, (int) gus_mem_size / 1024); + +#ifndef SCO + sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); +#endif + + if (irq < 0 || irq > 15) + { + printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return mem_start; + } + + if (dma < 0 || dma > 7) + { + printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma); + return mem_start; + } + + gus_irq = irq; + gus_dma = dma; + + if (num_synths >= MAX_SYNTH_DEV) + printk ("GUS Error: Too many synthesizers\n"); + else + { + voice_alloc = &guswave_operations.alloc; + synth_devs[num_synths++] = &guswave_operations; + } + + PERMANENT_MALLOC (struct patch_info *, samples, + (MAX_SAMPLE + 1) * sizeof (*samples), mem_start); + + reset_sample_memory (); + + gus_initialize (); + + if (num_audiodevs < MAX_AUDIO_DEV) + { + audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations; + audio_devs[gus_devnum]->dmachan = dma; + audio_devs[gus_devnum]->buffcount = 1; + audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE; + } + else + printk ("GUS: Too many PCM devices available\n"); + + /* + * Mixer dependent initialization. + */ + + switch (mixer_type) + { + case ICS2101: + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + return ics2101_mixer_init (mem_start); + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + return gus_default_mixer_init (mem_start); + } + + return mem_start; +} + +static void +do_loop_irq (int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + + tmp = gus_read8 (0x00); + tmp &= ~0x20; /* + * Disable wave IRQ for this_one voice + */ + gus_write8 (0x00, tmp); + + mode = voices[voice].loop_irq_mode; + voices[voice].loop_irq_mode = 0; + parm = voices[voice].loop_irq_parm; + + switch (mode) + { + + case LMODE_FINISH: /* + * Final loop finished, shoot volume down + */ + + if ((int) (gus_read16 (0x09) >> 4) < 100) /* + * Get current volume + */ + { + gus_voice_off (); + gus_rampoff (); + gus_voice_init (voice); + break; + } + gus_ramp_range (65, 4065); + gus_ramp_rate (0, 63); /* + * Fastest possible rate + */ + gus_rampon (0x20 | 0x40); /* + * Ramp down, once, irq + */ + voices[voice].volume_irq_mode = VMODE_HALT; + break; + + case LMODE_PCM_STOP: + pcm_active = 0; /* Signal to the play_next_pcm_block routine */ + case LMODE_PCM: + { + int orig_qlen = pcm_qlen; + int flag; /* 0 or 2 */ + + pcm_qlen--; + pcm_head = (pcm_head + 1) % pcm_nblk; + if (pcm_qlen) + { + play_next_pcm_block (); + } + else + { /* Underrun. Just stop the voice */ + gus_voice_off (); + gus_rampoff (); + pcm_active = 0; + } + + /* + * If the queue was full before this interrupt, the DMA transfer was + * suspended. Let it continue now. + */ + if (dma_active) + { + if (pcm_qlen == 0) + flag = 1; /* Underflow */ + else + flag = 0; + dma_active = 0; + } + else + flag = 2; /* Just notify the dmabuf.c */ + DMAbuf_outputintr (gus_devnum, flag); + } + break; + + default:; + } + RESTORE_INTR (flags); +} + +static void +do_volume_irq (int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + DISABLE_INTR (flags); + + gus_select_voice (voice); + + tmp = gus_read8 (0x0d); + tmp &= ~0x20; /* + * Disable volume ramp IRQ + */ + gus_write8 (0x0d, tmp); + + mode = voices[voice].volume_irq_mode; + voices[voice].volume_irq_mode = 0; + parm = voices[voice].volume_irq_parm; + + switch (mode) + { + case VMODE_HALT: /* + * Decay phase finished + */ + RESTORE_INTR (flags); + gus_voice_init (voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff (); + RESTORE_INTR (flags); + step_envelope (voice); + break; + + case VMODE_START_NOTE: + RESTORE_INTR (flags); + guswave_start_note2 (voices[voice].dev_pending, voice, + voices[voice].note_pending, voices[voice].volume_pending); + if (voices[voice].kill_pending) + guswave_kill_note (voices[voice].dev_pending, voice, + voices[voice].note_pending, 0); + + if (voices[voice].sample_pending >= 0) + { + guswave_set_instr (voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + } + break; + + default:; + } +} + +void +gus_voice_irq (void) +{ + unsigned long wave_ignore = 0, volume_ignore = 0; + unsigned long voice_bit; + + unsigned char src, voice; + + while (1) + { + src = gus_read8 (0x0f); /* + * Get source info + */ + voice = src & 0x1f; + src &= 0xc0; + + if (src == (0x80 | 0x40)) + return; /* + * No interrupt + */ + + voice_bit = 1 << voice; + + if (!(src & 0x80)) /* + * Wave IRQ pending + */ + if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + wave_ignore |= voice_bit; + do_loop_irq (voice); + } + + if (!(src & 0x40)) /* + * Volume IRQ pending + */ + if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + volume_ignore |= voice_bit; + do_volume_irq (voice); + } + } +} + +void +guswave_dma_irq (void) +{ + unsigned char status; + + status = gus_look8 (0x41); /* Get DMA IRQ Status */ + if (status & 0x40) /* DMA interrupt pending */ + switch (active_device) + { + case GUS_DEV_WAVE: + if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag)) + WAKE_UP (dram_sleeper, dram_sleep_flag); + break; + + case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ + gus_transfer_output_block (pcm_current_dev, pcm_current_buf, + pcm_current_count, + pcm_current_intrflag, 1); + break; + + case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ + if (pcm_qlen < pcm_nblk) + { + int flag = (1 - dma_active) * 2; /* 0 or 2 */ + + if (pcm_qlen == 0) + flag = 1; /* Underrun */ + dma_active = 0; + DMAbuf_outputintr (gus_devnum, flag); + } + break; + + default:; + } + + status = gus_look8 (0x49); /* + * Get Sampling IRQ Status + */ + if (status & 0x40) /* + * Sampling Irq pending + */ + { + DMAbuf_inputintr (gus_devnum); + } + +} + +#endif diff --git a/sys/i386/isa/sound/ics2101.c b/sys/i386/isa/sound/ics2101.c new file mode 100644 index 00000000000..4147a070fd8 --- /dev/null +++ b/sys/i386/isa/sound/ics2101.c @@ -0,0 +1,263 @@ +/* + * sound/ics2101.c + * + * Driver for the ICS2101 mixer of GUS v3.7. + * + * Copyright by Hannu Savolainen 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) + +#include "ultrasound.h" +#include "gus_hw.h" + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH| \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +extern int gus_base; +static int volumes[ICS_MIXDEVS]; +static int left_fix[ICS_MIXDEVS] = +{1, 1, 1, 2, 1, 2}; +static int right_fix[ICS_MIXDEVS] = +{2, 2, 2, 1, 2, 1}; + +static int +scale_vol (int vol) +{ +#if 1 + /* + * Experimental volume scaling by Risto Kankkunen. + * This should give smoother volume response than just + * a plain multiplication. + */ + int e; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) + { + while (vol < 16) + { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; + } + return ((e << 4) + vol); +#else + return ((vol * 127) + 50) / 100; +#endif +} + +static void +write_mix (int dev, int chn, int vol) +{ + int *selector; + unsigned long flags; + int ctrl_addr = dev << 3; + int attn_addr = dev << 3; + + vol = scale_vol (vol); + + if (chn == CHN_LEFT) + { + selector = left_fix; + ctrl_addr |= 0x00; + attn_addr |= 0x02; + } + else + { + selector = right_fix; + ctrl_addr |= 0x01; + attn_addr |= 0x03; + } + + DISABLE_INTR (flags); + OUTB (ctrl_addr, u_MixSelect); + OUTB (selector[dev], u_MixData); + OUTB (attn_addr, u_MixSelect); + OUTB ((unsigned char) vol, u_MixData); + RESTORE_INTR (flags); +} + +static int +set_volumes (int dev, int vol) +{ + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + if (left < 0) + left = 0; + if (left > 100) + left = 100; + if (right < 0) + right = 0; + if (right > 100) + right = 100; + + write_mix (dev, CHN_LEFT, left); + write_mix (dev, CHN_RIGHT, right); + + vol = left + (right << 8); + volumes[dev] = vol; + return vol; +} + +static int +ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl (dev, cmd, arg); + break; + + case SOUND_MIXER_MIC: + return IOCTL_OUT (arg, set_volumes (DEV_MIC, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_CD: + return IOCTL_OUT (arg, set_volumes (DEV_CD, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_LINE: + return IOCTL_OUT (arg, set_volumes (DEV_LINE, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_SYNTH: + return IOCTL_OUT (arg, set_volumes (DEV_GF1, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_VOLUME: + return IOCTL_OUT (arg, set_volumes (DEV_VOL, IOCTL_IN (arg))); + break; + + default: + return RET_ERROR (EINVAL); + } + else + switch (cmd & 0xff) /* + * Return parameters + */ + { + + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl (dev, cmd, arg); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, MIX_DEVS); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | + SOUND_MASK_MIC); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, 0); + break; + + case SOUND_MIXER_MIC: + return IOCTL_OUT (arg, volumes[DEV_MIC]); + break; + + case SOUND_MIXER_LINE: + return IOCTL_OUT (arg, volumes[DEV_LINE]); + break; + + case SOUND_MIXER_CD: + return IOCTL_OUT (arg, volumes[DEV_CD]); + break; + + case SOUND_MIXER_VOLUME: + return IOCTL_OUT (arg, volumes[DEV_VOL]); + break; + + case SOUND_MIXER_SYNTH: + return IOCTL_OUT (arg, volumes[DEV_GF1]); + break; + + default: + return RET_ERROR (EINVAL); + } + } + + return RET_ERROR (EINVAL); +} + +static struct mixer_operations ics2101_mixer_operations = +{ + ics2101_mixer_ioctl +}; + +long +ics2101_mixer_init (long mem_start) +{ + int i; + + if (num_mixers < MAX_MIXER_DEV) + { + mixer_devs[num_mixers++] = &ics2101_mixer_operations; + + /* + * Some GUS v3.7 cards had some channels flipped. Disable + * the flipping feature if the model id is other than 5. + */ + + if (INB (u_MixSelect) != 5) + { + for (i = 0; i < ICS_MIXDEVS; i++) + left_fix[i] = 1; + for (i = 0; i < ICS_MIXDEVS; i++) + right_fix[i] = 2; + } + + set_volumes (DEV_GF1, 0x5a5a); + set_volumes (DEV_CD, 0x5a5a); + set_volumes (DEV_MIC, 0x0000); + set_volumes (DEV_LINE, 0x5a5a); + set_volumes (DEV_VOL, 0x5a5a); + set_volumes (DEV_UNUSED, 0x0000); + } + + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h new file mode 100644 index 00000000000..533d60a2266 --- /dev/null +++ b/sys/i386/isa/sound/local.h @@ -0,0 +1,27 @@ +/* Generated by configure. Don't edit!!!! */ + +#define KERNEL_SOUNDCARD +#undef EXCLUDE_PAS +#undef EXCLUDE_SB +#undef EXCLUDE_ADLIB +#undef EXCLUDE_GUS +#define EXCLUDE_MPU401 +#define EXCLUDE_UART6850 +#define EXCLUDE_PSS +#undef EXCLUDE_GUS16 +#undef EXCLUDE_GUSMAX +#undef EXCLUDE_MSS +#undef EXCLUDE_SBPRO +#undef EXCLUDE_SB16 +#undef EXCLUDE_AUDIO +#undef EXCLUDE_MIDI +#undef EXCLUDE_YM3812 +#undef EXCLUDE_SEQUENCER + +#define DSP_BUFFSIZE 32768 +#define SELECTED_SOUND_OPTIONS 0xffffff8f +#define SOUND_VERSION_STRING "2.90-2" +#define SOUND_CONFIG_DATE "Thu Sep 29 15:33:39 PDT 1994" +#define SOUND_CONFIG_BY "swallace" +#define SOUND_CONFIG_HOST "pal-r32-a07b.slip.nts.uci.edu" +#define SOUND_CONFIG_DOMAIN "" diff --git a/sys/i386/isa/sound/midi_ctrl.h b/sys/i386/isa/sound/midi_ctrl.h new file mode 100644 index 00000000000..616b5914c6e --- /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 00000000000..fd6d8bde62c --- /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 00000000000..04075e294ca --- /dev/null +++ b/sys/i386/isa/sound/midi_synth.h @@ -0,0 +1,45 @@ +int midi_synth_ioctl (int dev, + unsigned int cmd, unsigned int arg); +int midi_synth_kill_note (int dev, int channel, int note, int velocity); +int midi_synth_set_instr (int dev, int channel, int instr_no); +int midi_synth_start_note (int dev, int channel, int note, int volume); +void midi_synth_reset (int dev); +int midi_synth_open (int dev, int mode); +void midi_synth_close (int dev); +void midi_synth_hw_control (int dev, unsigned char *event); +int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag); +void midi_synth_panning (int dev, int channel, int pressure); +void midi_synth_aftertouch (int dev, int channel, int pressure); +void midi_synth_controller (int dev, int channel, int ctrl_num, int value); +int midi_synth_patchmgr (int dev, struct patmgr_info *rec); +void midi_synth_bender (int dev, int chn, int value); + + +#ifndef _MIDI_SYNTH_C_ +static struct synth_info std_synth_info = +{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS}; + +static struct synth_operations std_midi_synth = +{ + &std_synth_info, + 0, + SYNTH_TYPE_MIDI, + 0, + midi_synth_open, + midi_synth_close, + midi_synth_ioctl, + midi_synth_kill_note, + midi_synth_start_note, + midi_synth_set_instr, + midi_synth_reset, + midi_synth_hw_control, + midi_synth_load_patch, + midi_synth_aftertouch, + midi_synth_controller, + midi_synth_panning, + NULL, + midi_synth_patchmgr, + midi_synth_bender +}; +#endif diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c new file mode 100644 index 00000000000..ae0ddc3ea54 --- /dev/null +++ b/sys/i386/isa/sound/midibuf.c @@ -0,0 +1,443 @@ +/* + * sound/midibuf.c + * + * Device file manager for /dev/midi# + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI) + +/* + * Don't make MAX_QUEUE_SIZE larger than 4000 + */ + +#define MAX_QUEUE_SIZE 4000 + +DEFINE_WAIT_QUEUES (midi_sleeper[MAX_MIDI_DEV], midi_sleep_flag[MAX_MIDI_DEV]); +DEFINE_WAIT_QUEUES (input_sleeper[MAX_MIDI_DEV], input_sleep_flag[MAX_MIDI_DEV]); + +struct midi_buf + { + int len, head, tail; + unsigned char queue[MAX_QUEUE_SIZE]; + }; + +struct midi_parms + { + int prech_timeout; /* + * Timeout before the first ch + */ + }; + +static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = +{NULL}; +static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = +{NULL}; +static struct midi_parms parms[MAX_MIDI_DEV]; + +static void midi_poll (unsigned long dummy); + +DEFINE_TIMER (poll_timer, midi_poll); +static volatile int open_devs = 0; + +#define DATA_AVAIL(q) (q->len) +#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) + +#define QUEUE_BYTE(q, data) \ + if (SPACE_AVAIL(q)) \ + { \ + unsigned long flags; \ + DISABLE_INTR(flags); \ + q->queue[q->tail] = (data); \ + q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ + RESTORE_INTR(flags); \ + } + +#define REMOVE_BYTE(q, data) \ + if (DATA_AVAIL(q)) \ + { \ + unsigned long flags; \ + DISABLE_INTR(flags); \ + data = q->queue[q->head]; \ + q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ + RESTORE_INTR(flags); \ + } + +void +drain_midi_queue (int dev) +{ + + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) && + midi_devs[dev]->buffer_status (dev)) + DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], HZ / 10); +} + +static void +midi_input_intr (int dev, unsigned char data) +{ + if (midi_in_buf[dev] == NULL) + return; + + if (data == 0xfe) /* + * Active sensing + */ + return; /* + * Ignore + */ + + if (SPACE_AVAIL (midi_in_buf[dev])) + { + QUEUE_BYTE (midi_in_buf[dev], data); + if (SOMEONE_WAITING (input_sleeper[dev], input_sleep_flag[dev])) + WAKE_UP (input_sleeper[dev], input_sleep_flag[dev]); + } + +} + +static void +midi_output_intr (int dev) +{ + /* + * Currently NOP + */ +} + +static void +midi_poll (unsigned long dummy) +{ + unsigned long flags; + int dev; + + DISABLE_INTR (flags); + if (open_devs) + { + for (dev = 0; dev < num_midis; dev++) + if (midi_out_buf[dev] != NULL) + { + while (DATA_AVAIL (midi_out_buf[dev]) && + midi_devs[dev]->putc (dev, + midi_out_buf[dev]->queue[midi_out_buf[dev]->head])) + { + midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; + midi_out_buf[dev]->len--; + } + + if (DATA_AVAIL (midi_out_buf[dev]) < 100 && + SOMEONE_WAITING (midi_sleeper[dev], midi_sleep_flag[dev])) + WAKE_UP (midi_sleeper[dev], midi_sleep_flag[dev]); + } + ACTIVATE_TIMER (poll_timer, midi_poll, 1); /* + * Come back later + */ + } + RESTORE_INTR (flags); +} + +int +MIDIbuf_open (int dev, struct fileinfo *file) +{ + int mode, err; + unsigned long flags; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (num_midis > MAX_MIDI_DEV) + { + printk ("Sound: FATAL ERROR: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } + + if (dev < 0 || dev >= num_midis) + { + printk ("Sound: Nonexistent MIDI interface %d\n", dev); + return RET_ERROR (ENXIO); + } + + /* + * Interrupts disabled. Be careful + */ + + DISABLE_INTR (flags); + if ((err = midi_devs[dev]->open (dev, mode, + midi_input_intr, midi_output_intr)) < 0) + { + RESTORE_INTR (flags); + return err; + } + + parms[dev].prech_timeout = 0; + + RESET_WAIT_QUEUE (midi_sleeper[dev], midi_sleep_flag[dev]); + RESET_WAIT_QUEUE (input_sleeper[dev], input_sleep_flag[dev]); + + midi_in_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf)); + + if (midi_in_buf[dev] == NULL) + { + printk ("midi: Can't allocate buffer\n"); + midi_devs[dev]->close (dev); + RESTORE_INTR (flags); + return RET_ERROR (EIO); + } + midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; + + midi_out_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf)); + + if (midi_out_buf[dev] == NULL) + { + printk ("midi: Can't allocate buffer\n"); + midi_devs[dev]->close (dev); + KERNEL_FREE (midi_in_buf[dev]); + midi_in_buf[dev] = NULL; + RESTORE_INTR (flags); + return RET_ERROR (EIO); + } + midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; + if (!open_devs) + ACTIVATE_TIMER (poll_timer, midi_poll, 1); /* + * Come back later + */ + open_devs++; + RESTORE_INTR (flags); + + return err; +} + +void +MIDIbuf_release (int dev, struct fileinfo *file) +{ + int mode; + unsigned long flags; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + DISABLE_INTR (flags); + + /* + * Wait until the queue is empty + */ + + if (mode != OPEN_READ) + { + midi_devs[dev]->putc (dev, 0xfe); /* + * Active sensing to shut the + * devices + */ + + while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) && + DATA_AVAIL (midi_out_buf[dev])) + DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); /* + * Sync + */ + + drain_midi_queue (dev); /* + * Ensure the output queues are empty + */ + } + + midi_devs[dev]->close (dev); + KERNEL_FREE (midi_in_buf[dev]); + KERNEL_FREE (midi_out_buf[dev]); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + open_devs--; + RESTORE_INTR (flags); +} + +int +MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned long flags; + int c, n, i; + unsigned char tmp_data; + + dev = dev >> 4; + + if (!count) + return 0; + + DISABLE_INTR (flags); + + c = 0; + + while (c < count) + { + n = SPACE_AVAIL (midi_out_buf[dev]); + + if (n == 0) /* + * No space just now. We have to sleep + */ + { + DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); + if (PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev])) + { + RESTORE_INTR (flags); + return RET_ERROR (EINTR); + } + + n = SPACE_AVAIL (midi_out_buf[dev]); + } + + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) + { + COPY_FROM_USER (&tmp_data, buf, c, 1); + QUEUE_BYTE (midi_out_buf[dev], tmp_data); + c++; + } + } + + RESTORE_INTR (flags); + + return c; +} + + +int +MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int n, c = 0; + unsigned long flags; + unsigned char tmp_data; + + dev = dev >> 4; + + DISABLE_INTR (flags); + + if (!DATA_AVAIL (midi_in_buf[dev])) /* + * No data yet, wait + */ + { + DO_SLEEP (input_sleeper[dev], input_sleep_flag[dev], + parms[dev].prech_timeout); + if (PROCESS_ABORTING (input_sleeper[dev], input_sleep_flag[dev])) + c = RET_ERROR (EINTR); /* + * The user is getting restless + */ + } + + if (c == 0 && DATA_AVAIL (midi_in_buf[dev])) /* + * Got some bytes + */ + { + n = DATA_AVAIL (midi_in_buf[dev]); + if (n > count) + n = count; + c = 0; + + while (c < n) + { + REMOVE_BYTE (midi_in_buf[dev], tmp_data); + COPY_TO_USER (buf, c, &tmp_data, 1); + c++; + } + } + + RESTORE_INTR (flags); + + return c; +} + +int +MIDIbuf_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + int val; + + dev = dev >> 4; + + switch (cmd) + { + + case SNDCTL_MIDI_PRETIME: + val = IOCTL_IN (arg); + if (val < 0) + val = 0; + + val = (HZ * val) / 10; + parms[dev].prech_timeout = val; + return IOCTL_OUT (arg, val); + break; + + default: + return midi_devs[dev]->ioctl (dev, cmd, arg); + } +} + +#ifdef ALLOW_SELECT +int +MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +{ + dev = dev >> 4; + + switch (sel_type) + { + case SEL_IN: + if (!DATA_AVAIL (midi_in_buf[dev])) + { + input_sleep_flag[dev].mode = WK_SLEEP; + select_wait (&input_sleeper[dev], wait); + return 0; + } + return 1; + break; + + case SEL_OUT: + if (SPACE_AVAIL (midi_out_buf[dev])) + { + midi_sleep_flag[dev].mode = WK_SLEEP; + select_wait (&midi_sleeper[dev], wait); + return 0; + } + return 1; + break; + + case SEL_EX: + return 0; + } + + return 0; +} + +#endif /* ALLOW_SELECT */ + +long +MIDIbuf_init (long mem_start) +{ + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c new file mode 100644 index 00000000000..40be06638ac --- /dev/null +++ b/sys/i386/isa/sound/mpu401.c @@ -0,0 +1,1722 @@ +/* + * sound/mpu401.c + * + * The low level driver for Roland MPU-401 compatible Midi cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI) + +static int init_sequence[20]; /* NOTE! pos 0 = len, start pos 1. */ +static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; + +struct mpu_config + { + int base; /* + * I/O base + */ + int irq; + int opened; /* + * Open mode + */ + int devno; + int synthno; + int uart_mode; + int initialized; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + unsigned char version, revision; + unsigned int capabilities; +#define MPU_CAP_INTLG 0x10000000 +#define MPU_CAP_SYNC 0x00000010 +#define MPU_CAP_FSK 0x00000020 +#define MPU_CAP_CLS 0x00000040 +#define MPU_CAP_SMPTE 0x00000080 +#define MPU_CAP_2PORT 0x00000001 + int timer_flag; + +#define MBUF_MAX 10 +#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ + {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} + int m_busy; + unsigned char m_buf[MBUF_MAX]; + int m_ptr; + int m_state; + int m_left; + unsigned char last_status; + void (*inputintr) (int dev, unsigned char data); + unsigned short controls[32]; + }; + +#define DATAPORT(base) (base) +#define COMDPORT(base) (base+1) +#define STATPORT(base) (base+1) + +#define mpu401_status(base) INB(STATPORT(base)) +#define input_avail(base) (!(mpu401_status(base)&INPUT_AVAIL)) +#define output_ready(base) (!(mpu401_status(base)&OUTPUT_READY)) +#define write_command(base, cmd) OUTB(cmd, COMDPORT(base)) +#define read_data(base) INB(DATAPORT(base)) + +#define write_data(base, byte) OUTB(byte, DATAPORT(base)) + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xF7 +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static struct mpu_config dev_conf[MAX_MIDI_DEV] = +{ + {0}}; + +static int n_mpu_devs = 0; +static int irq2dev[16]; + +static int reset_mpu401 (struct mpu_config *devc); +static void set_uart_mode (int dev, struct mpu_config *devc, int arg); +static void mpu_timer_init (int midi_dev); +static void mpu_timer_interrupt (void); +static void timer_ext_event (struct mpu_config *devc, int event, int parm); + +static struct synth_info mpu_synth_info_proto = +{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; + +/* + * States for the input scanner + */ + +#define ST_INIT 0 /* Ready for timing byte or msg */ +#define ST_TIMED 1 /* Leading timing byte rcvd */ +#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ + +#define ST_SYSMSG 100 /* System message (sysx etc). */ +#define ST_SYSEX 101 /* System exclusive msg */ +#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define ST_SONGSEL 103 /* Song select */ +#define ST_SONGPOS 104 /* Song position pointer */ + +static unsigned char len_tab[] =/* # of data bytes following a status + */ +{ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ +}; + +#define STORE(cmd) \ +if (devc->opened & OPEN_READ) \ +{ \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +static void +do_midi_msg (struct mpu_config *devc, unsigned char *msg, int mlen) +{ + switch (msg[0] & 0xf0) + { + case 0x90: + if (msg[2] != 0) + { + STORE (SEQ_START_NOTE (devc->synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + } + msg[2] = 64; + + case 0x80: + STORE (SEQ_STOP_NOTE (devc->synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xA0: + STORE (SEQ_KEY_PRESSURE (devc->synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xB0: + /* + * Fix the controller value (combine MSB and LSB) + */ + if (msg[1] < 64) + { + int ctrl = msg[1]; + + if (ctrl < 32) + { + devc->controls[ctrl] = (msg[2] & 0x7f) << 7; + } + else + { + ctrl -= 32; + devc->controls[ctrl] = + (devc->controls[ctrl] & ~0x7f) | (msg[2] & 0x7f); + } + STORE (SEQ_CONTROL (devc->synthno, msg[0] & 0x0f, + msg[1], devc->controls[ctrl])); + } + else + STORE (SEQ_CONTROL (devc->synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xC0: + STORE (SEQ_SET_PATCH (devc->synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xD0: + STORE (SEQ_CHN_PRESSURE (devc->synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xE0: + STORE (SEQ_BENDER (devc->synthno, msg[0] & 0x0f, + (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7))); + break; + + default: + printk ("MPU: Unknown midi channel message %02x\n", msg[0]); + } +} + +static int +mpu_input_scanner (struct mpu_config *devc, unsigned char midic) +{ + switch (devc->m_state) + { + case ST_INIT: + switch (midic) + { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + printk (""); + break; + + case 0xfd: + if (devc->timer_flag) + mpu_timer_interrupt (); + break; + + case 0xfe: + return MPU_ACK; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + printk ("", midic & 0x0f); + break; + + case 0xf9: + printk (""); + break; + + case 0xff: + devc->m_state = ST_SYSMSG; + break; + + default: + if (midic <= 0xef) + { + /* printk("mpu time: %d ", midic); */ + devc->m_state = ST_TIMED; + } + else + printk (" ", midic); + } + break; + + case ST_TIMED: + { + int msg = (midic & 0xf0) >> 4; + + devc->m_state = ST_DATABYTE; + if (msg < 8) /* Data byte */ + { + /* printk("midi msg (running status) "); */ + msg = (devc->last_status & 0xf0) >> 4; + msg -= 8; + devc->m_left = len_tab[msg] - 1; + + devc->m_ptr = 2; + devc->m_buf[0] = devc->last_status; + devc->m_buf[1] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg (devc, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + else if (msg == 0xf) /* MPU MARK */ + { + devc->m_state = ST_INIT; + + switch (midic) + { + case 0xf8: + /* printk("NOP "); */ + break; + + case 0xf9: + /* printk("meas end "); */ + break; + + case 0xfc: + /* printk("data end "); */ + break; + + default: + printk ("Unknown MPU mark %02x\n", midic); + } + } + else + { + devc->last_status = midic; + /* printk("midi msg "); */ + msg -= 8; + devc->m_left = len_tab[msg]; + + devc->m_ptr = 1; + devc->m_buf[0] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg (devc, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + } + break; + + case ST_SYSMSG: + switch (midic) + { + case 0xf0: + printk (""); + devc->m_state = ST_SYSEX; + break; + + case 0xf1: + devc->m_state = ST_MTC; + break; + + case 0xf2: + devc->m_state = ST_SONGPOS; + devc->m_ptr = 0; + break; + + case 0xf3: + devc->m_state = ST_SONGSEL; + break; + + case 0xf6: + /* printk("tune_request\n"); */ + devc->m_state = ST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + devc->m_state = ST_INIT; + timer_ext_event (devc, TMR_CLOCK, 0); + break; + + case 0xfA: + devc->m_state = ST_INIT; + timer_ext_event (devc, TMR_START, 0); + break; + + case 0xFB: + devc->m_state = ST_INIT; + timer_ext_event (devc, TMR_CONTINUE, 0); + break; + + case 0xFC: + devc->m_state = ST_INIT; + timer_ext_event (devc, TMR_STOP, 0); + break; + + case 0xFE: + /* active sensing */ + devc->m_state = ST_INIT; + break; + + case 0xff: + /* printk("midi hard reset"); */ + devc->m_state = ST_INIT; + break; + + default: + printk ("unknown MIDI sysmsg %0x\n", midic); + devc->m_state = ST_INIT; + } + break; + + case ST_MTC: + devc->m_state = ST_INIT; + printk ("MTC frame %x02\n", midic); + break; + + case ST_SYSEX: + if (midic == 0xf7) + { + printk (""); + devc->m_state = ST_INIT; + } + else + printk ("%02x ", midic); + break; + + case ST_SONGPOS: + BUFTEST (devc); + devc->m_buf[devc->m_ptr++] = midic; + if (devc->m_ptr == 2) + { + devc->m_state = ST_INIT; + devc->m_ptr = 0; + timer_ext_event (devc, TMR_SPP, + ((devc->m_buf[1] & 0x7f) << 7) | + (devc->m_buf[0] & 0x7f)); + } + break; + + case ST_DATABYTE: + BUFTEST (devc); + devc->m_buf[devc->m_ptr++] = midic; + if ((--devc->m_left) <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg (devc, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + break; + + default: + printk ("Bad state %d ", devc->m_state); + devc->m_state = ST_INIT; + } + + return 1; +} + +static void +mpu401_input_loop (struct mpu_config *devc) +{ + unsigned long flags; + int busy; + + DISABLE_INTR (flags); + busy = devc->m_busy; + devc->m_busy = 1; + RESTORE_INTR (flags); + + if (busy) + return; + + while (input_avail (devc->base)) + { + unsigned char c = read_data (devc->base); + + if (devc->mode == MODE_SYNTH) + { + mpu_input_scanner (devc, c); + } + else if (devc->opened & OPEN_READ && devc->inputintr != NULL) + devc->inputintr (devc->devno, c); + } + + devc->m_busy = 0; +} + +void +mpuintr (int irq) +{ + struct mpu_config *devc; + int dev; + +#ifdef linux + sti (); +#endif + + if (irq < 1 || irq > 15) + { + printk ("MPU-401: Interrupt #%d?\n", irq); + return; + } + + dev = irq2dev[irq]; + if (dev == -1) + { + printk ("MPU-401: Interrupt #%d?\n", irq); + return; + } + + devc = &dev_conf[dev]; + + if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) + if (input_avail (devc->base)) + mpu401_input_loop (devc); + +} + +static int +mpu401_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int err; + struct mpu_config *devc; + + if (dev < 0 || dev >= num_midis) + return RET_ERROR (ENXIO); + + devc = &dev_conf[dev]; + + if (devc->opened) + { + printk ("MPU-401: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + irq2dev[devc->irq] = dev; + if ((err = snd_set_irq_handler (devc->irq, mpuintr) < 0)) + return err; + + set_uart_mode (dev, devc, 1); + devc->mode = MODE_MIDI; + devc->synthno = 0; + + mpu401_input_loop (devc); + + devc->inputintr = input; + devc->opened = mode; + + return 0; +} + +static void +mpu401_close (int dev) +{ + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->uart_mode) + reset_mpu401 (devc); /* + * This disables the UART mode + */ + devc->mode = 0; + + snd_release_irq (devc->irq); + devc->inputintr = NULL; + irq2dev[devc->irq] = -1; + devc->opened = 0; +} + +static int +mpu401_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + struct mpu_config *devc; + + devc = &dev_conf[dev]; + +#if 0 + /* + * Test for input since pending input seems to block the output. + */ + + if (input_avail (devc->base)) + mpu401_input_loop (devc); +#endif + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready (devc->base); timeout--); /* + * Wait + */ + + DISABLE_INTR (flags); + if (!output_ready (devc->base)) + { + printk ("MPU-401: Send data timeout\n"); + RESTORE_INTR (flags); + return 0; + } + + write_data (devc->base, midi_byte); + RESTORE_INTR (flags); + return 1; +} + +static int +mpu401_command (int dev, mpu_command_rec * cmd) +{ + int i, timeout, ok; + int ret = 0; + unsigned long flags; + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->uart_mode) /* + * Not possible in UART mode + */ + { + printk ("MPU-401 commands not possible in the UART mode\n"); + return RET_ERROR (EINVAL); + } + + /* + * Test for input since pending input seems to block the output. + */ + if (input_avail (devc->base)) + mpu401_input_loop (devc); + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 500000; timeout > 0 && !output_ready (devc->base); timeout--); + + DISABLE_INTR (flags); + if (!output_ready (devc->base)) + { + printk ("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd); + RESTORE_INTR (flags); + return RET_ERROR (EIO); + } + + write_command (devc->base, cmd->cmd); + ok = 0; + for (timeout = 500000; timeout > 0 && !ok; timeout--) + if (input_avail (devc->base)) + if (mpu_input_scanner (devc, read_data (devc->base)) == MPU_ACK) + ok = 1; + + if (!ok) + { + RESTORE_INTR (flags); + printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); + return RET_ERROR (EIO); + } + + if (cmd->nr_args) + for (i = 0; i < cmd->nr_args; i++) + { + for (timeout = 30000; timeout > 0 && !output_ready (devc->base); timeout--); + + if (!mpu401_out (dev, cmd->data[i])) + { + RESTORE_INTR (flags); + printk ("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd); + return RET_ERROR (EIO); + } + } + + ret = 0; + cmd->data[0] = 0; + + if (cmd->nr_returns) + for (i = 0; i < cmd->nr_returns; i++) + { + ok = 0; + for (timeout = 5000; timeout > 0 && !ok; timeout--) + if (input_avail (devc->base)) + { + cmd->data[i] = read_data (devc->base); + ok = 1; + } + + if (!ok) + { + RESTORE_INTR (flags); + printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); + return RET_ERROR (EIO); + } + } + + RESTORE_INTR (flags); + + return ret; +} + +static int +exec_cmd (int dev, int cmd, int data) +{ + int ret; + + static mpu_command_rec rec; + + rec.cmd = cmd & 0xff; + rec.nr_args = ((cmd & 0xf0) == 0xE0); + rec.nr_returns = ((cmd & 0xf0) == 0xA0); + rec.data[0] = data & 0xff; + + if ((ret = mpu401_command (dev, &rec)) < 0) + return ret; + return (unsigned char) rec.data[0]; +} + +static int +mpu401_prefix_cmd (int dev, unsigned char status) +{ + struct mpu_config *devc = &dev_conf[dev]; + + if (devc->uart_mode) + return 1; + + if (status < 0xf0) + { + if (exec_cmd (dev, 0xD0, 0) < 0) + return 0; + + return 1; + } + + switch (status) + { + case 0xF0: + if (exec_cmd (dev, 0xDF, 0) < 0) + return 0; + + return 1; + break; + + default: + return 0; + } + + return 0; +} + +static int +mpu401_start_read (int dev) +{ + return 0; +} + +static int +mpu401_end_read (int dev) +{ + return 0; +} + +static int +mpu401_ioctl (int dev, unsigned cmd, unsigned arg) +{ + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + switch (cmd) + { + case 1: + IOCTL_FROM_USER ((char *) &init_sequence, (char *) arg, 0, sizeof (init_sequence)); + return 0; + break; + + case SNDCTL_MIDI_MPUMODE: + if (devc->version == 0) + { + printk ("MPU-401: Intelligent mode not supported by the HW\n"); + return RET_ERROR (EINVAL); + } + set_uart_mode (dev, devc, !IOCTL_IN (arg)); + return 0; + break; + + case SNDCTL_MIDI_MPUCMD: + { + int ret; + mpu_command_rec rec; + + IOCTL_FROM_USER ((char *) &rec, (char *) arg, 0, sizeof (rec)); + + if ((ret = mpu401_command (dev, &rec)) < 0) + return ret; + + IOCTL_TO_USER ((char *) arg, 0, (char *) &rec, sizeof (rec)); + return 0; + } + break; + + default: + return RET_ERROR (EINVAL); + } +} + +static void +mpu401_kick (int dev) +{ +} + +static int +mpu401_buffer_status (int dev) +{ + return 0; /* + * No data in buffers + */ +} + +static int +mpu_synth_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + int midi_dev; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis) + return RET_ERROR (ENXIO); + + devc = &dev_conf[midi_dev]; + + switch (cmd) + { + + case SNDCTL_SYNTH_INFO: + IOCTL_TO_USER ((char *) arg, 0, &mpu_synth_info[midi_dev], + sizeof (struct synth_info)); + + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + default: + return RET_ERROR (EINVAL); + } +} + +static int +mpu_synth_open (int dev, int mode) +{ + int midi_dev, err; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis) + return RET_ERROR (ENXIO); + + devc = &dev_conf[midi_dev]; + + if (devc->opened) + { + printk ("MPU-401: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + devc->opened = mode; + devc->mode = MODE_SYNTH; + devc->synthno = dev; + + devc->inputintr = NULL; + irq2dev[devc->irq] = midi_dev; + if ((err = snd_set_irq_handler (devc->irq, mpuintr) < 0)) + return err; + + reset_mpu401 (devc); + + if (mode & OPEN_READ) + { + exec_cmd (midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ + exec_cmd (midi_dev, 0x8B, 0); /* Enable data in stop mode */ + } + + return 0; +} + +static void +mpu_synth_close (int dev) +{ + int midi_dev; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + devc = &dev_conf[midi_dev]; + exec_cmd (midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ + exec_cmd (midi_dev, 0x8a, 0); /* Disable data in stopped mode */ + + devc->opened = 0; + devc->mode = 0; + snd_release_irq (devc->irq); + devc->inputintr = NULL; + irq2dev[devc->irq] = -1; +} + +#define MIDI_SYNTH_NAME "MPU-401 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations mpu401_synth_proto = +{ + NULL, + 0, + SYNTH_TYPE_MIDI, + 0, + mpu_synth_open, + mpu_synth_close, + mpu_synth_ioctl, + midi_synth_kill_note, + midi_synth_start_note, + midi_synth_set_instr, + midi_synth_reset, + midi_synth_hw_control, + midi_synth_load_patch, + midi_synth_aftertouch, + midi_synth_controller, + midi_synth_panning, + NULL, + midi_synth_patchmgr, + midi_synth_bender +}; + +static struct synth_operations mpu401_synth_operations[MAX_MIDI_DEV]; + +static struct midi_operations mpu401_midi_proto = +{ + {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + NULL, + mpu401_open, + mpu401_close, + mpu401_ioctl, + mpu401_out, + mpu401_start_read, + mpu401_end_read, + mpu401_kick, + NULL, + mpu401_buffer_status, + mpu401_prefix_cmd +}; + +static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; + +static void +mpu401_chk_version (struct mpu_config *devc) +{ + int tmp; + + devc->version = devc->revision = 0; + + if ((tmp = exec_cmd (num_midis, 0xAC, 0)) < 0) + return; + devc->version = tmp; + + if ((tmp = exec_cmd (num_midis, 0xAD, 0)) < 0) + return; + devc->revision = tmp; +} + +long +attach_mpu401 (long mem_start, struct address_info *hw_config) +{ + int i; + unsigned long flags; + char revision_char; + + struct mpu_config *devc; + + for (i = 0; i < 16; i++) + irq2dev[i] = -1; + + if (num_midis >= MAX_MIDI_DEV) + { + printk ("MPU-401: Too many midi devices detected\n"); + return mem_start; + } + + devc = &dev_conf[num_midis]; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->opened = 0; + devc->uart_mode = 0; + devc->initialized = 0; + devc->version = 0; + devc->revision = 0; + devc->capabilities = 0; + devc->timer_flag = 0; + devc->m_busy = 0; + devc->m_state = ST_INIT; + + for (i = 0; i < 32; i++) + devc->controls[i] = 0x2000; + + if (!reset_mpu401 (devc)) + return mem_start; + + DISABLE_INTR (flags); + mpu401_chk_version (devc); + if (devc->version == 0) + mpu401_chk_version (devc); + RESTORE_INTR (flags); + + if (devc->version == 0) + { + memcpy ((char *) &mpu401_synth_operations[num_midis], + (char *) &std_midi_synth, + sizeof (struct synth_operations)); + } + else + { + devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ + memcpy ((char *) &mpu401_synth_operations[num_midis], + (char *) &mpu401_synth_proto, + sizeof (struct synth_operations)); + } + + memcpy ((char *) &mpu401_midi_operations[num_midis], + (char *) &mpu401_midi_proto, + sizeof (struct midi_operations)); + + mpu401_midi_operations[num_midis].converter = + &mpu401_synth_operations[num_midis]; + + memcpy ((char *) &mpu_synth_info[num_midis], + (char *) &mpu_synth_info_proto, + sizeof (struct synth_info)); + + n_mpu_devs++; + + if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ + { + int ports = (devc->revision & 0x08) ? 32 : 16; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | + MPU_CAP_CLS | MPU_CAP_2PORT; + + revision_char = (devc->revision == 0x7f) ? 'M' : ' '; + printk (" ", + ports, + revision_char); +#ifndef SCO + sprintf (mpu_synth_info[num_midis].name, + "MQX-%d%c MIDI Interface #%d", + ports, + revision_char, + n_mpu_devs); +#endif + } + else + { + + revision_char = devc->revision ? devc->revision + '@' : ' '; + if (devc->revision > ('Z' - '@')) + revision_char = '+'; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; + + printk (" ", + (devc->version & 0xf0) >> 4, + devc->version & 0x0f, + revision_char); +#ifndef SCO + sprintf (mpu_synth_info[num_midis].name, + "MPU-401 %d.%d%c Midi interface #%d", + (devc->version & 0xf0) >> 4, + devc->version & 0x0f, + revision_char, + n_mpu_devs); +#endif + } + +#ifndef SCO + strcpy (mpu401_midi_operations[num_midis].info.name, + mpu_synth_info[num_midis].name); +#endif + + mpu401_synth_operations[num_midis].midi_dev = devc->devno = num_midis; + mpu401_synth_operations[devc->devno].info = + &mpu_synth_info[devc->devno]; + + if (devc->capabilities & MPU_CAP_INTLG) /* Has timer */ + mpu_timer_init (num_midis); + + midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno]; + return mem_start; +} + +static int +reset_mpu401 (struct mpu_config *devc) +{ + unsigned long flags; + int ok, timeout, n; + int timeout_limit; + + /* + * Send the RESET command. Try again if no success at the first time. + * (If the device is in the UART mode, it will not ack the reset cmd). + */ + + ok = 0; + + timeout_limit = devc->initialized ? 30000 : 100000; + devc->initialized = 1; + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) + ok = output_ready (devc->base); + + write_command (devc->base, MPU_RESET); /* + * Send MPU-401 RESET Command + */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) + { + DISABLE_INTR (flags); + if (input_avail (devc->base)) + if (read_data (devc->base) == MPU_ACK) + ok = 1; + RESTORE_INTR (flags); + } + + } + + devc->m_state = ST_INIT; + devc->m_ptr = 0; + devc->m_left = 0; + devc->last_status = 0; + devc->uart_mode = 0; + + return ok; +} + +static void +set_uart_mode (int dev, struct mpu_config *devc, int arg) +{ + + if (!arg && devc->version == 0) + return; + + if ((devc->uart_mode == 0) == (arg == 0)) + return; /* Already set */ + + reset_mpu401 (devc); /* This exits the uart mode */ + + if (arg) + { + if (exec_cmd (dev, UART_MODE_ON, 0) < 0) + { + printk ("MPU%d: Can't enter UART mode\n", devc->devno); + devc->uart_mode = 0; + return; + } + } + devc->uart_mode = arg; + +} + +int +probe_mpu401 (struct address_info *hw_config) +{ + int ok = 0; + struct mpu_config tmp_devc; + + tmp_devc.base = hw_config->io_base; + tmp_devc.irq = hw_config->irq; + tmp_devc.initialized = 0; + + ok = reset_mpu401 (&tmp_devc); + + return ok; +} + +/***************************************************** + * Timer stuff + ****************************************************/ + +#if !defined(EXCLUDE_SEQUENCER) + +static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; +static volatile int curr_tempo, curr_timebase, hw_timebase; +static int max_timebase = 8; /* 8*24=192 ppqn */ +static volatile unsigned long next_event_time; +static volatile unsigned long curr_ticks, curr_clocks; +static unsigned long prev_event_time; +static int metronome_mode; + +static unsigned long +clocks2ticks (unsigned long clocks) +{ + /* + * The MPU-401 supports just a limited set of possible timebase values. + * Since the applications require more choices, the driver has to + * program the HW to do it's best and to convert between the HW and + * actual timebases. + */ + + return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; +} + +static void +set_timebase (int midi_dev, int val) +{ + int hw_val; + + if (val < 48) + val = 48; + if (val > 1000) + val = 1000; + + hw_val = val; + hw_val = (hw_val + 23) / 24; + if (hw_val > max_timebase) + hw_val = max_timebase; + + if (exec_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) + { + printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24); + return; + } + hw_timebase = hw_val * 24; + curr_timebase = val; + +} + +static void +tmr_reset (void) +{ + unsigned long flags; + + DISABLE_INTR (flags); + next_event_time = 0xffffffff; + prev_event_time = 0; + curr_ticks = curr_clocks = 0; + RESTORE_INTR (flags); +} + +static void +set_timer_mode (int midi_dev) +{ + if (timer_mode & TMR_MODE_CLS) + exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ + + if (timer_mode & TMR_INTERNAL) + { + exec_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */ + } + else + { + if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) + { + exec_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */ + exec_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ + } + else if (timer_mode & TMR_MODE_FSK) + exec_cmd (midi_dev, 0x81, 0); /* Use FSK sync */ + } +} + +static void +stop_metronome (int midi_dev) +{ + exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */ +} + +static void +setup_metronome (int midi_dev) +{ + int numerator, denominator; + int clks_per_click, num_32nds_per_beat; + int beats_per_measure; + + numerator = ((unsigned) metronome_mode >> 24) & 0xff; + denominator = ((unsigned) metronome_mode >> 16) & 0xff; + clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; + num_32nds_per_beat = (unsigned) metronome_mode & 0xff; + beats_per_measure = (numerator * 4) >> denominator; + + if (!metronome_mode) + exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */ + else + { + exec_cmd (midi_dev, 0xE4, clks_per_click); + exec_cmd (midi_dev, 0xE6, beats_per_measure); + exec_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */ + } +} + +static int +start_timer (int midi_dev) +{ + tmr_reset (); + set_timer_mode (midi_dev); + + if (tmr_running) + return TIMER_NOT_ARMED; /* Already running */ + + if (timer_mode & TMR_INTERNAL) + { + exec_cmd (midi_dev, 0x02, 0); /* Send MIDI start */ + tmr_running = 1; + return TIMER_NOT_ARMED; + } + else + { + exec_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */ + exec_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */ + exec_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */ + exec_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ + } + + return TIMER_ARMED; +} + +static int +mpu_timer_open (int dev, int mode) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + if (timer_open) + return RET_ERROR (EBUSY); + + tmr_reset (); + curr_tempo = 50; + exec_cmd (midi_dev, 0xE0, 50); + curr_timebase = hw_timebase = 120; + set_timebase (midi_dev, 120); + timer_open = 1; + metronome_mode = 0; + set_timer_mode (midi_dev); + + exec_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */ + exec_cmd (midi_dev, 0x95, 0); /* Enable clock to host */ + + return 0; +} + +static void +mpu_timer_close (int dev) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + timer_open = tmr_running = 0; + exec_cmd (midi_dev, 0x15, 0); /* Stop all */ + exec_cmd (midi_dev, 0x94, 0); /* Disable clock to host */ + exec_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */ + stop_metronome (midi_dev); +} + +static int +mpu_timer_event (int dev, unsigned char *event) +{ + unsigned char command = event[1]; + unsigned long parm = *(unsigned int *) &event[4]; + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + if (tmr_running) + break; + return start_timer (midi_dev); + break; + + case TMR_STOP: + exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome (midi_dev); + tmr_running = 0; + break; + + case TMR_CONTINUE: + if (tmr_running) + break; + exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ + setup_metronome (midi_dev); + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + + if (exec_cmd (midi_dev, 0xE0, parm) < 0) + printk ("MPU: Can't set tempo to %d\n", (int) parm); + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input (event, 8); + break; + + case TMR_TIMESIG: + if (metronome_mode) /* Metronome enabled */ + { + metronome_mode = parm; + setup_metronome (midi_dev); + } + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static unsigned long +mpu_timer_get_time (int dev) +{ + if (!timer_open) + return 0; + + return curr_ticks; +} + +static int +mpu_timer_ioctl (int dev, + unsigned int command, unsigned int arg) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) + { + case SNDCTL_TMR_SOURCE: + { + int parm = IOCTL_IN (arg) & timer_caps; + + if (parm != 0) + { + timer_mode = parm; + + if (timer_mode & TMR_MODE_CLS) + exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ + } + + return IOCTL_OUT (arg, timer_mode); + } + break; + + case SNDCTL_TMR_START: + if (tmr_running) + return 0; + start_timer (midi_dev); + return 0; + break; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome (midi_dev); + return 0; + break; + + case SNDCTL_TMR_CONTINUE: + if (tmr_running) + return 0; + tmr_running = 1; + exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ + return 0; + break; + + case SNDCTL_TMR_TIMEBASE: + { + int val = IOCTL_IN (arg); + + if (val) + set_timebase (midi_dev, val); + + return IOCTL_OUT (arg, curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val = IOCTL_IN (arg); + int ret; + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + if ((ret = exec_cmd (midi_dev, 0xE0, val)) < 0) + { + printk ("MPU: Can't set tempo to %d\n", (int) val); + return ret; + } + + curr_tempo = val; + } + + return IOCTL_OUT (arg, curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + if (IOCTL_IN (arg) != 0) /* Can't change */ + return RET_ERROR (EINVAL); + + return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); + break; + + case SNDCTL_TMR_METRONOME: + metronome_mode = IOCTL_IN (arg); + setup_metronome (midi_dev); + return 0; + break; + + default: + } + + return RET_ERROR (EINVAL); +} + +static void +mpu_timer_arm (int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +static struct sound_timer_operations mpu_timer = +{ + {"MPU-401 Timer", 0}, + 10, /* Priority */ + 0, /* Local device link */ + mpu_timer_open, + mpu_timer_close, + mpu_timer_event, + mpu_timer_get_time, + mpu_timer_ioctl, + mpu_timer_arm +}; + +static void +mpu_timer_interrupt (void) +{ + + if (!timer_open) + return; + + if (!tmr_running) + return; + + curr_clocks++; + curr_ticks = clocks2ticks (curr_clocks); + + if (curr_ticks >= next_event_time) + { + next_event_time = 0xffffffff; + sequencer_timer (); + } +} + +static void +timer_ext_event (struct mpu_config *devc, int event, int parm) +{ + int midi_dev = devc->devno; + + if (!devc->timer_flag) + return; + + switch (event) + { + case TMR_CLOCK: + printk (""); + break; + + case TMR_START: + printk ("Ext MIDI start\n"); + if (!tmr_running) + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome (midi_dev); + next_event_time = 0; + STORE (SEQ_START_TIMER ()); + } + break; + + case TMR_STOP: + printk ("Ext MIDI stop\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 0; + stop_metronome (midi_dev); + STORE (SEQ_STOP_TIMER ()); + } + break; + + case TMR_CONTINUE: + printk ("Ext MIDI continue\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome (midi_dev); + STORE (SEQ_CONTINUE_TIMER ()); + } + break; + + case TMR_SPP: + printk ("Songpos: %d\n", parm); + if (timer_mode & TMR_EXTERNAL) + { + STORE (SEQ_SONGPOS (parm)); + } + break; + } +} + +static void +mpu_timer_init (int midi_dev) +{ + struct mpu_config *devc; + int n; + + devc = &dev_conf[midi_dev]; + + if (timer_initialized) + return; /* There is already a similar timer */ + + timer_initialized = 1; + + mpu_timer.devlink = midi_dev; + dev_conf[midi_dev].timer_flag = 1; + +#if 1 + if (num_sound_timers >= MAX_TIMER_DEV) + n = 0; /* Overwrite the system timer */ + else + n = num_sound_timers++; +#else + n = 0; +#endif + sound_timer_devs[n] = &mpu_timer; + + if (devc->version < 0x20) /* Original MPU-401 */ + timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; + else + { + /* + * The version number 2.0 is used (at least) by the + * MusicQuest cards and the Roland Super-MPU. + * + * MusicQuest has given a special meaning to the bits of the + * revision number. The Super-MPU returns 0. + */ + + if (devc->revision) + timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; + + if (devc->revision & 0x02) + timer_caps |= TMR_MODE_CLS; + +#if 0 + if (devc->revision & 0x04) + timer_caps |= TMR_MODE_SMPTE; +#endif + + if (devc->revision & 0x40) + max_timebase = 10; /* Has the 216 and 240 ppqn modes */ + } + + timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; + +} + +#endif + +#endif + +#endif diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c new file mode 100644 index 00000000000..0acd0c6203a --- /dev/null +++ b/sys/i386/isa/sound/opl3.c @@ -0,0 +1,1225 @@ +/* + * sound/opl3.c + * + * A low level driver for Yamaha YM3812 and OPL-3 -chips + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * Major improvements to the FM handling 30AUG92 by Rob Hooft, + */ +/* + * hooft@chem.ruu.nl + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812) + +#include "opl3.h" + +#define MAX_VOICE 18 +#define OFFS_4OP 11 /* + * * * Definitions for the operators OP3 and + * * OP4 * * begin here */ + +static int opl3_enabled = 0; +static int left_address = 0x388, right_address = 0x388, both_address = 0; + +static int nr_voices = 9; +static int logical_voices[MAX_VOICE] = +{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; + +struct voice_info + { + unsigned char keyon_byte; + long bender; + long bender_range; + unsigned long orig_freq; + unsigned long current_freq; + int mode; + }; + +static struct voice_info voices[MAX_VOICE]; +static struct voice_alloc_info *voice_alloc; +static struct channel_info *chn_info; + +static struct sbi_instrument *instrmap; +static struct sbi_instrument *active_instrument[MAX_VOICE] = +{NULL}; + +static struct synth_info fm_info = +{"OPL-2", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0}; + +static int already_initialized = 0; + +static int opl3_ok = 0; +static int opl3_busy = 0; +static int fm_model = 0; /* + + + * * * * 0=no fm, 1=mono, 2=SB Pro 1, 3=SB + * Pro 2 * * */ + +static int store_instr (int instr_no, struct sbi_instrument *instr); +static void freq_to_fnum (int freq, int *block, int *fnum); +static void opl3_command (int io_addr, unsigned int addr, unsigned int val); +static int opl3_kill_note (int dev, int voice, int note, int velocity); +static unsigned char connection_mask = 0x00; + +void +enable_opl3_mode (int left, int right, int both) +{ + if (opl3_enabled) + return; + + opl3_enabled = 1; + left_address = left; + right_address = right; + both_address = both; + fm_info.capabilities = SYNTH_CAP_OPL3; + fm_info.synth_subtype = FM_TYPE_OPL3; +} + +static void +enter_4op_mode (void) +{ + int i; + static int voices_4op[MAX_VOICE] = + {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17}; + + connection_mask = 0x3f; /* Connect all possible 4 OP voices */ + opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f); + + for (i = 0; i < 3; i++) + physical_voices[i].voice_mode = 4; + for (i = 3; i < 6; i++) + physical_voices[i].voice_mode = 0; + + for (i = 9; i < 12; i++) + physical_voices[i].voice_mode = 4; + for (i = 12; i < 15; i++) + physical_voices[i].voice_mode = 0; + + for (i = 0; i < 12; i++) + logical_voices[i] = voices_4op[i]; + voice_alloc->max_voice = nr_voices = 12; +} + +static int +opl3_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + switch (cmd) + { + + case SNDCTL_FM_LOAD_INSTR: + { + struct sbi_instrument ins; + + IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins)); + + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) + { + printk ("FM Error: Invalid instrument number %d\n", ins.channel); + return RET_ERROR (EINVAL); + } + + pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0); + return store_instr (ins.channel, &ins); + } + break; + + case SNDCTL_SYNTH_INFO: + fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices; + + IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info)); + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + case SNDCTL_FM_4OP_ENABLE: + if (opl3_enabled) + enter_4op_mode (); + return 0; + break; + + default: + return RET_ERROR (EINVAL); + } + +} + +int +opl3_detect (int ioaddr) +{ + /* + * This function returns 1 if the FM chicp is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, stat2; + int i; + + if (already_initialized) + { + return 0; /* + * Do avoid duplicate initializations + */ + } + + if (opl3_enabled) + ioaddr = left_address; + + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* + * Reset + * timers + * 1 + * and + * 2 + */ + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* + * Reset the + * IRQ of FM + * * chicp + */ + + stat1 = INB (ioaddr); /* + * Read status register + */ + + if ((stat1 & 0xE0) != 0x00) + { + return 0; /* + * Should be 0x00 + */ + } + + opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* + * Set timer 1 to + * 0xff + */ + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, + TIMER2_MASK | TIMER1_START); /* + * Unmask and start timer 1 + */ + + /* + * Now we have to delay at least 80 msec + */ + + for (i = 0; i < 50; i++) + tenmicrosec (); /* + * To be sure + */ + + stat2 = INB (ioaddr); /* + * Read status after timers have expired + */ + + /* + * Stop the timers + */ + + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* + * Reset + * timers + * 1 + * and + * 2 + */ + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* + * Reset the + * IRQ of FM + * * chicp + */ + + if ((stat2 & 0xE0) != 0xc0) + { + return 0; /* + * There is no YM3812 + */ + } + + /* + * There is a FM chicp in this address. Now set some default values. + */ + + for (i = 0; i < 9; i++) + opl3_command (ioaddr, KEYON_BLOCK + i, 0); /* + * Note off + */ + + opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); + opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /* + * Melodic mode. + */ + + return 1; +} + +static int +opl3_kill_note (int dev, int voice, int note, int velocity) +{ + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return 0; + + voice_alloc->map[voice] = 0; + + map = &physical_voices[logical_voices[voice]]; + + DEB (printk ("Kill note %d\n", voice)); + + if (map->voice_mode == 0) + return 0; + + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20); + + voices[voice].keyon_byte = 0; + voices[voice].bender = 0; + voices[voice].bender_range = 200; /* + * 200 cents = 2 semitones + */ + voices[voice].orig_freq = 0; + voices[voice].current_freq = 0; + voices[voice].mode = 0; + + return 0; +} + +#define HIHAT 0 +#define CYMBAL 1 +#define TOMTOM 2 +#define SNARE 3 +#define BDRUM 4 +#define UNDEFINED TOMTOM +#define DEFAULT TOMTOM + +static int +store_instr (int instr_no, struct sbi_instrument *instr) +{ + + if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled)) + printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key); + memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr)); + + return 0; +} + +static int +opl3_set_instr (int dev, int voice, int instr_no) +{ + if (voice < 0 || voice >= nr_voices) + return 0; + + if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) + return 0; + + active_instrument[voice] = &instrmap[instr_no]; + return 0; +} + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (RH) + */ +char fm_volume_table[128] = +{-64, -48, -40, -35, -32, -29, -27, -26, /* + * 0 - 7 + */ + -24, -23, -21, -20, -19, -18, -18, -17, /* + * 8 - 15 + */ + -16, -15, -15, -14, -13, -13, -12, -12, /* + * 16 - 23 + */ + -11, -11, -10, -10, -10, -9, -9, -8, /* + * 24 - 31 + */ + -8, -8, -7, -7, -7, -6, -6, -6,/* + * 32 - 39 + */ + -5, -5, -5, -5, -4, -4, -4, -4,/* + * 40 - 47 + */ + -3, -3, -3, -3, -2, -2, -2, -2,/* + * 48 - 55 + */ + -2, -1, -1, -1, -1, 0, 0, 0, /* + * 56 - 63 + */ + 0, 0, 0, 1, 1, 1, 1, 1, /* + * 64 - 71 + */ + 1, 2, 2, 2, 2, 2, 2, 2, /* + * 72 - 79 + */ + 3, 3, 3, 3, 3, 3, 3, 4, /* + * 80 - 87 + */ + 4, 4, 4, 4, 4, 4, 4, 5, /* + * 88 - 95 + */ + 5, 5, 5, 5, 5, 5, 5, 5, /* + * 96 - 103 + */ + 6, 6, 6, 6, 6, 6, 6, 6, /* + * 104 - 111 + */ + 6, 7, 7, 7, 7, 7, 7, 7, /* + * 112 - 119 + */ + 7, 7, 7, 8, 8, 8, 8, 8}; /* + + + * * * * 120 - 127 */ + +static void +calc_vol (unsigned char *regbyte, int volume) +{ + int level = (~*regbyte & 0x3f); + + if (level) + level += fm_volume_table[volume]; + + if (level > 0x3f) + level = 0x3f; + if (level < 0) + level = 0; + + *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); +} + +static void +set_voice_volume (int voice, int volume) +{ + unsigned char vol1, vol2, vol3, vol4; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return; + + map = &physical_voices[logical_voices[voice]]; + + instr = active_instrument[voice]; + + if (!instr) + instr = &instrmap[0]; + + if (instr->channel < 0) + return; + + if (voices[voice].mode == 0) + return; + + if (voices[voice].mode == 2) + { /* + * 2 OP voice + */ + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + + if ((instr->operators[10] & 0x01)) + { /* + * Additive synthesis + */ + calc_vol (&vol1, volume); + calc_vol (&vol2, volume); + } + else + { /* + * FM synthesis + */ + calc_vol (&vol2, volume); + } + + opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /* + * Modulator + * volume + */ + opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /* + * Carrier + * volume + */ + } + else + { /* + * 4 OP voice + */ + int connection; + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + vol3 = instr->operators[OFFS_4OP + 2]; + vol4 = instr->operators[OFFS_4OP + 3]; + + /* + * The connection method for 4 OP voices is defined by the rightmost + * bits at the offsets 10 and 10+OFFS_4OP + */ + + connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + calc_vol (&vol4, volume); /* + * Just the OP 4 is carrier + */ + break; + + case 1: + calc_vol (&vol2, volume); + calc_vol (&vol4, volume); + break; + + case 2: + calc_vol (&vol1, volume); + calc_vol (&vol4, volume); + break; + + case 3: + calc_vol (&vol1, volume); + calc_vol (&vol3, volume); + calc_vol (&vol4, volume); + break; + + default: /* + * Why ?? + */ ; + } + + opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4); + } +} + +static int +opl3_start_note (int dev, int voice, int note, int volume) +{ + unsigned char data, fpc; + int block, fnum, freq, voice_mode; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return 0; + + map = &physical_voices[logical_voices[voice]]; + + if (map->voice_mode == 0) + return 0; + + if (note == 255) /* + * Just change the volume + */ + { + set_voice_volume (voice, volume); + return 0; + } + + /* + * Kill previous note before playing + */ + opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* + * Carrier + * volume to + * min + */ + opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* + * Modulator + * volume to + */ + + if (map->voice_mode == 4) + { + opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff); + } + + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* + * Note + * off + */ + + instr = active_instrument[voice]; + + if (!instr) + instr = &instrmap[0]; + + if (instr->channel < 0) + { + printk ( + "OPL3: Initializing voice %d with undefined instrument\n", + voice); + return 0; + } + + if (map->voice_mode == 2 && instr->key == OPL3_PATCH) + return 0; /* + * Cannot play + */ + + voice_mode = map->voice_mode; + + if (voice_mode == 4) + { + int voice_shift; + + voice_shift = (map->ioaddr == left_address) ? 0 : 3; + voice_shift += map->voice_num; + + if (instr->key != OPL3_PATCH) /* + * Just 2 OP patch + */ + { + voice_mode = 2; + connection_mask &= ~(1 << voice_shift); + } + else + { + connection_mask |= (1 << voice_shift); + } + + opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); + } + + /* + * Set Sound Characteristics + */ + opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); + opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); + + /* + * Set Attack/Decay + */ + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); + + /* + * Set Sustain/Release + */ + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); + + /* + * Set Wave Select + */ + opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); + opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); + + /* + * Set Feedback/Connection + */ + fpc = instr->operators[10]; + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, + fpc); + + /* + * If the voice is a 4 OP one, initialize the operators 3 and 4 also + */ + + if (voice_mode == 4) + { + + /* + * Set Sound Characteristics + */ + opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); + opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); + + /* + * Set Attack/Decay + */ + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); + + /* + * Set Sustain/Release + */ + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); + + /* + * Set Wave Select + */ + opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); + opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); + + /* + * Set Feedback/Connection + */ + fpc = instr->operators[OFFS_4OP + 10]; + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); + } + + voices[voice].mode = voice_mode; + + set_voice_volume (voice, volume); + + freq = voices[voice].orig_freq = note_to_freq (note) / 1000; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); + voices[voice].current_freq = freq; + + freq_to_fnum (freq, &block, &fnum); + + /* + * Play note + */ + + data = fnum & 0xff; /* + * Least significant bits of fnumber + */ + opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + voices[voice].keyon_byte = data; + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); + if (voice_mode == 4) + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); + + return 0; +} + +static void +freq_to_fnum (int freq, int *block, int *fnum) +{ + int f, octave; + + /* + * Converts the note frequency to block and fnum values for the FM chip + */ + /* + * First try to compute the block -value (octave) where the note belongs + */ + + f = freq; + + octave = 5; + + if (f == 0) + octave = 0; + else if (f < 261) + { + while (f < 261) + { + octave--; + f <<= 1; + } + } + else if (f > 493) + { + while (f > 493) + { + octave++; + f >>= 1; + } + } + + if (octave > 7) + octave = 7; + + *fnum = freq * (1 << (20 - octave)) / 49716; + *block = octave; +} + +static void +opl3_command (int io_addr, unsigned int addr, unsigned int val) +{ + int i; + + /* + * The original 2-OP synth requires a quite long delay after writing to a + * register. The OPL-3 survives with just two INBs + */ + + OUTB ((unsigned char) (addr & 0xff), io_addr); /* + * Select register + * + */ + + if (!opl3_enabled) + tenmicrosec (); + else + for (i = 0; i < 2; i++) + INB (io_addr); + + OUTB ((unsigned char) (val & 0xff), io_addr + 1); /* + * Write to register + * + */ + + if (!opl3_enabled) + { + tenmicrosec (); + tenmicrosec (); + tenmicrosec (); + } + else + for (i = 0; i < 2; i++) + INB (io_addr); +} + +static void +opl3_reset (int dev) +{ + int i; + + for (i = 0; i < nr_voices; i++) + { + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff); + + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); + + if (physical_voices[logical_voices[i]].voice_mode == 4) + { + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); + + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); + } + + opl3_kill_note (dev, i, 0, 64); + } + + if (opl3_enabled) + { + voice_alloc->max_voice = nr_voices = 18; + + for (i = 0; i < 18; i++) + logical_voices[i] = i; + + for (i = 0; i < 18; i++) + physical_voices[i].voice_mode = 2; + + } + +} + +static int +opl3_open (int dev, int mode) +{ + if (!opl3_ok) + return RET_ERROR (ENXIO); + if (opl3_busy) + return RET_ERROR (EBUSY); + opl3_busy = 1; + + connection_mask = 0x00; /* + * Just 2 OP voices + */ + if (opl3_enabled) + opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); + return 0; +} + +static void +opl3_close (int dev) +{ + opl3_busy = 0; + voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9; + fm_info.nr_drums = 0; + fm_info.perc_mode = 0; + + opl3_reset (dev); +} + +static void +opl3_hw_control (int dev, unsigned char *event) +{ +} + +static int +opl3_load_patch (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + struct sbi_instrument ins; + + if (count < sizeof (ins)) + { + printk ("FM Error: Patch record too short\n"); + return RET_ERROR (EINVAL); + } + + COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs); + + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) + { + printk ("FM Error: Invalid instrument number %d\n", ins.channel); + return RET_ERROR (EINVAL); + } + ins.key = format; + + return store_instr (ins.channel, &ins); +} + +static void +opl3_panning (int dev, int voice, int pressure) +{ +} + +static void +opl3_volume_method (int dev, int mode) +{ +} + +#define SET_VIBRATO(cell) { \ + tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ + if (pressure > 110) \ + tmp |= 0x40; /* Vibrato on */ \ + opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} + +static void +opl3_aftertouch (int dev, int voice, int pressure) +{ + int tmp; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return; + + map = &physical_voices[logical_voices[voice]]; + + DEB (printk ("Aftertouch %d\n", voice)); + + if (map->voice_mode == 0) + return; + + /* + * Adjust the amount of vibrato depending the pressure + */ + + instr = active_instrument[voice]; + + if (!instr) + instr = &instrmap[0]; + + if (voices[voice].mode == 4) + { + int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + SET_VIBRATO (4); + break; + + case 1: + SET_VIBRATO (2); + SET_VIBRATO (4); + break; + + case 2: + SET_VIBRATO (1); + SET_VIBRATO (4); + break; + + case 3: + SET_VIBRATO (1); + SET_VIBRATO (3); + SET_VIBRATO (4); + break; + + } + /* + * Not implemented yet + */ + } + else + { + SET_VIBRATO (1); + + if ((instr->operators[10] & 0x01)) /* + * Additive synthesis + */ + SET_VIBRATO (2); + } +} + +#undef SET_VIBRATO + +static void +bend_pitch (int dev, int voice, int value) +{ + unsigned char data; + int block, fnum, freq; + struct physical_voice_info *map; + + map = &physical_voices[logical_voices[voice]]; + + if (map->voice_mode == 0) + return; + + voices[voice].bender = value; + if (!value) + return; + if (!(voices[voice].keyon_byte & 0x20)) + return; /* + * Not keyed on + */ + + freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); + voices[voice].current_freq = freq; + + freq_to_fnum (freq, &block, &fnum); + + data = fnum & 0xff; /* + * Least significant bits of fnumber + */ + opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* + * * + * KEYON|OCTAVE|MS + * + * * bits * * + * of * f-num + * + */ + voices[voice].keyon_byte = data; + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); +} + +static void +opl3_controller (int dev, int voice, int ctrl_num, int value) +{ + if (voice < 0 || voice >= nr_voices) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + bend_pitch (dev, voice, value); + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + } +} + +static int +opl3_patchmgr (int dev, struct patmgr_info *rec) +{ + return RET_ERROR (EINVAL); +} + +static void +opl3_bender (int dev, int voice, int value) +{ + if (voice < 0 || voice >= nr_voices) + return; + + bend_pitch (dev, voice, value); +} + +static int +opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, avail_voices; + struct sbi_instrument *instr; + int is4op; + int instr_no; + + if (chn < 0 || chn > 15) + instr_no = 0; + else + instr_no = chn_info[chn].pgm_num; + + instr = &instrmap[instr_no]; + if (instr->channel < 0 || /* Instrument not loaded */ + nr_voices != 12) /* Not in 4 OP mode */ + is4op = 0; + else if (nr_voices == 12) /* 4 OP mode */ + is4op = (instr->key == OPL3_PATCH); + else + is4op = 0; + + if (is4op) + { + p = 0; + avail_voices = 6; + } + else + { + if (nr_voices == 12) /* 4 OP mode. Use the '2 OP only' voices first */ + p = 6; + else + p = 0; + avail_voices = nr_voices; + } + + /* + * Now try to find a free voice + */ + + for (i = 0; i < avail_voices; i++) + { + if (alloc->map[p] == 0) + { + return p; + } + p = (p + 1) % nr_voices; + } + + /* + * Insert some kind of priority mechanism here. + */ + + printk ("OPL3: Out of free voices\n"); + return 0; /* All voices in use. Select the first one. */ +} + +static struct synth_operations opl3_operations = +{ + &fm_info, + 0, + SYNTH_TYPE_FM, + FM_TYPE_ADLIB, + opl3_open, + opl3_close, + opl3_ioctl, + opl3_kill_note, + opl3_start_note, + opl3_set_instr, + opl3_reset, + opl3_hw_control, + opl3_load_patch, + opl3_aftertouch, + opl3_controller, + opl3_panning, + opl3_volume_method, + opl3_patchmgr, + opl3_bender, + opl3_alloc_voice +}; + +long +opl3_init (long mem_start) +{ + int i; + + PERMANENT_MALLOC (struct sbi_instrument *, instrmap, + SBFM_MAXINSTR * sizeof (*instrmap), mem_start); + + if (num_synths >= MAX_SYNTH_DEV) + printk ("OPL3 Error: Too many synthesizers\n"); + else + { + synth_devs[num_synths++] = &opl3_operations; + voice_alloc = &opl3_operations.alloc; + chn_info = &opl3_operations.chn_info[0]; + } + + fm_model = 0; + opl3_ok = 1; + if (opl3_enabled) + { + printk (" "); + fm_model = 2; + voice_alloc->max_voice = nr_voices = 18; + fm_info.nr_drums = 0; + fm_info.capabilities |= SYNTH_CAP_OPL3; +#ifndef SCO + strcpy (fm_info.name, "Yamaha OPL-3"); +#endif + + for (i = 0; i < 18; i++) + if (physical_voices[i].ioaddr == USE_LEFT) + physical_voices[i].ioaddr = left_address; + else + physical_voices[i].ioaddr = right_address; + + + opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /* + * Enable + * OPL-3 + * mode + */ + opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /* + * Select + * all + * 2-OP + * * + * voices + */ + } + else + { + printk (" "); + fm_model = 1; + voice_alloc->max_voice = nr_voices = 9; + fm_info.nr_drums = 0; + + for (i = 0; i < 18; i++) + physical_voices[i].ioaddr = left_address; + }; + + already_initialized = 1; + for (i = 0; i < SBFM_MAXINSTR; i++) + instrmap[i].channel = -1; + + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/opl3.h b/sys/i386/isa/sound/opl3.h new file mode 100644 index 00000000000..ea7901fc60a --- /dev/null +++ b/sys/i386/isa/sound/opl3.h @@ -0,0 +1,260 @@ +/* + * opl3.h - Definitions of the OPL-3 registers + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exeptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + */ + +/* + * Register numbers for the global registers + */ + +#define TEST_REGISTER 0x01 +#define ENABLE_WAVE_SELECT 0x20 + +#define TIMER1_REGISTER 0x02 +#define TIMER2_REGISTER 0x03 +#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ +#define IRQ_RESET 0x80 +#define TIMER1_MASK 0x40 +#define TIMER2_MASK 0x20 +#define TIMER1_START 0x01 +#define TIMER2_START 0x02 + +#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ +#define RIGHT_4OP_0 0x01 +#define RIGHT_4OP_1 0x02 +#define RIGHT_4OP_2 0x04 +#define LEFT_4OP_0 0x08 +#define LEFT_4OP_1 0x10 +#define LEFT_4OP_2 0x20 + +#define OPL3_MODE_REGISTER 0x05 /* Right side */ +#define OPL3_ENABLE 0x01 + +#define KBD_SPLIT_REGISTER 0x08 /* Left side */ +#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define KEYBOARD_SPLIT 0x40 + +#define PERCUSSION_REGISTER 0xbd /* Left side only */ +#define TREMOLO_DEPTH 0x80 +#define VIBRATO_DEPTH 0x40 +#define PERCUSSION_ENABLE 0x20 +#define BASSDRUM_ON 0x10 +#define SNAREDRUM_ON 0x08 +#define TOMTOM_ON 0x04 +#define CYMBAL_ON 0x02 +#define HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ + #define AM_VIB 0x20 + #define TREMOLO_ON 0x80 + #define VIBRATO_ON 0x40 + #define SUSTAIN_ON 0x20 + #define KSR 0x10 /* Key scaling rate */ + #define MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define KSL_LEVEL 0x40 +#define KSL_MASK 0xc0 /* Envelope scaling bits */ +#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define ATTACK_DECAY 0x60 +#define ATTACK_MASK 0xf0 +#define DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define SUSTAIN_RELEASE 0x80 +#define SUSTAIN_MASK 0xf0 +#define RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define WAVE_SELECT 0xe0 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define KEYON_BLOCK 0xb0 +#define KEYON_BIT 0x20 +#define BLOCKNUM_MASK 0x1c +#define FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halfs (gives 4 ways to connect the operators). + */ +#define FEEDBACK_CONNECTION 0xc0 +#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define STEREO_BITS 0x30 /* OPL-3 only */ +#define VOICE_TO_LEFT 0x10 +#define VOICE_TO_RIGHT 0x20 + +/* + * Definition table for the physical voices + */ + +struct physical_voice_info { + unsigned char voice_num; + unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ + unsigned short ioaddr; /* I/O port (left or right side) */ + unsigned char op[4]; /* Operator offsets */ + }; + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + +#define USE_LEFT 0 +#define USE_RIGHT 1 + +static struct physical_voice_info physical_voices[18] = +{ +/* No Mode Side OP1 OP2 OP3 OP4 */ +/* --------------------------------------------------- */ + { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ + { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ + { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ + + { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, + { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, + { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} +}; diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h new file mode 100644 index 00000000000..0b0ac6295c2 --- /dev/null +++ b/sys/i386/isa/sound/os.h @@ -0,0 +1,306 @@ +#ifndef _OS_H_ +#define _OS_H_ +/* + * OS specific settings for 386BSD + * + * This chould be used as an example when porting the driver to a new + * operating systems. + * + * What you should do is to rewrite the soundcard.c and os.h (this file). + * You should create a new subdirectory and put these two files there. + * In addition you have to do a makefile.. + * + * If you have to make changes to other than these two files, please contact me + * before making the changes. It's possible that I have already made the + * change. + */ + +/* + * Insert here the includes required by your kernel. + */ + +#include "param.h" +#include "systm.h" +#include "ioctl.h" +#include "tty.h" +#include "proc.h" +#include "user.h" +#include "conf.h" +#include "file.h" +#include "uio.h" +/* #include "kernel.h" */ +#include "syslog.h" +#include "errno.h" +#include "malloc.h" +#include "buf.h" +#include "i386/isa/isa_device.h" + +/* These few lines are used by 386BSD (only??). */ + +#if NSND > 0 +#define KERNEL_SOUNDCARD +#else +#undef KERNEL_SOUNDCARD +#endif + + +/* + * Rest of the file is compiled only if the driver is really required. + */ +#ifdef CONFIGURE_SOUNDCARD + +/* + * select() is currently implemented in Linux specific way. Don't enable. + * I don't remember what the SHORT_BANNERS means so forget it. + */ + +#undef ALLOW_SELECT +#define SHORT_BANNERS + +/* The soundcard.h could be in a nonstandard place so inclyde it here. */ +#include "soundcard.h" + +/* + * Here is the first portability problem. Every OS has it's own way to + * pass a pointer to the buffer in read() and write() calls. In Linux it's + * just a char*. In BSD it's struct uio. This parameter is passed to + * all functions called from read() or write(). Since nothing can be + * assumed about this structure, the driver uses set of macros for + * accessing the user buffer. + * + * The driver reads/writes bytes in the user buffer sequentially which + * means that calls like uiomove() can be used. + * + * snd_rw_buf is the type which is passed to the device file specific + * read() and write() calls. + * + * The following macros are used to move date to and from the + * user buffer. These macros should be used only when the + * target or source parameter has snd_rw_buf type. + * The offs parameter is a offset relative to the beginning of + * the user buffer. In Linux the offset is required but for example + * BSD passes the offset info in the uio structure. It could be usefull + * if these macros verify that the offs parameter and the value in + * the snd_rw_buf structure are equal. + */ +typedef struct uio snd_rw_buf; + +/* + * Move bytes from the buffer which the application given in a + * write() call. + * offs is position relative to the beginning of the buffer in + * user space. The count is number of bytes to be moved. + */ +#define COPY_FROM_USER(target, source, offs, count) \ + if (uiomove(target, count, source)) { \ + printf ("sb: Bad copyin()!\n"); \ + } else +/* Like COPY_FOM_USER but for writes. */ +#define COPY_TO_USER(target, offs, source, count) \ + if (uiomove(source, count, target)) { \ + printf ("sb: Bad copyout()!\n"); \ + } else +/* + * The following macros are like COPY_*_USER but work just with one byte (8bit), + * short (16 bit) or long (32 bit) at a time. + * The same restrictions apply than for COPY_*_USER + */ +#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, addr);} +#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, addr);} +#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, addr);} +#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, addr);} + +/* + * The way how the ioctl arguments are passed is another nonportable thing. + * In Linux the argument is just a pointer directly to the user segment. On + * 386bsd the data is already moved to the kernel space. The following + * macros should handle the difference. + */ + +/* + * IOCTL_FROM_USER is used to copy a record pointed by the argument to + * a buffer in the kernel space. On 386bsd it can be done just by calling + * memcpy. With Linux a memcpy_from_fs should be called instead. + * Parameters of the following macros are like in the COPY_*_USER macros. + */ + +/* + * When the ioctl argument points to a record or array (longer than 32 bits), + * the macros IOCTL_*_USER are used. It's assumed that the source and target + * parameters are direct memory addresses. + */ +#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);} +#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);} +/* The following macros are used if the ioctl argument points to 32 bit int */ +#define IOCTL_IN(arg) (*(int*)arg) +#define IOCTL_OUT(arg, ret) *(int*)arg = ret + +/* + * When the driver displays something to the console, printk() will be called. + * The name can be changed here. + */ +#define printk printf + +/* + * The following macros define an interface to the process management. + */ + +struct snd_wait { + int mode; int aborting; + }; + +/* + * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define + * a structure which can be passed as a parameter to a sleep(). The second + * parameter is name of a flag variable (must be defined as int). + */ +#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \ + static volatile struct snd_wait flag = {0} +/* Like the above but defines an array of wait queues and flags */ +#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \ + static volatile struct snd_wait flag = {{0}} + +#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;} +#define SET_ABORT_FLAG(q, f) f.aborting = 1 +#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT) +#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP) +/* + * This driver handles interrupts little bit nonstandard way. The following + * macro is used to test if the current process has received a signal which + * is aborts the process. This macro is called from close() to see if the + * buffers should be discarded. If this kind info is not available, a constant + * 1 or 0 could be returned (1 should be better than 0). + * I'm not sure if the following is correct for 386BSD. + */ +#define PROCESS_ABORTING(q, f) (f.aborting | curproc->p_sig) + +/* + * The following macro calls sleep. It should be implemented such that + * the process is resumed if it receives a signal. The following is propably + * not the way how it should be done on 386bsd. + * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(), + * and the second is a workarea parameter. The third is a timeout + * in ticks. Zero means no timeout. + */ +#define DO_SLEEP(q, f, time_limit) \ + { \ + int flag, chn; \ + f.mode = WK_SLEEP; \ + q = &chn; \ + flag=tsleep(&chn, (PRIBIO-5)|PCATCH, "sndint", time_limit); \ + if(flag == ERESTART) f.aborting = 1;\ + else f.aborting = 0;\ + f.mode &= ~WK_SLEEP; \ + } +/* An the following wakes up a process */ +#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wakeup(q);} + +/* + * Timing macros. This driver assumes that there is a timer running in the + * kernel. The timer should return a value which is increased once at every + * timer tick. The macro HZ should return the number of such ticks/sec. + */ + +#ifndef HZ +extern int hz; +#define HZ hz +#endif + +/* + * GET_TIME() returns current value of the counter incremented at timer + * ticks. This can overflow, so the timeout might be real big... + * + */ +unsigned long get_time(void); +#define GET_TIME() get_time() +/*#define GET_TIME() (lbolt) /* Returns current time (1/HZ secs since boot) */ + +/* + * The following three macros are called before and after atomic + * code sequences. The flags parameter has always type of unsigned long. + * The macro DISABLE_INTR() should ensure that all interrupts which + * may invoke any part of the driver (timer, soundcard interrupts) are + * disabled. + * RESTORE_INTR() should return the interrupt status back to the + * state when DISABLE_INTR() was called. The flags parameter is + * a variable which can carry 32 bits of state information between + * DISABLE_INTR() and RESTORE_INTR() calls. + */ +#define DISABLE_INTR(flags) flags = splhigh() +#define RESTORE_INTR(flags) splx(flags) + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +#define INB inb +/* + * The outb(0, 0x80) is just for slowdown. It's bit unsafe since + * this address could be used for something usefull. + */ +#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);} + +/* memcpy() was not defined og 386bsd. Lets define it here */ +#define memcpy(d, s, c) bcopy(s, d, c) + +/* + * When a error (such as EINVAL) is returned by a function, + * the following macro is used. The driver assumes that a + * error is signalled by returning a negative value. + */ + +#define RET_ERROR(err) -(err) + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK) +#define KERNEL_FREE(addr) free(addr, M_TEMP) + +/* + * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) + * returns size bytes of + * (kernel virtual) memory which will never get freed by the driver. + * This macro is called only during boot. The linux_ptr is a linux specific + * parameter which should be ignored in other operating systems. + * The mem_ptr is a pointer variable where the macro assigns pointer to the + * memory area. The type is the type of the mem_ptr. + */ +#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \ + {mem_ptr = (typecast)malloc(size, M_DEVBUF, M_NOWAIT); \ + if (!mem_ptr)panic("SOUND: Cannot allocate memory\n");} + +/* + * The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if + * required. The name is the variable/name to be used and the proc is + * the procedure to be called when the timer expires. + */ + +#define DEFINE_TIMER(name, proc) + +/* + * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks. + */ + +#define ACTIVATE_TIMER(name, proc, time) \ + timeout(proc, 0, time); +/* + * The rest of this file is not complete yet. The functions using these + * macros will not work + */ +#define ALLOC_DMA_CHN(chn) (0) +#define RELEASE_DMA_CHN(chn) (0) +#define DMA_MODE_READ 0 +#define DMA_MODE_WRITE 1 +#define RELEASE_IRQ(irq_no) + +#endif +#endif diff --git a/sys/i386/isa/sound/pas.h b/sys/i386/isa/sound/pas.h new file mode 100644 index 00000000000..70c61dc57f5 --- /dev/null +++ b/sys/i386/isa/sound/pas.h @@ -0,0 +1,250 @@ +/* */ +/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */ +/* */ +/* Feel free to use this header file in any application you create that has support for the Media Vision */ +/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */ +/* */ +/* - cmetz@thor.tjhsst.edu */ +/* */ +/* Notes: */ +/* */ +/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */ +/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */ +/* they don't actually have a direct connection. */ +/* */ +/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */ +/* PAS cards are pretty defunct now, so no attempt is made here to support them. */ +/* */ +/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */ +/* incompatibilities, there still are differences that need to be accounted for. */ +/* */ +/* Card CD-ROM interface PCM chip Mixer chip FM chip */ +/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */ +/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */ +/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */ +/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* */ +#define PAS_DEFAULT_BASE 0x388 + +/* Symbolic Name Value R W Subsystem Description */ +#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */ +#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */ +#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */ +#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */ +#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */ +#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */ + +#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */ +#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */ +#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */ +#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */ +#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */ +#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */ + +#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */ + #define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */ + #define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */ + #define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */ + #define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */ + #define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */ + #define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */ + #define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */ +#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */ + #define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */ + #define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */ +#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */ + #define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */ +#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */ + +#define IO_CONFIGURATION_1 0xF388 /* R W Control */ + #define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */ +#define IO_CONFIGURATION_2 0xF389 /* R W Control */ + #define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */ +#define IO_CONFIGURATION_3 0xF38A /* R W Control */ + #define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */ + +#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */ + #define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */ + #define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */ + #define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */ + #define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */ + #define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */ +#define EMULATION_ADDRESS 0xF789 /* R W Control */ + #define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */ + #define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */ +#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */ + #define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */ + #define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */ + #define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */ + +#define OPERATION_MODE_1 0xEF8B /* R Control */ + #define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */ + #define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */ + #define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */ +#define OPERATION_MODE_2 0xFF8B /* R Control */ + #define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */ + #define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */ + #define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */ + +#define INTERRUPT_MASK 0x0B8B /* R W Control */ + #define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */ + #define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */ + #define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */ + #define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */ + #define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */ + #define I_M_BOARD_REV 0xE0 /* R Control Board revision */ + +#define INTERRUPT_STATUS 0x0B89 /* R W Control */ + #define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */ + #define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */ + #define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */ + #define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */ + #define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */ + #define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */ + #define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */ + #define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */ + +#define FILTER_FREQUENCY 0x0B8A /* R W Control */ + #define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */ +#if 0 + struct { /* R W Mixer Filter translation */ + unsigned int freq:24; + unsigned int value:8; + } F_F_FILTER_translate[] = + { { 73500, 0x01 }, /* 73500Hz - divide by 16 */ + { 65333, 0x02 }, /* 65333Hz - divide by 18 */ + { 49000, 0x09 }, /* 49000Hz - divide by 24 */ + { 36750, 0x11 }, /* 36750Hz - divide by 32 */ + { 24500, 0x19 }, /* 24500Hz - divide by 48 */ + { 18375, 0x07 }, /* 18375Hz - divide by 64 */ + { 12783, 0x0f }, /* 12783Hz - divide by 92 */ + { 12250, 0x04 }, /* 12250Hz - divide by 96 */ + { 9188, 0x17 }, /* 9188Hz - divide by 128 */ + { 6125, 0x1f }, /* 6125Hz - divide by 192 */ + }; +#endif + #define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */ + #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */ + #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */ + +#define PAS_NONE 0 +#define PAS_PLUS 1 +#define PAS_CDPC 2 +#define PAS_16 3 +#define PAS_16D 4 + +#ifdef DEFINE_TRANSLATIONS + unsigned char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */ + { 4, 1, 2, 3, 0, 5, 6, 7 }; + unsigned char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */ + { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 }; + unsigned char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */ + { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 }; + unsigned char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */ + { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 }; + unsigned char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */ + { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 }; + unsigned char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */ + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 }; +#else + extern unsigned char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */ + extern unsigned char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */ + extern unsigned char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */ + extern unsigned char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */ + extern unsigned char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */ + extern unsigned char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */ +#endif + +#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */ + #define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */ + #define P_M_MV508_DATA 0x00 + #define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */ + #define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */ + #define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */ + #define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */ + #define P_M_MV508_VOLUME 0x00 + + #define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */ + #define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */ + + #define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */ + #define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */ + #define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */ + #define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */ + #define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */ + + #define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */ + #define P_M_MV508_ENHANCE_BITS 0x03 + #define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */ + #define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */ + #define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */ + #define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */ + + #define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */ + #define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */ + #define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */ + #define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */ + #define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */ + #define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */ + #define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */ + #define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */ + +#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */ + #define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */ + #define S_M_FM_RESET 0x02 /* R W FM FM chip reset */ + #define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */ + #define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */ + #define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */ + #define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */ + +#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */ + #define P_C_MIXER_CROSS_FIELD 0x0f + #define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */ + #define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */ + #define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */ + #define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */ + #define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */ + #define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */ + #define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */ + #define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */ + #define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */ + #define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */ + +#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */ + #define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */ + #define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */ + #define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */ + + /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */ + #define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */ + #define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */ + + #define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */ + +#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */ +#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */ + +#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */ + #define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */ + #define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */ + #define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */ + #define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */ + #define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */ + #define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */ + #define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */ + #define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */ + +#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */ + #define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */ + #define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */ + #define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */ + #define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */ + #define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */ + #define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */ + #define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */ + #define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */ + +#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */ +#define MIDI_DATA 0x178A /* R W MIDI Midi data register */ +#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */ diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c new file mode 100644 index 00000000000..1bfd73fcc40 --- /dev/null +++ b/sys/i386/isa/sound/pas2_card.c @@ -0,0 +1,407 @@ +#define _PAS2_CARD_C_ +/* + * sound/pas2_card.c + * + * Detection routine for the Pro Audio Spectrum cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS) + +#define DEFINE_TRANSLATIONS +#include "pas.h" + +/* + * The Address Translation code is used to convert I/O register addresses to + * be relative to the given base -register + */ + +int translat_code; +static int pas_intr_mask = 0; +static int pas_irq = 0; + +static char pas_model; +static char *pas_model_names[] = +{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"}; + +/* + * pas_read() and pas_write() are equivalents of INB() and OUTB() + */ +/* + * These routines perform the I/O address translation required + */ +/* + * to support other than the default base address + */ + +unsigned char +pas_read (int ioaddr) +{ + return INB (ioaddr ^ translat_code); +} + +void +pas_write (unsigned char data, int ioaddr) +{ + OUTB (data, ioaddr ^ translat_code); +} + +void +pas2_msg (char *foo) +{ + printk (" PAS2: %s.\n", foo); +} + +/******************* Begin of the Interrupt Handler ********************/ + +void +pasintr (int unused) +{ + int status; + + status = pas_read (INTERRUPT_STATUS); + pas_write (status, INTERRUPT_STATUS); /* + * Clear interrupt + */ + + if (status & I_S_PCM_SAMPLE_BUFFER_IRQ) + { +#ifndef EXCLUDE_AUDIO + pas_pcm_interrupt (status, 1); +#endif + status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ; + } + if (status & I_S_MIDI_IRQ) + { +#ifndef EXCLUDE_MIDI +#ifdef EXCLUDE_PRO_MIDI + pas_midi_interrupt (); +#endif +#endif + status &= ~I_S_MIDI_IRQ; + } + +} + +int +pas_set_intr (int mask) +{ + int err; + + if (!mask) + return 0; + + if (!pas_intr_mask) + { + if ((err = snd_set_irq_handler (pas_irq, pasintr)) < 0) + return err; + } + pas_intr_mask |= mask; + + pas_write (pas_intr_mask, INTERRUPT_MASK); + return 0; +} + +int +pas_remove_intr (int mask) +{ + if (!mask) + return 0; + + pas_intr_mask &= ~mask; + pas_write (pas_intr_mask, INTERRUPT_MASK); + + if (!pas_intr_mask) + { + snd_release_irq (pas_irq); + } + return 0; +} + +/******************* End of the Interrupt handler **********************/ + +/******************* Begin of the Initialization Code ******************/ + +int +config_pas_hw (struct address_info *hw_config) +{ + char ok = 1; + unsigned int_ptrs; /* scsi/sound interrupt pointers */ + + pas_irq = hw_config->irq; + + pas_write (0x00, INTERRUPT_MASK); + + pas_write (0x36, SAMPLE_COUNTER_CONTROL); /* + * Local timer control * + * register + */ + + pas_write (0x36, SAMPLE_RATE_TIMER); /* + * Sample rate timer (16 bit) + */ + pas_write (0, SAMPLE_RATE_TIMER); + + pas_write (0x74, SAMPLE_COUNTER_CONTROL); /* + * Local timer control * + * register + */ + + pas_write (0x74, SAMPLE_BUFFER_COUNTER); /* + * Sample count register (16 + * * bit) + */ + pas_write (0, SAMPLE_BUFFER_COUNTER); + + pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY); + pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL); + pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* + * | + * S_M_OPL3_DUAL_MONO + */ , SERIAL_MIXER); + + pas_write (I_C_1_BOOT_RESET_ENABLE, IO_CONFIGURATION_1); + + if (pas_irq < 0 || pas_irq > 15) + { + printk ("PAS2: Invalid IRQ %d", pas_irq); + ok = 0; + } + else + { + int_ptrs = pas_read (IO_CONFIGURATION_3); + int_ptrs |= I_C_3_PCM_IRQ_translate[pas_irq] & 0xf; + pas_write (int_ptrs, IO_CONFIGURATION_3); + if (!I_C_3_PCM_IRQ_translate[pas_irq]) + { + printk ("PAS2: Invalid IRQ %d", pas_irq); + ok = 0; + } + } + + if (hw_config->dma < 0 || hw_config->dma > 7) + { + printk ("PAS2: Invalid DMA selection %d", hw_config->dma); + ok = 0; + } + else + { + pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2); + if (!I_C_2_PCM_DMA_translate[hw_config->dma]) + { + printk ("PAS2: Invalid DMA selection %d", hw_config->dma); + ok = 0; + } + } + + /* + * This fixes the timing problems of the PAS due to the Symphony chipset + * as per Media Vision. Only define this if your PAS doesn't work correctly. + */ +#ifdef SYMPHONY_PAS + OUTB (0x05, 0xa8); + OUTB (0x60, 0xa9); +#endif + +#ifdef BROKEN_BUS_CLOCK + pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); +#else + /* + * pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); + */ + pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1); +#endif + pas_write (0x18, SYSTEM_CONFIGURATION_3); /* + * ??? + */ + + pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* + * Sets mute + * off and * + * selects + * filter + * rate * of + * 17.897 kHz + */ + + if (pas_model == PAS_16 || pas_model == PAS_16D) + pas_write (8, PRESCALE_DIVIDER); + else + pas_write (0, PRESCALE_DIVIDER); + + pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER); + pas_write (5, PARALLEL_MIXER); + +#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB) + + { + struct address_info *sb_config; + + if ((sb_config = sound_getconf (SNDCARD_SB))) + { + unsigned char irq_dma; + + /* + * Turn on Sound Blaster compatibility + */ + /* + * bit 1 = SB emulation + */ + /* + * bit 0 = MPU401 emulation (CDPC only :-( ) + */ + pas_write (0x02, COMPATIBILITY_ENABLE); + + /* + * "Emulation address" + */ + pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS); + + if (!E_C_SB_DMA_translate[sb_config->dma]) + printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", + sb_config->dma); + + if (!E_C_SB_IRQ_translate[sb_config->irq]) + printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", + sb_config->irq); + + irq_dma = E_C_SB_DMA_translate[sb_config->dma] | + E_C_SB_IRQ_translate[sb_config->irq]; + + pas_write (irq_dma, EMULATION_CONFIGURATION); + } + } +#endif + + if (!ok) + pas2_msg ("Driver not enabled"); + + return ok; +} + +int +detect_pas_hw (struct address_info *hw_config) +{ + unsigned char board_id, foo; + + /* + * WARNING: Setting an option like W:1 or so that disables warm boot reset + * of the card will screw up this detect code something fierce. Adding code + * to handle this means possibly interfering with other cards on the bus if + * you have something on base port 0x388. SO be forewarned. + */ + + OUTB (0xBC, MASTER_DECODE); /* + * Talk to first board + */ + OUTB (hw_config->io_base >> 2, MASTER_DECODE); /* + * Set base address + */ + translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base; + pas_write (1, WAIT_STATE); /* + * One wait-state + */ + + board_id = pas_read (INTERRUPT_MASK); + + if (board_id == 0xff) + return 0; + + /* + * We probably have a PAS-series board, now check for a PAS2-series board + * by trying to change the board revision bits. PAS2-series hardware won't + * let you do this - the bits are read-only. + */ + + foo = board_id ^ 0xe0; + + pas_write (foo, INTERRUPT_MASK); + foo = INB (INTERRUPT_MASK); + pas_write (board_id, INTERRUPT_MASK); + + if (board_id != foo) /* + * Not a PAS2 + */ + return 0; + + pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]; + + return pas_model; +} + +long +attach_pas_card (long mem_start, struct address_info *hw_config) +{ + pas_irq = hw_config->irq; + + if (detect_pas_hw (hw_config)) + { + + if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f])) + { + printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID)); + } + + if (config_pas_hw (hw_config)) + { + +#ifndef EXCLUDE_AUDIO + mem_start = pas_pcm_init (mem_start, hw_config); +#endif + +#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB) + + sb_dsp_disable_midi (); /* + * The SB emulation don't support * + * midi + */ +#endif + +#ifndef EXCLUDE_YM3812 + enable_opl3_mode (0x388, 0x38a, 0); +#endif + +#ifndef EXCLUDE_MIDI +#ifdef EXCLUDE_PRO_MIDI + mem_start = pas_midi_init (mem_start); +#endif +#endif + + pas_init_mixer (); + } + } + + return mem_start; +} + +int +probe_pas (struct address_info *hw_config) +{ + return detect_pas_hw (hw_config); +} + +#endif diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c new file mode 100644 index 00000000000..1c6bf5937a0 --- /dev/null +++ b/sys/i386/isa/sound/pas2_midi.c @@ -0,0 +1,338 @@ +/* + * sound/pas2_midi.c + * + * The low level driver for the PAS Midi Interface. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "pas.h" + +#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI) + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static volatile int ofifo_bytes = 0; + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static int +pas_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int err; + unsigned long flags; + unsigned char ctrl; + + + if (midi_busy) + { + printk ("PAS2: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + /* + * Reset input and output FIFO pointers + */ + pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, + MIDI_CONTROL); + + DISABLE_INTR (flags); + + if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0) + return err; + + /* + * Enable input available and output FIFO empty interrupts + */ + + ctrl = 0; + input_opened = 0; + midi_input_intr = input; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { + ctrl |= M_C_ENA_INPUT_IRQ;/* + * Enable input + */ + input_opened = 1; + } + + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + { + ctrl |= M_C_ENA_OUTPUT_IRQ | /* + * Enable output + */ + M_C_ENA_OUTPUT_HALF_IRQ; + } + + pas_write (ctrl, + MIDI_CONTROL); + + /* + * Acknowledge any pending interrupts + */ + + pas_write (0xff, MIDI_STATUS); + ofifo_bytes = 0; + + RESTORE_INTR (flags); + + midi_busy = 1; + qlen = qhead = qtail = 0; + return 0; +} + +static void +pas_midi_close (int dev) +{ + + /* + * Reset FIFO pointers, disable intrs + */ + pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL); + + pas_remove_intr (I_M_MIDI_IRQ_ENABLE); + midi_busy = 0; +} + +static int +dump_to_midi (unsigned char midi_byte) +{ + int fifo_space, x; + + fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f; + + if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /* + * Fifo + * full + */ + { + return 0; /* + * Upper layer will call again + */ + } + + ofifo_bytes++; + + pas_write (midi_byte, MIDI_DATA); + + return 1; +} + +static int +pas_midi_out (int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + DISABLE_INTR (flags); + + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi (midi_byte)) + return 1; /* + * OK + */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* + * Local queue full + */ + + DISABLE_INTR (flags); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + RESTORE_INTR (flags); + + return 1; +} + +static int +pas_midi_start_read (int dev) +{ + return 0; +} + +static int +pas_midi_end_read (int dev) +{ + return 0; +} + +static int +pas_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +pas_midi_kick (int dev) +{ + ofifo_bytes = 0; +} + +static int +pas_buffer_status (int dev) +{ + return !qlen; +} + +#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations pas_midi_operations = +{ + {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, + &std_midi_synth, + pas_midi_open, + pas_midi_close, + pas_midi_ioctl, + pas_midi_out, + pas_midi_start_read, + pas_midi_end_read, + pas_midi_kick, + NULL, /* + * command + */ + pas_buffer_status, + NULL +}; + +long +pas_midi_init (long mem_start) +{ + if (num_midis >= MAX_MIDI_DEV) + { + printk ("Sound: Too many midi devices detected\n"); + return mem_start; + } + + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &pas_midi_operations; + return mem_start; +} + +void +pas_midi_interrupt (void) +{ + unsigned char stat; + int i, incount; + unsigned long flags; + + stat = pas_read (MIDI_STATUS); + + if (stat & M_S_INPUT_AVAIL) /* + * Input byte available + */ + { + incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /* + * Input FIFO count + */ + if (!incount) + incount = 16; + + for (i = 0; i < incount; i++) + if (input_opened) + { + midi_input_intr (my_dev, pas_read (MIDI_DATA)); + } + else + pas_read (MIDI_DATA); /* + * Flush + */ + } + + if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY)) + { + if (!(stat & M_S_OUTPUT_EMPTY)) + { + ofifo_bytes = 8; + } + else + { + ofifo_bytes = 0; + } + + DISABLE_INTR (flags); + + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + } + + if (stat & M_S_FRAMING_ERROR) + printk ("MIDI framing error\n"); + + if (stat & M_S_OUTPUT_OVERRUN) + { + printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes); + ofifo_bytes = 100; + } + + pas_write (stat, MIDI_STATUS);/* + * Acknowledge interrupts + */ +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c new file mode 100644 index 00000000000..c125b47c181 --- /dev/null +++ b/sys/i386/isa/sound/pas2_mixer.c @@ -0,0 +1,398 @@ +#define _PAS2_MIXER_C_ + +/* + * sound/pas2_mixer.c + * + * Mixer routines for the Pro Audio Spectrum cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS) + +#include "pas.h" + +#define TRACE(what) /* + * * * (what) */ + +extern int translat_code; + +static int rec_devices = (SOUND_MASK_MIC); /* + + + * * * * Default * + * recording * source + * + * * */ +static int mode_control = 0; + +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM) + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \ + SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x3232, /* + * Master Volume + */ + 0x3232, /* + * Bass + */ + 0x3232, /* + * Treble + */ + 0x5050, /* + * FM + */ + 0x4b4b, /* + * PCM + */ + 0x3232, /* + * PC Speaker + */ + 0x4b4b, /* + * Ext Line + */ + 0x4b4b, /* + * Mic + */ + 0x4b4b, /* + * CD + */ + 0x6464, /* + * Recording monitor + */ + 0x4b4b, /* + * SB PCM + */ + 0x6464}; /* + + + * * * * Recording level */ + +static int +mixer_output (int right_vol, int left_vol, int div, int bits, + int mixer /* + * Input or output mixer + */ ) +{ + int left = left_vol * div / 100; + int right = right_vol * div / 100; + + /* + * The Revision D cards have a problem with their MVA508 interface. The + * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and + * MSBs out of the output byte and to do a 16-bit out to the mixer port - + * 1. We don't need to do this because the call to pas_write more than + * compensates for the timing problems. + */ + + if (bits & P_M_MV508_MIXER) + { /* + * Select input or output mixer + */ + left |= mixer; + right |= mixer; + } + + if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE) + { /* + * Bass and trebble are mono devices + */ + pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER); + pas_write (left, PARALLEL_MIXER); + right_vol = left_vol; + } + else + { + pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER); + pas_write (left, PARALLEL_MIXER); + pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER); + pas_write (right, PARALLEL_MIXER); + } + + return (left_vol | (right_vol << 8)); +} + +void +set_mode (int new_mode) +{ + pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); + pas_write (new_mode, PARALLEL_MIXER); + + mode_control = new_mode; +} + +static int +pas_mixer_set (int whichDev, unsigned int level) +{ + int left, right, devmask, changed, i, mixer = 0; + + TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + if (whichDev < SOUND_MIXER_NRDEVICES) + if ((1 << whichDev) & rec_devices) + mixer = P_M_MV508_INPUTMIX; + else + mixer = P_M_MV508_OUTPUTMIX; + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* + * Master volume (0-63) + */ + levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0); + break; + + /* + * Note! Bass and Treble are mono devices. Will use just the left + * channel. + */ + case SOUND_MIXER_BASS: /* + * Bass (0-12) + */ + levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0); + break; + case SOUND_MIXER_TREBLE: /* + * Treble (0-12) + */ + levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0); + break; + + case SOUND_MIXER_SYNTH: /* + * Internal synthesizer (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer); + break; + case SOUND_MIXER_PCM: /* + * PAS PCM (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer); + break; + case SOUND_MIXER_ALTPCM: /* + * SB PCM (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer); + break; + case SOUND_MIXER_SPEAKER: /* + * PC speaker (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer); + break; + case SOUND_MIXER_LINE: /* + * External line (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer); + break; + case SOUND_MIXER_CD: /* + * CD (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer); + break; + case SOUND_MIXER_MIC: /* + * External microphone (0-31) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer); + break; + case SOUND_MIXER_IMIX: /* + * Recording monitor (0-31) (Only available * + * on the Output Mixer) + */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER, + P_M_MV508_OUTPUTMIX); + break; + case SOUND_MIXER_RECLEV: /* + * Recording level (0-15) + */ + levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0); + break; + + case SOUND_MIXER_MUTE: + return 0; + break; + + case SOUND_MIXER_ENHANCE: + i = 0; + level &= 0x7f; + if (level) + i = (level / 20) - 1; + + mode_control &= ~P_M_MV508_ENHANCE_BITS; + mode_control |= P_M_MV508_ENHANCE_BITS; + set_mode (mode_control); + + if (i) + i = (i + 1) * 20; + return i; + break; + + case SOUND_MIXER_LOUD: + mode_control &= ~P_M_MV508_LOUDNESS; + if (level) + mode_control |= P_M_MV508_LOUDNESS; + set_mode (mode_control); + return !!level; /* + * 0 or 1 + */ + break; + + case SOUND_MIXER_RECSRC: + devmask = level & POSSIBLE_RECORDING_DEVICES; + + changed = devmask ^ rec_devices; + rec_devices = devmask; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (changed & (1 << i)) + { + pas_mixer_set (i, levels[i]); + } + return rec_devices; + break; + + default: + return RET_ERROR (EINVAL); + } + + return (levels[whichDev]); +} + +/*****/ + +static int +getmixer (int dev, int chn) +{ + if (chn == P_M_MV508_RIGHT) + { + return (levels[dev] >> 8) & 0x7f; + } + else + { + return levels[dev] & 0x7f; + } +} + +static void +pas_mixer_reset (void) +{ + int foo; + + TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n")); + + for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) + pas_mixer_set (foo, levels[foo]); + + set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40); +} + +int +pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg))); + else + { /* + * Read parameters + */ + + switch (cmd & 0xff) + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, rec_devices); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE)); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, 0); /* + * No special capabilities + */ + break; + + case SOUND_MIXER_MUTE: + return IOCTL_OUT (arg, 0); /* + * No mute yet + */ + break; + + case SOUND_MIXER_ENHANCE: + if (!(mode_control & P_M_MV508_ENHANCE_BITS)) + return IOCTL_OUT (arg, 0); + return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20); + break; + + case SOUND_MIXER_LOUD: + if (mode_control & P_M_MV508_LOUDNESS) + return IOCTL_OUT (arg, 1); + return IOCTL_OUT (arg, 0); + break; + + default: + return IOCTL_OUT (arg, levels[cmd & 0xff]); + } + } + } + return RET_ERROR (EINVAL); +} + +static struct mixer_operations pas_mixer_operations = +{ + pas_mixer_ioctl +}; + +int +pas_init_mixer (void) +{ + pas_mixer_reset (); + + if (num_mixers < MAX_MIXER_DEV) + mixer_devs[num_mixers++] = &pas_mixer_operations; + return 1; +} + +#endif diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c new file mode 100644 index 00000000000..e4c9a63c384 --- /dev/null +++ b/sys/i386/isa/sound/pas2_pcm.c @@ -0,0 +1,453 @@ +#define _PAS2_PCM_C_ +/* + * sound/pas2_pcm.c + * + * The low level driver for the Pro Audio Spectrum ADC/DAC. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "pas.h" + +#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO) + +#define TRACE(WHAT) /* + * * * (WHAT) */ + +#define PAS_PCM_INTRBITS (0x08) +/* + * Sample buffer timer interrupt enable + */ + +#define PCM_NON 0 +#define PCM_DAC 1 +#define PCM_ADC 2 + +static unsigned long pcm_speed = 0; /* sampling rate */ +static unsigned char pcm_channels = 1; /* channels (1 or 2) */ +static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ +static unsigned char pcm_filter = 0; /* filter FLAG */ +static unsigned char pcm_mode = PCM_NON; +static unsigned long pcm_count = 0; +static unsigned short pcm_bitsok = 8; /* mask of OK bits */ +static int my_devnum = 0; + +int +pcm_set_speed (int arg) +{ + int foo, tmp; + unsigned long flags; + + if (arg > 44100) + arg = 44100; + if (arg < 5000) + arg = 5000; + + foo = (1193180 + (arg / 2)) / arg; + arg = 1193180 / foo; + + if (pcm_channels & 2) + foo = foo >> 1; + + pcm_speed = arg; + + tmp = pas_read (FILTER_FREQUENCY); + + /* + * Set anti-aliasing filters according to sample rate. You reall *NEED* + * to enable this feature for all normal recording unless you want to + * experiment with aliasing effects. + * These filters apply to the selected "recording" source. + * I (pfw) don't know the encoding of these 5 bits. The values shown + * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. +*/ +#if !defined NO_AUTO_FILTER_SET + tmp &= 0xe0; + if (pcm_speed >= 2 * 17897) + tmp |= 0x21; + else if (pcm_speed >= 2 * 15909) + tmp |= 0x22; + else if (pcm_speed >= 2 * 11931) + tmp |= 0x29; + else if (pcm_speed >= 2 * 8948) + tmp |= 0x31; + else if (pcm_speed >= 2 * 5965) + tmp |= 0x39; + else if (pcm_speed >= 2 * 2982) + tmp |= 0x24; + pcm_filter = tmp; +#endif + + DISABLE_INTR (flags); + + pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY); + pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write (foo & 0xff, SAMPLE_RATE_TIMER); + pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER); + pas_write (tmp, FILTER_FREQUENCY); + + RESTORE_INTR (flags); + + return pcm_speed; +} + +int +pcm_set_channels (int arg) +{ + + if ((arg != 1) && (arg != 2)) + return pcm_channels; + + if (arg != pcm_channels) + { + pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL); + + pcm_channels = arg; + pcm_set_speed (pcm_speed);/* + * The speed must be reinitialized + */ + } + + return pcm_channels; +} + +int +pcm_set_bits (int arg) +{ + if ((arg & pcm_bitsok) != arg) + return pcm_bits; + + if (arg != pcm_bits) + { + pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); + + pcm_bits = arg; + } + + return pcm_bits; +} + +static int +pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return pcm_set_speed (arg); + return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return pcm_speed; + return IOCTL_OUT (arg, pcm_speed); + break; + + case SNDCTL_DSP_STEREO: + if (local) + return pcm_set_channels (arg + 1) - 1; + return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1); + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return pcm_set_channels (arg); + return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return pcm_channels; + return IOCTL_OUT (arg, pcm_channels); + break; + + case SNDCTL_DSP_SETFMT: + if (local) + return pcm_set_bits (arg); + return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return pcm_bits; + return IOCTL_OUT (arg, pcm_bits); + + case SOUND_PCM_WRITE_FILTER: /* + * NOT YET IMPLEMENTED + */ + if (IOCTL_IN (arg) > 1) + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + break; + + pcm_filter = IOCTL_IN (arg); + case SOUND_PCM_READ_FILTER: + return IOCTL_OUT (arg, pcm_filter); + break; + + default: + return RET_ERROR (EINVAL); + } + + return RET_ERROR (EINVAL); +} + +static void +pas_pcm_reset (int dev) +{ + TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n")); + + pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); +} + +static int +pas_pcm_open (int dev, int mode) +{ + int err; + + TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode)); + + if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0) + return err; + + if (DMAbuf_open_dma (dev) < 0) + { + pas_remove_intr (PAS_PCM_INTRBITS); + return RET_ERROR (EBUSY); + } + + pcm_count = 0; + + return 0; +} + +static void +pas_pcm_close (int dev) +{ + unsigned long flags; + + TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n")); + + DISABLE_INTR (flags); + + pas_pcm_reset (dev); + DMAbuf_close_dma (dev); + pas_remove_intr (PAS_PCM_INTRBITS); + pcm_mode = PCM_NON; + + RESTORE_INTR (flags); +} + +static void +pas_pcm_output_block (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) +{ + unsigned long flags, cnt; + + TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmachan > 3) + cnt >>= 1; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; /* + * Auto mode on. No need to react + */ + + DISABLE_INTR (flags); + + pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, + PCM_CONTROL); + + if (restart_dma) + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + + if (audio_devs[dev]->dmachan > 3) + count >>= 1; + + if (count != pcm_count) + { + pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + + pcm_count = count; + } + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); + pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL); + + pcm_mode = PCM_DAC; + + RESTORE_INTR (flags); +} + +static void +pas_pcm_start_input (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) +{ + unsigned long flags; + int cnt; + + TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmachan > 3) + cnt >>= 1; + + if (audio_devs[my_devnum]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; /* + * Auto mode on. No need to react + */ + + DISABLE_INTR (flags); + + if (restart_dma) + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + + if (audio_devs[dev]->dmachan > 3) + count >>= 1; + + if (count != pcm_count) + { + pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + + pcm_count = count; + } + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); + pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL); + + pcm_mode = PCM_ADC; + + RESTORE_INTR (flags); +} + +static int +pas_pcm_prepare_for_input (int dev, int bsize, int bcount) +{ + return 0; +} +static int +pas_pcm_prepare_for_output (int dev, int bsize, int bcount) +{ + return 0; +} + +static struct audio_operations pas_pcm_operations = +{ + "Pro Audio Spectrum", + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + pas_pcm_open, + pas_pcm_close, + pas_pcm_output_block, + pas_pcm_start_input, + pas_pcm_ioctl, + pas_pcm_prepare_for_input, + pas_pcm_prepare_for_output, + pas_pcm_reset, + pas_pcm_reset, + NULL, + NULL +}; + +long +pas_pcm_init (long mem_start, struct address_info *hw_config) +{ + TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start)); + + pcm_bitsok = 8; + if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE) + pcm_bitsok |= 16; + + pcm_set_speed (DSP_DEFAULT_SPEED); + + if (num_audiodevs < MAX_AUDIO_DEV) + { + audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations; + audio_devs[my_devnum]->dmachan = hw_config->dma; + audio_devs[my_devnum]->buffcount = 1; + audio_devs[my_devnum]->buffsize = 2 * DSP_BUFFSIZE; + } + else + printk ("PAS2: Too many PCM devices available\n"); + + return mem_start; +} + +void +pas_pcm_interrupt (unsigned char status, int cause) +{ + if (cause == 1) /* + * PCM buffer done + */ + { + /* + * Halt the PCM first. Otherwise we don't have time to start a new + * block before the PCM chip proceeds to the next sample + */ + + if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE)) + { + pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, + PCM_CONTROL); + } + + switch (pcm_mode) + { + + case PCM_DAC: + DMAbuf_outputintr (my_devnum, 1); + break; + + case PCM_ADC: + DMAbuf_inputintr (my_devnum); + break; + + default: + printk ("PAS: Unexpected PCM interrupt\n"); + } + } +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c new file mode 100644 index 00000000000..a7762660910 --- /dev/null +++ b/sys/i386/isa/sound/patmgr.c @@ -0,0 +1,264 @@ +/* + * sound/patmgr.c + * + * The patch maneger interface for the /dev/sequencer + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define PATMGR_C +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER) + +DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV], + server_wait_flag[MAX_SYNTH_DEV]); + +static struct patmgr_info *mbox[MAX_SYNTH_DEV] = +{NULL}; +static volatile int msg_direction[MAX_SYNTH_DEV] = +{0}; + +static int pmgr_opened[MAX_SYNTH_DEV] = +{0}; + +#define A_TO_S 1 +#define S_TO_A 2 + +DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag); + +int +pmgr_open (int dev) +{ + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (pmgr_opened[dev]) + return RET_ERROR (EBUSY); + pmgr_opened[dev] = 1; + + RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]); + + return 0; +} + +void +pmgr_release (int dev) +{ + + if (mbox[dev]) /* + * Killed in action. Inform the client + */ + { + + mbox[dev]->key = PM_ERROR; + mbox[dev]->parm1 = RET_ERROR (EIO); + + if (SOMEONE_WAITING (appl_proc, appl_wait_flag)) + WAKE_UP (appl_proc, appl_wait_flag); + } + + pmgr_opened[dev] = 0; +} + +int +pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned long flags; + int ok = 0; + + if (count != sizeof (struct patmgr_info)) + { + printk ("PATMGR%d: Invalid read count\n", dev); + return RET_ERROR (EIO); + } + + while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev])) + { + DISABLE_INTR (flags); + + while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && + !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev])) + { + DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0); + } + + if (mbox[dev] && msg_direction[dev] == A_TO_S) + { + COPY_TO_USER (buf, 0, (char *) mbox[dev], count); + msg_direction[dev] = 0; + ok = 1; + } + + RESTORE_INTR (flags); + + } + + if (!ok) + return RET_ERROR (EINTR); + return count; +} + +int +pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned long flags; + + if (count < 4) + { + printk ("PATMGR%d: Write count < 4\n", dev); + return RET_ERROR (EIO); + } + + COPY_FROM_USER (mbox[dev], buf, 0, 4); + + if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE) + { + int tmp_dev; + + tmp_dev = ((unsigned short *) mbox[dev])[2]; + if (tmp_dev != dev) + return RET_ERROR (ENXIO); + + return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev], + buf, 4, count, 1); + } + + if (count != sizeof (struct patmgr_info)) + { + printk ("PATMGR%d: Invalid write count\n", dev); + return RET_ERROR (EIO); + } + + /* + * If everything went OK, there should be a preallocated buffer in the + * mailbox and a client waiting. + */ + + DISABLE_INTR (flags); + + if (mbox[dev] && !msg_direction[dev]) + { + COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4); + msg_direction[dev] = S_TO_A; + + if (SOMEONE_WAITING (appl_proc, appl_wait_flag)) + { + WAKE_UP (appl_proc, appl_wait_flag); + } + } + + RESTORE_INTR (flags); + + return count; +} + +int +pmgr_access (int dev, struct patmgr_info *rec) +{ + unsigned long flags; + int err = 0; + + DISABLE_INTR (flags); + + if (mbox[dev]) + printk (" PATMGR: Server %d mbox full. Why?\n", dev); + else + { + rec->key = PM_K_COMMAND; + mbox[dev] = rec; + msg_direction[dev] = A_TO_S; + + if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev])) + { + WAKE_UP (server_procs[dev], server_wait_flag[dev]); + } + + DO_SLEEP (appl_proc, appl_wait_flag, 0); + + if (msg_direction[dev] != S_TO_A) + { + rec->key = PM_ERROR; + rec->parm1 = RET_ERROR (EIO); + } + else if (rec->key == PM_ERROR) + { + err = rec->parm1; + if (err > 0) + err = -err; + } + + mbox[dev] = NULL; + msg_direction[dev] = 0; + } + + RESTORE_INTR (flags); + + return err; +} + +int +pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2, + unsigned long p3, unsigned long p4) +{ + unsigned long flags; + int err = 0; + + if (!pmgr_opened[dev]) + return 0; + + DISABLE_INTR (flags); + + if (mbox[dev]) + printk (" PATMGR: Server %d mbox full. Why?\n", dev); + else + { + mbox[dev] = + (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info)); + + mbox[dev]->key = PM_K_EVENT; + mbox[dev]->command = event; + mbox[dev]->parm1 = p1; + mbox[dev]->parm2 = p2; + mbox[dev]->parm3 = p3; + msg_direction[dev] = A_TO_S; + + if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev])) + { + WAKE_UP (server_procs[dev], server_wait_flag[dev]); + } + + DO_SLEEP (appl_proc, appl_wait_flag, 0); + if (mbox[dev]) + KERNEL_FREE (mbox[dev]); + mbox[dev] = NULL; + msg_direction[dev] = 0; + } + + RESTORE_INTR (flags); + + return err; +} + +#endif diff --git a/sys/i386/isa/sound/pss.c b/sys/i386/isa/sound/pss.c new file mode 100644 index 00000000000..6a0e7a8327f --- /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 .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 (" "); + } + + 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 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 00000000000..e020af6ae9b --- /dev/null +++ b/sys/i386/isa/sound/pss.h @@ -0,0 +1,371 @@ +/****************************************************************************** + + def.h + + Version 1.3 11/2/93 + + Copyright (c) 1993 Analog Devices Inc. All rights reserved + +******************************************************************************/ +/* Port offsets from base port for Sound Blaster DSP */ +#define DSP_PORT_CMSD0 0x00 /* C/MS music voice 1-6 data port, write only */ +#define DSP_PORT_CMSR0 0x01 /* C/MS music voice 1-6 register port, write only */ +#define DSP_PORT_CMSD1 0x02 /* C/MS music voice 7-12 data port, write only */ +#define DSP_PORT_CMSR1 0x03 /* C/MS music voice 7-12 register port, write only */ + +#define DSP_PORT_STATUS 0x04 /* DSP Status bits, read only */ +#define DSP_PORT_CONTROL 0x04 /* DSP Control bits, write only */ +#define DSP_PORT_DATA_LSB 0x05 /* Read or write LSB of 16 bit data */ + + +#define DSP_PORT_RESET 0x06 /* DSP Reset, write only */ +#define DSP_PORT_07h 0x07 /* reserved port */ + +#define DSP_PORT_FMD0 0x08 /* FM music data/status port, read/write */ +#define DSP_PORT_FMR0 0x09 /* FM music data/status port, write only */ + +#define DSP_PORT_RDDATA 0x0A /* DSP Read data, read only reading signals DSP */ +#define DSP_PORT_0Bh 0x0B /* reserved port */ +#define DSP_PORT_WRDATA 0x0C /* DSP Write data or command, write */ +#define DSP_PORT_WRBUSY 0x0C /* DSP Write buffer status (bit 7), read */ +#define DSP_PORT_0Dh 0x0D /* reserved port */ +#define DSP_PORT_DATAAVAIL 0x0E /* DSP Data available status (bit 7), read only */ +#define DSP_PORT_INTERFACE 0x0E /* Sets DMA Channel and Interrupt, write only */ +#define DSP_PORT_0Fh 0x0F /* reserved port (used on Pro cards) */ + +#define ADDR_MASK 0x003f + +#define INT_MASK 0xffc7 +#define INT_3_BITS 0x0008 +#define INT_5_BITS 0x0010 +#define INT_7_BITS 0x0018 +#define INT_9_BITS 0x0020 +#define INT_10_BITS 0x0028 +#define INT_11_BITS 0x0030 +#define INT_12_BITS 0x0038 + +#define GAME_BIT 0x0400 +#define GAME_BIT_MASK 0xfbff + +#define INT_TEST_BIT 0x0200 +#define INT_TEST_PASS 0x0100 +#define INT_TEST_BIT_MASK 0xFDFF + +#define DMA_MASK 0xfff8 +#define DMA_0_BITS 0x0001 +#define DMA_1_BITS 0x0002 +#define DMA_3_BITS 0x0003 +#define DMA_5_BITS 0x0004 +#define DMA_6_BITS 0x0005 +#define DMA_7_BITS 0x0006 + +#define DMA_TEST_BIT 0x0080 +#define DMA_TEST_PASS 0x0040 +#define DMA_TEST_BIT_MASK 0xFF7F + + +/* Echo DSP Flags */ + +#define DSP_FLAG3 0x10 +#define DSP_FLAG2 0x08 +#define DSP_FLAG1 0x80 +#define DSP_FLAG0 0x40 + +#define PSS_CONFIG 0x10 +#define PSS_WSS_CONFIG 0x12 +#define SB_CONFIG 0x14 +#define MIDI_CONFIG 0x18 +#define CD_CONFIG 0x16 +#define UART_CONFIG 0x1a + +#define PSS_DATA 0x00 +#define PSS_STATUS 0x02 +#define PSS_CONTROL 0x02 +#define PSS_ID_VERS 0x04 + +#define PSS_FLAG3 0x0800 +#define PSS_FLAG2 0x0400 +#define PSS_FLAG1 0x1000 +#define PSS_FLAG0 0x0800 + +/*_____ WSS defines */ +#define WSS_BASE_ADDRESS 0x530 +#define WSS_CONFIG 0x0 +#define WSS_VERSION 0x03 +#define WSS_SP0 0x04 +#define WSS_SP1 0x05 +#define WSS_SP2 0x06 +#define WSS_SP3 0x07 + +/*_____ SoundPort register addresses */ + +#define SP_LIN_SOURCE_CTRL 0x00 +#define SP_RIN_SOURCE_CTRL 0x01 +#define SP_LIN_GAIN_CTRL 0x10 +#define SP_RIN_GAIN_CTRL 0x11 +#define SP_LAUX1_CTRL 0x02 +#define SP_RAUX1_CTRL 0x03 +#define SP_LAUX2_CTRL 0x04 +#define SP_RAUX2_CTRL 0x05 +#define SP_LOUT_CTRL 0x06 +#define SP_ROUT_CTRL 0x07 +#define SP_CLK_FORMAT 0x48 +#define SP_INT_CONF 0x09 +#define SP_INT_CONF_MCE 0x49 +#define SP_PIN_CTRL 0x0a +#define SP_TEST_INIT 0x0b +#define SP_MISC_CTRL 0x0c +#define SP_MIX_CTRL 0x0d +#define SP_DMA_UCNT 0x0e +#define SP_DMA_LCNT 0x0f + +/*_____ Gain constants */ + +#define GAIN_0 0x00 +#define GAIN_1_5 0x01 +#define GAIN_3 0x02 +#define GAIN_4_5 0x03 +#define GAIN_6 0x04 +#define GAIN_7_5 0x05 +#define GAIN_9 0x06 +#define GAIN_10_5 0x07 +#define GAIN_12 0x08 +#define GAIN_13_5 0x09 +#define GAIN_15 0x0a +#define GAIN_16_5 0x0b +#define GAIN_18 0x0c +#define GAIN_19_5 0x0d +#define GAIN_21 0x0e +#define GAIN_22_5 0x0f +#define MUTE 0XFFFF + +/*_____ Attenuation constants */ + +#define ATTEN_0 0x00 +#define ATTEN_1_5 0x01 +#define ATTEN_3 0x02 +#define ATTEN_4_5 0x03 +#define ATTEN_6 0x04 +#define ATTEN_7_5 0x05 +#define ATTEN_9 0x06 +#define ATTEN_10_5 0x07 +#define ATTEN_12 0x08 +#define ATTEN_13_5 0x09 +#define ATTEN_15 0x0a +#define ATTEN_16_5 0x0b +#define ATTEN_18 0x0c +#define ATTEN_19_5 0x0d +#define ATTEN_21 0x0e +#define ATTEN_22_5 0x0f + + +#define PSS_WRITE_EMPTY 0x8000 + +#define CD_POL_MASK 0xFFBF +#define CD_POL_BIT 0x0040 + + + +/****************************************************************************** + + host.h + + Version 1.2 9/27/93 + + Copyright (c) 1993 Analog Devices Inc. All rights reserved + +******************************************************************************/ +#define SB_WRITE_FULL 0x80 +#define SB_READ_FULL 0x80 +#define SB_WRITE_STATUS 0x0C +#define SB_READ_STATUS 0x0E +#define SB_READ_DATA 0x0A +#define SB_WRITE_DATA 0x0C + +#define PSS_DATA_REG 0x00 +#define PSS_STATUS_REG 0x02 +#define PSS_WRITE_EMPTY 0x8000 +#define PSS_READ_FULL 0x4000 + +/*_____ 1848 Sound Port bit defines */ + +#define SP_IN_INIT 0x80 +#define MODE_CHANGE_ENABLE 0x40 +#define MODE_CHANGE_MASK 0xbf +#define TRANSFER_DISABLE 0x20 +#define TRANSFER_DISABLE_MASK 0xdf +#define ADDRESS_MASK 0xf0 + +/*_____ Status bits */ +#define INTERRUPT_STATUS 0x01 +#define PLAYBACK_READY 0x02 +#define PLAYBACK_LEFT 0x04 +/*_____ pbright is not left */ +#define PLAYBACK_UPPER 0x08 +/*_____ bplower is not upper */ + +#define SAMPLE_OVERRUN 0x10 +#define SAMPLE_UNDERRUN 0x10 +#define CAPTURE_READY 0x20 +#define CAPTURE_LEFT 0x40 +/*_____ cpright is not left */ +#define CAPTURE_UPPER 0x08 +/*_____ cplower is not upper */ + +/*_____ Input & Output regs bits */ +#define LINE_INPUT 0x80 +#define AUX_INPUT 0x40 +#define MIC_INPUT 0x80 +#define MIXED_DAC_INPUT 0xC0 +#define INPUT_GAIN_MASK 0xf0 +#define INPUT_MIC_GAIN_ENABLE 0x20 +#define INPUT_MIC_GAIN_MASK 0xdf +#define INPUT_SOURCE_MASK 0x3f +#define AUX_INPUT_ATTEN_MASK 0xf0 +#define AUX_INPUT_MUTE 0x80 +#define AUX_INPUT_MUTE_MASK 0x7f +#define OUTPUT_MUTE 0x80 +#define OUTPUT_MUTE_MASK 0x7f +#define OUTPUT_ATTEN_MASK 0xc0 + +/*_____ Clock and Data format reg bits */ +#define CLOCK_SELECT_MASK 0xfe +#define CLOCK_XTAL2 0x01 +#define CLOCK_XTAL1 0x00 +#define CLOCK_FREQ_MASK 0xf1 +#define STEREO_MONO_MASK 0xef +#define STEREO 0x10 +#define AUDIO_MONO 0x00 +#define LINEAR_COMP_MASK 0xdf +#define LINEAR 0x00 +#define COMPANDED 0x20 +#define FORMAT_MASK 0xbf +#define PCM 0x00 +#define ULAW 0x00 +#define TWOS_COMP 0x40 +#define ALAW 0x40 + +/*_____ Interface Configuration reg bits */ +#define PLAYBACK_ENABLE 0x01 +#define PLAYBACK_ENABLE_MASK 0xfe +#define CAPTURE_ENABLE 0x02 +#define CAPTURE_ENABLE_MASK 0xfd +#define SINGLE_DMA 0x04 +#define SINGLE_DMA_MASK 0xfb +#define DUAL_DMA 0x00 +#define AUTO_CAL_ENABLE 0x08 +#define AUTO_CAL_DISABLE_MASK 0xf7 +#define PLAYBACK_PIO_ENABLE 0x40 +#define PLAYBACK_DMA_MASK 0xbf +#define CAPTURE_PIO_ENABLE 0x80 +#define CAPTURE_DMA_MASK 0x7f + +/*_____ Pin control bits */ +#define INTERRUPT_ENABLE 0x02 +#define INTERRUPT_MASK 0xfd + +/*_____ Test and init reg bits */ +#define OVERRANGE_LEFT_MASK 0xfc +#define OVERRANGE_RIGHT_MASK 0xf3 +#define DATA_REQUEST_STATUS 0x10 +#define AUTO_CAL_IN_PROG 0x20 +#define PLAYBACK_UNDERRUN 0x40 +#define CAPTURE_UNDERRUN 0x80 + +/*_____ Miscellaneous Control reg bits */ +#define ID_MASK 0xf0 + +/*_____ Digital Mix Control reg bits */ +#define DIGITAL_MIX1_MUTE_MASK 0xfe +#define MIX_ATTEN_MASK 0x03 + +/*_____ 1848 Sound Port reg defines */ + +#define SP_LEFT_INPUT_CONTROL 0x0 +#define SP_RIGHT_INPUT_CONTROL 0x1 +#define SP_LEFT_AUX1_CONTROL 0x2 +#define SP_RIGHT_AUX1_CONTROL 0x3 +#define SP_LEFT_AUX2_CONTROL 0x4 +#define SP_RIGHT_AUX2_CONTROL 0x5 +#define SP_LEFT_OUTPUT_CONTROL 0x6 +#define SP_RIGHT_OUTPUT_CONTROL 0x7 +#define SP_CLOCK_DATA_FORMAT 0x8 +#define SP_INTERFACE_CONFIG 0x9 +#define SP_PIN_CONTROL 0xA +#define SP_TEST_AND_INIT 0xB +#define SP_MISC_INFO 0xC +#define SP_DIGITAL_MIX 0xD +#define SP_UPPER_BASE_COUNT 0xE +#define SP_LOWER_BASE_COUNT 0xF + +#define HOST_SP_ADDR (0x534) +#define HOST_SP_DATA (0x535) + + +/****************************************************************************** + + phillips.h + + Version 1.2 9/27/93 + + Copyright (c) 1993 Analog Devices Inc. All rights reserved + +******************************************************************************/ +/*_____ Phillips control SW defines */ + +/*_____ Settings and ranges */ +#define VOLUME_MAX 6 +#define VOLUME_MIN (-64) +#define VOLUME_RANGE 70 +#define VOLUME_STEP 2 +#define BASS_MAX 15 +#define BASS_MIN (-12) +#define BASS_STEP 2 +#define BASS_RANGE 27 +#define TREBLE_MAX 12 +#define TREBLE_MIN (-12) +#define TREBLE_STEP 2 +#define TREBLE_RANGE 24 + +#define VOLUME_CONSTANT 252 +#define BASS_CONSTANT 246 +#define TREBLE_CONSTANT 246 + +/*_____ Software commands */ +#define SET_MASTER_COMMAND 0x0010 +#define MASTER_VOLUME_LEFT 0x0000 +#define MASTER_VOLUME_RIGHT 0x0100 +#define MASTER_BASS 0x0200 +#define MASTER_TREBLE 0x0300 +#define MASTER_SWITCH 0x0800 + +#define STEREO_MODE 0x00ce +#define PSEUDO_MODE 0x00d6 +#define SPATIAL_MODE 0x00de +#define MONO_MODE 0x00c6 + + +#define PSS_STEREO 0x00ce +#define PSS_PSEUDO 0x00d6 +#define PSS_SPATIAL 0x00de +#define PSS_MONO 0x00c6 + +#define PHILLIPS_VOL_MIN -64 +#define PHILLIPS_VOL_MAX 6 +#define PHILLIPS_VOL_DELTA 70 +#define PHILLIPS_VOL_INITIAL -20 +#define PHILLIPS_VOL_CONSTANT 252 +#define PHILLIPS_VOL_STEP 2 +#define PHILLIPS_BASS_MIN -12 +#define PHILLIPS_BASS_MAX 15 +#define PHILLIPS_BASS_DELTA 27 +#define PHILLIPS_BASS_INITIAL 0 +#define PHILLIPS_BASS_CONSTANT 246 +#define PHILLIPS_BASS_STEP 2 +#define PHILLIPS_TREBLE_MIN -12 +#define PHILLIPS_TREBLE_MAX 12 +#define PHILLIPS_TREBLE_DELTA 24 +#define PHILLIPS_TREBLE_INITIAL 0 +#define PHILLIPS_TREBLE_CONSTANT 246 +#define PHILLIPS_TREBLE_STEP 2 + diff --git a/sys/i386/isa/sound/sb.h b/sys/i386/isa/sound/sb.h new file mode 100644 index 00000000000..bb8ae12d7e6 --- /dev/null +++ b/sys/i386/isa/sound/sb.h @@ -0,0 +1,28 @@ +#define DSP_RESET (sbc_base + 0x6) +#define DSP_READ (sbc_base + 0xA) +#define DSP_WRITE (sbc_base + 0xC) +#define DSP_COMMAND (sbc_base + 0xC) +#define DSP_STATUS (sbc_base + 0xC) +#define DSP_DATA_AVAIL (sbc_base + 0xE) +#define DSP_DATA_AVL16 (sbc_base + 0xF) +#define MIXER_ADDR (sbc_base + 0x4) +#define MIXER_DATA (sbc_base + 0x5) +#define OPL3_LEFT (sbc_base + 0x0) +#define OPL3_RIGHT (sbc_base + 0x2) +#define OPL3_BOTH (sbc_base + 0x8) +/* DSP Commands */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 +#define DSP_CMD_DMAON 0xD0 +#define DSP_CMD_DMAOFF 0xD4 + +#define IMODE_NONE 0 +#define IMODE_OUTPUT 1 +#define IMODE_INPUT 2 +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 + diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c new file mode 100644 index 00000000000..e3078bca7bf --- /dev/null +++ b/sys/i386/isa/sound/sb16_dsp.c @@ -0,0 +1,568 @@ +/* + * sound/sb16_dsp.c + * + * The low level driver for the SoundBlaster DSP chip. + * + * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de) + * + * based on SB-driver by (C) Hannu Savolainen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define DEB(x) +#define DEB1(x) +/* + * #define DEB_DMARES + */ +#include "sound_config.h" +#include "sb.h" +#include "sb_mixer.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO) + +extern int sbc_base; + +static int sb16_dsp_ok = 0;/* + + + * * * * Set to 1 after successful * + * * initialization */ +static int dsp_16bit = 0; +static int dsp_stereo = 0; +static int dsp_current_speed = 8000; /* + + + * * * * DSP_DEFAULT_SPEED; */ +static int dsp_busy = 0; +static int dma16, dma8; +static unsigned long dsp_count = 0; + +static int irq_mode = IMODE_NONE; /* + + + * * * * IMODE_INPUT, IMODE_OUTPUT + * or * * IMODE_NONE */ +static int my_dev = 0; + +static volatile int intr_active = 0; + +static int sb16_dsp_open (int dev, int mode); +static void sb16_dsp_close (int dev); +static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); +static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); +static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local); +static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount); +static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount); +static void sb16_dsp_reset (int dev); +static void sb16_dsp_halt (int dev); +static int dsp_set_speed (int); +static int dsp_set_stereo (int); +static void dsp_cleanup (void); +int sb_reset_dsp (void); + +static struct audio_operations sb16_dsp_operations = +{ + "SoundBlaster 16", + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + sb16_dsp_open, + sb16_dsp_close, + sb16_dsp_output_block, + sb16_dsp_start_input, + sb16_dsp_ioctl, + sb16_dsp_prepare_for_input, + sb16_dsp_prepare_for_output, + sb16_dsp_reset, + sb16_dsp_halt, + NULL, + NULL +}; + +static int +sb_dsp_command01 (unsigned char val) +{ + int i = 1 << 16; + + while (--i & (!INB (DSP_STATUS) & 0x80)); + if (!i) + printk ("SB16 sb_dsp_command01 Timeout\n"); + return sb_dsp_command (val); +} + +static int +dsp_set_speed (int mode) +{ + DEB (printk ("dsp_set_speed(%d)\n", mode)); + if (mode) + { + if (mode < 5000) + mode = 5000; + if (mode > 44100) + mode = 44100; + dsp_current_speed = mode; + } + return mode; +} + +static int +dsp_set_stereo (int mode) +{ + DEB (printk ("dsp_set_stereo(%d)\n", mode)); + + dsp_stereo = mode; + + return mode; +} + +static int +dsp_set_bits (int arg) +{ + DEB (printk ("dsp_set_bits(%d)\n", arg)); + + if (arg) + switch (arg) + { + case 8: + dsp_16bit = 0; + break; + case 16: + dsp_16bit = 1; + break; + default: + dsp_16bit = 0; + } + return dsp_16bit ? 16 : 8; +} + +static int +sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_set_speed (arg); + return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg))); + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return IOCTL_OUT (arg, dsp_current_speed); + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo (arg); + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo (arg - 1) + 1; + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return IOCTL_OUT (arg, dsp_stereo + 1); + + case SNDCTL_DSP_SETFMT: + if (local) + return dsp_set_bits (arg); + return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg))); + + case SOUND_PCM_READ_BITS: + if (local) + return dsp_16bit ? 16 : 8; + return IOCTL_OUT (arg, dsp_16bit ? 16 : 8); + + case SOUND_PCM_WRITE_FILTER: /* + * NOT YET IMPLEMENTED + */ + if (IOCTL_IN (arg) > 1) + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + default: + return RET_ERROR (EINVAL); + } + + return RET_ERROR (EINVAL); +} + +static int +sb16_dsp_open (int dev, int mode) +{ + int retval; + + DEB (printk ("sb16_dsp_open()\n")); + if (!sb16_dsp_ok) + { + printk ("SB16 Error: SoundBlaster board not installed\n"); + return RET_ERROR (ENXIO); + } + + if (intr_active) + return RET_ERROR (EBUSY); + + retval = sb_get_irq (); + if (retval < 0) + return retval; + + sb_reset_dsp (); + + if (ALLOC_DMA_CHN (dma8)) + { + printk ("SB16: Unable to grab DMA%d\n", dma8); + sb_free_irq (); + return RET_ERROR (EBUSY); + } + + if (dma16 != dma8) + if (ALLOC_DMA_CHN (dma16)) + { + printk ("SB16: Unable to grab DMA%d\n", dma16); + sb_free_irq (); + RELEASE_DMA_CHN (dma8); + return RET_ERROR (EBUSY); + } + + irq_mode = IMODE_NONE; + dsp_busy = 1; + + return 0; +} + +static void +sb16_dsp_close (int dev) +{ + unsigned long flags; + + DEB (printk ("sb16_dsp_close()\n")); + sb_dsp_command01 (0xd9); + sb_dsp_command01 (0xd5); + + DISABLE_INTR (flags); + RELEASE_DMA_CHN (dma8); + + if (dma16 != dma8) + RELEASE_DMA_CHN (dma16); + sb_free_irq (); + dsp_cleanup (); + dsp_busy = 0; + RESTORE_INTR (flags); +} + +static void +sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +{ + unsigned long flags, cnt; + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + +#ifdef DEB_DMARES + printk ("output_block: %x %d %d\n", buf, count, intrflag); + if (intrflag) + { + int pos, chan = audio_devs[dev]->dmachan; + + DISABLE_INTR (flags); + clear_dma_ff (chan); + disable_dma (chan); + pos = get_dma_residue (chan); + enable_dma (chan); + RESTORE_INTR (flags); + printk ("dmapos=%d %x\n", pos, pos); + } +#endif + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == dsp_count) + { + irq_mode = IMODE_OUTPUT; + intr_active = 1; + return; /* + * Auto mode on. No need to react + */ + } + DISABLE_INTR (flags); + + if (dma_restart) + { + sb16_dsp_halt (dev); + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + } + sb_dsp_command (0x41); + sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff)); + sb_dsp_command ((unsigned char) (dsp_16bit ? 0xb6 : 0xc6)); + sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command01 ((unsigned char) (cnt & 0xff)); + sb_dsp_command ((unsigned char) (cnt >> 8)); + + dsp_count = cnt; + irq_mode = IMODE_OUTPUT; + intr_active = 1; + RESTORE_INTR (flags); +} + +static void +sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +{ + unsigned long flags, cnt; + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + +#ifdef DEB_DMARES + printk ("start_input: %x %d %d\n", buf, count, intrflag); + if (intrflag) + { + int pos, chan = audio_devs[dev]->dmachan; + + DISABLE_INTR (flags); + clear_dma_ff (chan); + disable_dma (chan); + pos = get_dma_residue (chan); + enable_dma (chan); + RESTORE_INTR (flags); + printk ("dmapos=%d %x\n", pos, pos); + } +#endif + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == dsp_count) + { + irq_mode = IMODE_INPUT; + intr_active = 1; + return; /* + * Auto mode on. No need to react + */ + } + DISABLE_INTR (flags); + + if (dma_restart) + { + sb_reset_dsp (); + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + } + + sb_dsp_command (0x42); + sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff)); + sb_dsp_command ((unsigned char) (dsp_16bit ? 0xbe : 0xce)); + sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command01 ((unsigned char) (cnt & 0xff)); + sb_dsp_command ((unsigned char) (cnt >> 8)); + + dsp_count = cnt; + irq_mode = IMODE_INPUT; + intr_active = 1; + RESTORE_INTR (flags); +} + +static int +sb16_dsp_prepare_for_input (int dev, int bsize, int bcount) +{ + audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; + dsp_count = 0; + dsp_cleanup (); + return 0; +} + +static int +sb16_dsp_prepare_for_output (int dev, int bsize, int bcount) +{ + audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; + dsp_count = 0; + dsp_cleanup (); + return 0; +} + +static void +dsp_cleanup (void) +{ + irq_mode = IMODE_NONE; + intr_active = 0; +} + +static void +sb16_dsp_reset (int dev) +{ + unsigned long flags; + + DISABLE_INTR (flags); + + sb_reset_dsp (); + dsp_cleanup (); + + RESTORE_INTR (flags); +} + +static void +sb16_dsp_halt (int dev) +{ + if (dsp_16bit) + { + sb_dsp_command01 (0xd9); + sb_dsp_command01 (0xd5); + } + else + { + sb_dsp_command01 (0xda); + sb_dsp_command01 (0xd0); + } +} + +static void +set_irq_hw (int level) +{ + int ival; + + switch (level) + { + case 5: + ival = 2; + break; + case 7: + ival = 4; + break; + case 9: + ival = 1; + break; + case 10: + ival = 8; + break; + default: + printk ("SB16_IRQ_LEVEL %d does not exist\n", level); + return; + } + sb_setmixer (IRQ_NR, ival); +} + +long +sb16_dsp_init (long mem_start, struct address_info *hw_config) +{ + extern int sbc_major, sbc_minor; + + if (sbc_major < 4) + return mem_start; /* Not a SB16 */ + +#ifndef SCO + sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); +#endif + + printk (" <%s>", sb16_dsp_operations.name); + + if (num_audiodevs < MAX_AUDIO_DEV) + { + audio_devs[my_dev = num_audiodevs++] = &sb16_dsp_operations; + audio_devs[my_dev]->dmachan = hw_config->dma; + audio_devs[my_dev]->buffcount = 1; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + } + else + printk ("SB: Too many DSP devices available\n"); + sb16_dsp_ok = 1; + return mem_start; +} + +int +sb16_dsp_detect (struct address_info *hw_config) +{ + struct address_info *sb_config; + extern int sbc_major; + + if (sb16_dsp_ok) + return 1; /* Can't drive two cards */ + + if (!(sb_config = sound_getconf (SNDCARD_SB))) + { + printk ("SB16 Error: Plain SB not configured\n"); + return 0; + } + + /* + * sb_setmixer(OPSW,0xf); if(sb_getmixer(OPSW)!=0xf) return 0; + */ + + if (!sb_reset_dsp ()) + return 0; + + if (sbc_major < 4) /* Set by the plain SB driver */ + return 0; /* Not a SB16 */ + + if (hw_config->dma < 4) + if (hw_config->dma != sb_config->dma) + { + printk ("SB16 Error: Invalid DMA channel %d/%d\n", + sb_config->dma, hw_config->dma); + return 0; + } + + dma16 = hw_config->dma; + dma8 = sb_config->dma; + set_irq_hw (sb_config->irq); + sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma)); + + DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma)); + + /* + * dsp_showmessage(0xe3,99); + */ + sb16_dsp_ok = 1; + return 1; +} + +void +sb16_dsp_interrupt (int unused) +{ + int data; + + data = INB (DSP_DATA_AVL16); /* + * Interrupt acknowledge + */ + + if (intr_active) + switch (irq_mode) + { + case IMODE_OUTPUT: + intr_active = 0; + DMAbuf_outputintr (my_dev, 1); + break; + + case IMODE_INPUT: + intr_active = 0; + DMAbuf_inputintr (my_dev); + break; + + default: + printk ("SoundBlaster: Unexpected interrupt\n"); + } +} + +#endif diff --git a/sys/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c new file mode 100644 index 00000000000..7a3fdb1a3be --- /dev/null +++ b/sys/i386/isa/sound/sb16_midi.c @@ -0,0 +1,304 @@ +/* + * sound/sb16_midi.c + * + * The low level driver for the MPU-401 UART emulation of the SB16. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI) + +#include "sb.h" + +#define DATAPORT (sb16midi_base) +#define COMDPORT (sb16midi_base+1) +#define STATPORT (sb16midi_base+1) + +#define sb16midi_status() INB(STATPORT) +#define input_avail() (!(sb16midi_status()&INPUT_AVAIL)) +#define output_ready() (!(sb16midi_status()&OUTPUT_READY)) +#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT) +#define sb16midi_read() INB(DATAPORT) +#define sb16midi_write(byte) OUTB(byte, DATAPORT) + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static int sb16midi_opened = 0; +static int sb16midi_base = 0x330; +static int sb16midi_detected = 0; +static int my_dev; +extern int sbc_base; + +static int reset_sb16midi (void); +static void (*midi_input_intr) (int dev, unsigned char data); + +static void +sb16midi_input_loop (void) +{ + while (input_avail ()) + { + unsigned char c = sb16midi_read (); + + if (sb16midi_opened & OPEN_READ) + midi_input_intr (my_dev, c); + } +} + +void +sb16midiintr (int unit) +{ + if (input_avail ()) + sb16midi_input_loop (); +} + +static int +sb16midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + if (sb16midi_opened) + { + return RET_ERROR (EBUSY); + } + + sb16midi_input_loop (); + + midi_input_intr = input; + sb16midi_opened = mode; + + return 0; +} + +static void +sb16midi_close (int dev) +{ + sb16midi_opened = 0; +} + +static int +sb16midi_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + DISABLE_INTR (flags); + + if (input_avail ()) + sb16midi_input_loop (); + + RESTORE_INTR (flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* + * Wait + */ + + if (!output_ready ()) + { + printk ("MPU-401: Timeout\n"); + return 0; + } + + sb16midi_write (midi_byte); + return 1; +} + +static int +sb16midi_start_read (int dev) +{ + return 0; +} + +static int +sb16midi_end_read (int dev) +{ + return 0; +} + +static int +sb16midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +sb16midi_kick (int dev) +{ +} + +static int +sb16midi_buffer_status (int dev) +{ + return 0; /* + * No data in buffers + */ +} + +#define MIDI_SYNTH_NAME "SoundBlaster 16 Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations sb16midi_operations = +{ + {"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI}, + &std_midi_synth, + sb16midi_open, + sb16midi_close, + sb16midi_ioctl, + sb16midi_out, + sb16midi_start_read, + sb16midi_end_read, + sb16midi_kick, + NULL, + sb16midi_buffer_status, + NULL +}; + + +long +attach_sb16midi (long mem_start, struct address_info *hw_config) +{ + int ok, timeout; + unsigned long flags; + + sb16midi_base = hw_config->io_base; + + if (!sb16midi_detected) + return RET_ERROR (EIO); + + DISABLE_INTR (flags); + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* + * Wait + */ + sb16midi_cmd (UART_MODE_ON); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_avail ()) + if (sb16midi_read () == MPU_ACK) + ok = 1; + + RESTORE_INTR (flags); + + if (num_midis >= MAX_MIDI_DEV) + { + printk ("Sound: Too many midi devices detected\n"); + return mem_start; + } + + printk (" "); + + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &sb16midi_operations; + return mem_start; +} + +static int +reset_sb16midi (void) +{ + unsigned long flags; + int ok, timeout, n; + + /* + * Send the RESET command. Try again if no success at the first time. + */ + + ok = 0; + + DISABLE_INTR (flags); + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* + * Wait + */ + sb16midi_cmd (MPU_RESET); /* + * Send MPU-401 RESET Command + */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_avail ()) + if (sb16midi_read () == MPU_ACK) + ok = 1; + + } + + sb16midi_opened = 0; + if (ok) + sb16midi_input_loop (); /* + * Flush input before enabling interrupts + */ + + RESTORE_INTR (flags); + + return ok; +} + + +int +probe_sb16midi (struct address_info *hw_config) +{ + int ok = 0; + int i; + extern int sbc_major; + + if (sbc_major < 4) + return 0; /* Not a SB16 */ + + sb16midi_base = hw_config->io_base; + + if (sb_get_irq () < 0) + return 0; + + ok = reset_sb16midi (); + + sb16midi_detected = ok; + return ok; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c new file mode 100644 index 00000000000..f7588e1ca17 --- /dev/null +++ b/sys/i386/isa/sound/sb_card.c @@ -0,0 +1,52 @@ +/* + * sound/sb_card.c + * + * Detection routine for the SoundBlaster cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) + +long +attach_sb_card (long mem_start, struct address_info *hw_config) +{ +#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI) + if (!sb_dsp_detect (hw_config)) + return mem_start; + mem_start = sb_dsp_init (mem_start, hw_config); +#endif + + return mem_start; +} + +int +probe_sb (struct address_info *hw_config) +{ + return sb_dsp_detect (hw_config); +} + +#endif diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c new file mode 100644 index 00000000000..37be35c4cfc --- /dev/null +++ b/sys/i386/isa/sound/sb_dsp.c @@ -0,0 +1,863 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro). + * + * Copyright by Hannu Savolainen 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified: + * Hunyue Yau Jan 6 1994 + * Added code to support Sound Galaxy NX Pro + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) + +#include "sb.h" +#include "sb_mixer.h" +#undef SB_TEST_IRQ + +int sbc_base = 0; +static int sbc_irq = 0; +static int open_mode = 0; /* Read, write or both */ + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + +int sb_dsp_ok = 0; /* + + + * * * * Set to 1 after successful + * initialization * */ +static int midi_disabled = 0; +int sb_dsp_highspeed = 0; +int sbc_major = 1, sbc_minor = 0; /* + + + * * * * DSP version */ +static int dsp_stereo = 0; +static int dsp_current_speed = DSP_DEFAULT_SPEED; +static int sb16 = 0; +static int irq_verified = 0; + +int sb_midi_mode = NORMAL_MIDI; +int sb_midi_busy = 0; /* + + + * * * * 1 if the process has output + * to * * MIDI */ +int sb_dsp_busy = 0; + +volatile int sb_irq_mode = IMODE_NONE; /* + + + * * * * IMODE_INPUT, * + * IMODE_OUTPUT * * or * + * IMODE_NONE */ +static volatile int irq_ok = 0; + +int sb_duplex_midi = 0; +static int my_dev = 0; + +volatile int sb_intr_active = 0; + +static int dsp_speed (int); +static int dsp_set_stereo (int mode); +int sb_dsp_command (unsigned char val); + +#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO) + +/* + * Common code for the midi and pcm functions + */ + +int +sb_dsp_command (unsigned char val) +{ + int i; + unsigned long limit; + + limit = GET_TIME () + HZ / 10;/* + * The timeout is 0.1 secods + */ + + /* + * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 500000 && GET_TIME () < limit; i++) + { + if ((INB (DSP_STATUS) & 0x80) == 0) + { + OUTB (val, DSP_COMMAND); + return 1; + } + } + + printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val); + printk ("IRQ conflict???\n"); + return 0; +} + +void +sbintr (int unit) +{ + int status; + +#ifndef EXCLUDE_SBPRO + if (sb16) + { + unsigned char src = sb_getmixer (IRQ_STAT); /* + + + * * * * Interrupt + * source * * + * register */ + +#ifndef EXCLUDE_SB16 + if (src & 3) + sb16_dsp_interrupt (unit); + +#ifndef EXCLUDE_MIDI + if (src & 4) + sb16midiintr (unit); /* + * SB MPU401 interrupt + */ +#endif + +#endif + + if (!(src & 1)) + return; /* + * Not a DSP interupt + */ + } +#endif + + status = INB (DSP_DATA_AVAIL);/* + * Clear interrupt + */ + + if (sb_intr_active) + switch (sb_irq_mode) + { + case IMODE_OUTPUT: + sb_intr_active = 0; + DMAbuf_outputintr (my_dev, 1); + break; + + case IMODE_INPUT: + sb_intr_active = 0; + DMAbuf_inputintr (my_dev); + /* + * A complete buffer has been input. Let's start new one + */ + break; + + case IMODE_INIT: + sb_intr_active = 0; + irq_ok = 1; + break; + + case IMODE_MIDI: +#ifndef EXCLUDE_MIDI + sb_midi_interrupt (unit); +#endif + break; + + default: + printk ("SoundBlaster: Unexpected interrupt\n"); + } +} + +static int sb_irq_usecount = 0; + +int +sb_get_irq (void) +{ + int ok; + + if (!sb_irq_usecount) + if ((ok = snd_set_irq_handler (sbc_irq, sbintr)) < 0) + return ok; + + sb_irq_usecount++; + + return 0; +} + +void +sb_free_irq (void) +{ + if (!sb_irq_usecount) + return; + + sb_irq_usecount--; + + if (!sb_irq_usecount) + snd_release_irq (sbc_irq); +} + +int +sb_reset_dsp (void) +{ + int loopc; + + OUTB (1, DSP_RESET); + tenmicrosec (); + OUTB (0, DSP_RESET); + tenmicrosec (); + tenmicrosec (); + tenmicrosec (); + + for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /* + * Wait + * for + * data + * * + * available + * status + */ + + if (INB (DSP_READ) != 0xAA) + return 0; /* + * Sorry + */ + + return 1; +} + +#endif + +#ifndef EXCLUDE_AUDIO + +static void +dsp_speaker (char state) +{ + if (state) + sb_dsp_command (DSP_CMD_SPKON); + else + sb_dsp_command (DSP_CMD_SPKOFF); +} + +static int +dsp_speed (int speed) +{ + unsigned char tconst; + unsigned long flags; + int max_speed = 44100; + + if (speed < 4000) + speed = 4000; + + /* + * Older SB models don't support higher speeds than 22050. + */ + + if (sbc_major < 2 || + (sbc_major == 2 && sbc_minor == 0)) + max_speed = 22050; + + /* + * SB models earlier than SB Pro have low limit for the input speed. + */ + if (open_mode != OPEN_WRITE) /* Recording is possible */ + if (sbc_major < 3) /* Limited input speed with these cards */ + if (sbc_major == 2 && sbc_minor > 0) + max_speed = 15000; + else + max_speed = 13000; + + if (speed > max_speed) + speed = max_speed; /* + * Invalid speed + */ + + if (dsp_stereo && speed > 22050) + speed = 22050; + /* + * Max. stereo speed is 22050 + */ + + if ((speed > 22050) && sb_midi_busy) + { + printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); + speed = 22050; + } + + if (dsp_stereo) + speed *= 2; + + /* + * Now the speed should be valid + */ + + if (speed > 22050) + { /* + * High speed mode + */ + int tmp; + + tconst = (unsigned char) ((65536 - + ((256000000 + speed / 2) / speed)) >> 8); + sb_dsp_highspeed = 1; + + DISABLE_INTR (flags); + if (sb_dsp_command (0x40)) + sb_dsp_command (tconst); + RESTORE_INTR (flags); + + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; + } + else + { + int tmp; + + sb_dsp_highspeed = 0; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + + DISABLE_INTR (flags); + if (sb_dsp_command (0x40))/* + * Set time constant + */ + sb_dsp_command (tconst); + RESTORE_INTR (flags); + + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; + } + + if (dsp_stereo) + speed /= 2; + + dsp_current_speed = speed; + return speed; +} + +static int +dsp_set_stereo (int mode) +{ + dsp_stereo = 0; + +#ifdef EXCLUDE_SBPRO + return 0; +#else + if (sbc_major < 3 || sb16) + return 0; /* + * Sorry no stereo + */ + + if (mode && sb_midi_busy) + { + printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n"); + return 0; + } + + dsp_stereo = !!mode; + return dsp_stereo; +#endif +} + +static void +sb_dsp_output_block (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) +{ + unsigned long flags; + + if (!sb_irq_mode) + dsp_speaker (ON); + + sb_irq_mode = IMODE_OUTPUT; + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + + if (audio_devs[dev]->dmachan > 3) + count >>= 1; + count--; + + if (sb_dsp_highspeed) + { + DISABLE_INTR (flags); + if (sb_dsp_command (0x48))/* + * High speed size + */ + { + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); + sb_dsp_command (0x91);/* + * High speed 8 bit DAC + */ + } + else + printk ("SB Error: Unable to start (high speed) DAC\n"); + RESTORE_INTR (flags); + } + else + { + DISABLE_INTR (flags); + if (sb_dsp_command (0x14))/* + * 8-bit DAC (DMA) + */ + { + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); + } + else + printk ("SB Error: Unable to start DAC\n"); + RESTORE_INTR (flags); + } + sb_intr_active = 1; +} + +static void +sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, + int restart_dma) +{ + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + unsigned long flags; + + if (!sb_irq_mode) + dsp_speaker (OFF); + + sb_irq_mode = IMODE_INPUT; + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + + if (audio_devs[dev]->dmachan > 3) + count >>= 1; + count--; + + if (sb_dsp_highspeed) + { + DISABLE_INTR (flags); + if (sb_dsp_command (0x48))/* + * High speed size + */ + { + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); + sb_dsp_command (0x99);/* + * High speed 8 bit ADC + */ + } + else + printk ("SB Error: Unable to start (high speed) ADC\n"); + RESTORE_INTR (flags); + } + else + { + DISABLE_INTR (flags); + if (sb_dsp_command (0x24))/* + * 8-bit ADC (DMA) + */ + { + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); + } + else + printk ("SB Error: Unable to start ADC\n"); + RESTORE_INTR (flags); + } + + sb_intr_active = 1; +} + +static void +dsp_cleanup (void) +{ + sb_intr_active = 0; +} + +static int +sb_dsp_prepare_for_input (int dev, int bsize, int bcount) +{ + dsp_cleanup (); + dsp_speaker (OFF); + + if (sbc_major == 3) /* + * SB Pro + */ + { + if (dsp_stereo) + sb_dsp_command (0xa8); + else + sb_dsp_command (0xa0); + + dsp_speed (dsp_current_speed); /* + * Speed must be recalculated if + * #channels * changes + */ + } + return 0; +} + +static int +sb_dsp_prepare_for_output (int dev, int bsize, int bcount) +{ + dsp_cleanup (); + dsp_speaker (ON); + +#ifndef EXCLUDE_SBPRO + if (sbc_major == 3) /* + * SB Pro + */ + { + sb_mixer_set_stereo (dsp_stereo); + dsp_speed (dsp_current_speed); /* + * Speed must be recalculated if + * #channels * changes + */ + } +#endif + return 0; +} + +static void +sb_dsp_halt_xfer (int dev) +{ +} + +static int +verify_irq (void) +{ +#if 0 + DEFINE_WAIT_QUEUE (testq, testf); + + irq_ok = 0; + + if (sb_get_irq () == -1) + { + printk ("*** SB Error: Irq %d already in use\n", sbc_irq); + return 0; + } + + + sb_irq_mode = IMODE_INIT; + + sb_dsp_command (0xf2); /* + * This should cause immediate interrupt + */ + + DO_SLEEP (testq, testf, HZ / 5); + + sb_free_irq (); + + if (!irq_ok) + { + printk ("SB Warning: IRQ%d test not passed!", sbc_irq); + irq_ok = 1; + } +#else + irq_ok = 1; +#endif + return irq_ok; +} + +static int +sb_dsp_open (int dev, int mode) +{ + int retval; + + if (!sb_dsp_ok) + { + printk ("SB Error: SoundBlaster board not installed\n"); + return RET_ERROR (ENXIO); + } + + if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI)) + { + printk ("SB: PCM not possible during MIDI input\n"); + return RET_ERROR (EBUSY); + } + + if (!irq_verified) + { + verify_irq (); + irq_verified = 1; + } + else if (!irq_ok) + printk ("SB Warning: Incorrect IRQ setting %d\n", + sbc_irq); + + retval = sb_get_irq (); + if (retval) + return retval; + + if (DMAbuf_open_dma (dev) < 0) + { + sb_free_irq (); + printk ("SB: DMA Busy\n"); + return RET_ERROR (EBUSY); + } + + sb_irq_mode = IMODE_NONE; + + sb_dsp_busy = 1; + open_mode = mode; + + return 0; +} + +static void +sb_dsp_close (int dev) +{ + DMAbuf_close_dma (dev); + sb_free_irq (); + dsp_cleanup (); + dsp_speaker (OFF); + sb_dsp_busy = 0; + sb_dsp_highspeed = 0; + open_mode = 0; +} + +static int +sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_speed (arg); + return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return IOCTL_OUT (arg, dsp_current_speed); + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo (arg - 1) + 1; + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return IOCTL_OUT (arg, dsp_stereo + 1); + break; + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo (arg); + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); + break; + + case SOUND_PCM_WRITE_BITS: + case SOUND_PCM_READ_BITS: + if (local) + return 8; + return IOCTL_OUT (arg, 8);/* + * Only 8 bits/sample supported + */ + break; + + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return RET_ERROR (EINVAL); + break; + + default: + return RET_ERROR (EINVAL); + } + + return RET_ERROR (EINVAL); +} + +static void +sb_dsp_reset (int dev) +{ + unsigned long flags; + + DISABLE_INTR (flags); + + sb_reset_dsp (); + dsp_speed (dsp_current_speed); + dsp_cleanup (); + + RESTORE_INTR (flags); +} + +#endif + +int +sb_dsp_detect (struct address_info *hw_config) +{ + sbc_base = hw_config->io_base; + sbc_irq = hw_config->irq; + + if (sb_dsp_ok) + return 0; /* + * Already initialized + */ + + if (!sb_reset_dsp ()) + return 0; + + return 1; /* + * Detected + */ +} + +#ifndef EXCLUDE_AUDIO +static struct audio_operations sb_dsp_operations = +{ + "SoundBlaster", + NOTHING_SPECIAL, + AFMT_U8, /* Just 8 bits. Poor old SB */ + NULL, + sb_dsp_open, + sb_dsp_close, + sb_dsp_output_block, + sb_dsp_start_input, + sb_dsp_ioctl, + sb_dsp_prepare_for_input, + sb_dsp_prepare_for_output, + sb_dsp_reset, + sb_dsp_halt_xfer, + NULL, /* local_qlen */ + NULL /* copy_from_user */ +}; + +#endif + +long +sb_dsp_init (long mem_start, struct address_info *hw_config) +{ + int i; + int mixer_type = 0; + + sbc_major = sbc_minor = 0; + sb_dsp_command (0xe1); /* + * Get version + */ + + for (i = 1000; i; i--) + { + if (INB (DSP_DATA_AVAIL) & 0x80) + { /* + * wait for Data Ready + */ + if (sbc_major == 0) + sbc_major = INB (DSP_READ); + else + { + sbc_minor = INB (DSP_READ); + break; + } + } + } + + if (sbc_major == 2 || sbc_major == 3) + sb_duplex_midi = 1; + + if (sbc_major == 4) + sb16 = 1; + +#ifndef EXCLUDE_SBPRO + if (sbc_major >= 3) + mixer_type = sb_mixer_init (sbc_major); +#endif + +#ifndef EXCLUDE_YM8312 + + if (sbc_major > 3 || + (sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */ + enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); +#endif + + if (sbc_major >= 3) + { +#ifndef SCO +# ifdef __SGNXPRO__ + if (mixer_type == 2) + { + sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor); + } + else +# endif + { + sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor); + } +#endif + } + else + { +#ifndef SCO + sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor); +#endif + } + + printk (" <%s>", sb_dsp_operations.name); + +#ifndef EXCLUDE_AUDIO +#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO) + if (!sb16) /* + * There is a better driver for SB16 + */ +#endif + if (num_audiodevs < MAX_AUDIO_DEV) + { + audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations; + audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + audio_devs[my_dev]->dmachan = hw_config->dma; + } + else + printk ("SB: Too many DSP devices available\n"); +#endif + +#ifndef EXCLUDE_MIDI + if (!midi_disabled && !sb16) /* + * Midi don't work in the SB emulation mode * + * of PAS, SB16 has better midi interface + */ + sb_midi_init (sbc_major); +#endif + + sb_dsp_ok = 1; + return mem_start; +} + +void +sb_dsp_disable_midi (void) +{ + midi_disabled = 1; +} + +#endif diff --git a/sys/i386/isa/sound/sb_midi.c b/sys/i386/isa/sound/sb_midi.c new file mode 100644 index 00000000000..a78225ed273 --- /dev/null +++ b/sys/i386/isa/sound/sb_midi.c @@ -0,0 +1,252 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the SoundBlaster DS chips. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_MIDI) + +#include "sb.h" +#undef SB_TEST_IRQ + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + +extern int sb_dsp_ok; /* Set to 1 atfer successful initialization */ +extern int sbc_base; + +extern int sb_midi_mode; +extern int sb_midi_busy; /* + + + * * * * 1 if the process has output to MIDI + * + */ +extern int sb_dsp_busy; +extern int sb_dsp_highspeed; + +extern volatile int sb_irq_mode; +extern int sb_duplex_midi; +extern int sb_intr_active; +int input_opened = 0; +static int my_dev; + +void (*midi_input_intr) (int dev, unsigned char data); + +static int +sb_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int ret; + + if (!sb_dsp_ok) + { + printk ("SB Error: MIDI hardware not installed\n"); + return RET_ERROR (ENXIO); + } + + if (sb_midi_busy) + return RET_ERROR (EBUSY); + + if (mode != OPEN_WRITE && !sb_duplex_midi) + { + if (num_midis == 1) + printk ("SoundBlaster: Midi input not currently supported\n"); + return RET_ERROR (EPERM); + } + + sb_midi_mode = NORMAL_MIDI; + if (mode != OPEN_WRITE) + { + if (sb_dsp_busy || sb_intr_active) + return RET_ERROR (EBUSY); + sb_midi_mode = UART_MIDI; + } + + if (sb_dsp_highspeed) + { + printk ("SB Error: Midi output not possible during stereo or high speed audio\n"); + return RET_ERROR (EBUSY); + } + + if (sb_midi_mode == UART_MIDI) + { + sb_irq_mode = IMODE_MIDI; + + sb_reset_dsp (); + + if (!sb_dsp_command (0x35)) + return RET_ERROR (EIO); /* + * Enter the UART mode + */ + sb_intr_active = 1; + + if ((ret = sb_get_irq ()) < 0) + { + sb_reset_dsp (); + return 0; /* + * IRQ not free + */ + } + input_opened = 1; + midi_input_intr = input; + } + + sb_midi_busy = 1; + + return 0; +} + +static void +sb_midi_close (int dev) +{ + if (sb_midi_mode == UART_MIDI) + { + sb_reset_dsp (); /* + * The only way to kill the UART mode + */ + sb_free_irq (); + } + sb_intr_active = 0; + sb_midi_busy = 0; + input_opened = 0; +} + +static int +sb_midi_out (int dev, unsigned char midi_byte) +{ + unsigned long flags; + + if (sb_midi_mode == NORMAL_MIDI) + { + DISABLE_INTR (flags); + if (sb_dsp_command (0x38)) + sb_dsp_command (midi_byte); + else + printk ("SB Error: Unable to send a MIDI byte\n"); + RESTORE_INTR (flags); + } + else + sb_dsp_command (midi_byte); /* + * UART write + */ + + return 1; +} + +static int +sb_midi_start_read (int dev) +{ + if (sb_midi_mode != UART_MIDI) + { + printk ("SoundBlaster: MIDI input not implemented.\n"); + return RET_ERROR (EPERM); + } + return 0; +} + +static int +sb_midi_end_read (int dev) +{ + if (sb_midi_mode == UART_MIDI) + { + sb_reset_dsp (); + sb_intr_active = 0; + } + return 0; +} + +static int +sb_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EPERM); +} + +void +sb_midi_interrupt (int dummy) +{ + unsigned long flags; + unsigned char data; + + DISABLE_INTR (flags); + + data = INB (DSP_READ); + if (input_opened) + midi_input_intr (my_dev, data); + + RESTORE_INTR (flags); +} + +#define MIDI_SYNTH_NAME "SoundBlaster Midi" +#define MIDI_SYNTH_CAPS 0 +#include "midi_synth.h" + +static struct midi_operations sb_midi_operations = +{ + {"SoundBlaster", 0, 0, SNDCARD_SB}, + &std_midi_synth, + sb_midi_open, + sb_midi_close, + sb_midi_ioctl, + sb_midi_out, + sb_midi_start_read, + sb_midi_end_read, + NULL, /* + * Kick + */ + NULL, /* + * command + */ + NULL, /* + * buffer_status + */ + NULL +}; + +void +sb_midi_init (int model) +{ + if (num_midis >= MAX_MIDI_DEV) + { + printk ("Sound: Too many midi devices detected\n"); + return; + } + + std_midi_synth.midi_dev = num_midis; + my_dev = num_midis; + midi_devs[num_midis++] = &sb_midi_operations; +} + +#endif diff --git a/sys/i386/isa/sound/sb_mixer.c b/sys/i386/isa/sound/sb_mixer.c new file mode 100644 index 00000000000..312a15c76fb --- /dev/null +++ b/sys/i386/isa/sound/sb_mixer.c @@ -0,0 +1,453 @@ + +/* + * sound/sb_mixer.c + * + * The low level mixer driver for the SoundBlaster Pro and SB16 cards. + * + * Copyright by Hannu Savolainen 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified: + * Hunyue Yau Jan 6 1994 + * Added code to support the Sound Galaxy NX Pro mixer. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO) +#define __SB_MIXER_C__ + +#include "sb.h" +#include "sb_mixer.h" +#undef SB_TEST_IRQ + +extern int sbc_base; + +static int mixer_initialized = 0; + +static int supported_rec_devices; +static int supported_devices; +static int recmask = 0; +static int mixer_model; +static int mixer_caps; +static mixer_tab *iomap; + +void +sb_setmixer (unsigned int port, unsigned int value) +{ + unsigned long flags; + + DISABLE_INTR (flags); + OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* + * Select register + */ + tenmicrosec (); + OUTB ((unsigned char) (value & 0xff), MIXER_DATA); + tenmicrosec (); + RESTORE_INTR (flags); +} + +int +sb_getmixer (unsigned int port) +{ + int val; + unsigned long flags; + + DISABLE_INTR (flags); + OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* + * Select register + */ + tenmicrosec (); + val = INB (MIXER_DATA); + tenmicrosec (); + RESTORE_INTR (flags); + + return val; +} + +void +sb_mixer_set_stereo (int mode) +{ + if (!mixer_initialized) + return; + + sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC) + | (mode ? STEREO_DAC : MONO_DAC))); +} + +/* + * Returns: + * 0 No mixer detected. + * 1 Only a plain Sound Blaster Pro style mixer detected. + * 2 The Sound Galaxy NX Pro mixer detected. + */ +static int +detect_mixer (void) +{ +#ifdef __SGNXPRO__ + int oldbass, oldtreble; + +#endif + int retcode = 1; + + /* + * Detect the mixer by changing parameters of two volume channels. If the + * values read back match with the values written, the mixer is there (is + * it?) + */ + sb_setmixer (FM_VOL, 0xff); + sb_setmixer (VOC_VOL, 0x33); + + if (sb_getmixer (FM_VOL) != 0xff) + return 0; /* + * No match + */ + if (sb_getmixer (VOC_VOL) != 0x33) + return 0; + +#ifdef __SGNXPRO__ + /* Attempt to detect the SG NX Pro by check for valid bass/treble + * registers. + */ + oldbass = sb_getmixer (BASS_LVL); + oldtreble = sb_getmixer (TREBLE_LVL); + + sb_setmixer (BASS_LVL, 0xaa); + sb_setmixer (TREBLE_LVL, 0x55); + + if ((sb_getmixer (BASS_LVL) != 0xaa) || + (sb_getmixer (TREBLE_LVL) != 0x55)) + { + retcode = 1; /* 1 == Only SB Pro detected */ + } + else + retcode = 2; /* 2 == SG NX Pro detected */ + /* Restore register in either case since SG NX Pro has EEPROM with + * 'preferred' values stored. + */ + sb_setmixer (BASS_LVL, oldbass); + sb_setmixer (TREBLE_LVL, oldtreble); +#endif + return retcode; +} + +static void +change_bits (unsigned char *regval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + + mask = (1 << (*iomap)[dev][chn].nbits) - 1; + newval = (int) ((newval * mask) + 50) / 100; /* + * Scale it + */ + + shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1; + + *regval &= ~(mask << shift); /* + * Filter out the previous value + */ + *regval |= (newval & mask) << shift; /* + * Set the new value + */ +} + +static int +sb_mixer_get (int dev) +{ + if (!((1 << dev) & supported_devices)) + return RET_ERROR (EINVAL); + + return levels[dev]; +} + +static int +sb_mixer_set (int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + + int regoffs; + unsigned char val; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (dev > 31) + return RET_ERROR (EINVAL); + + if (!(supported_devices & (1 << dev))) /* + * Not supported + */ + return RET_ERROR (EINVAL); + + regoffs = (*iomap)[dev][LEFT_CHN].regno; + + if (regoffs == 0) + return RET_ERROR (EINVAL); + + val = sb_getmixer (regoffs); + change_bits (&val, dev, LEFT_CHN, left); + + levels[dev] = left | (left << 8); + + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) /* + * Change register + */ + { + sb_setmixer (regoffs, val); /* + * Save the old one + */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + + if (regoffs == 0) + return left | (left << 8); /* + * Just left channel present + */ + + val = sb_getmixer (regoffs); /* + * Read the new one + */ + } + + change_bits (&val, dev, RIGHT_CHN, right); + sb_setmixer (regoffs, val); + + levels[dev] = left | (right << 8); + return left | (right << 8); +} + +static void +set_recsrc (int src) +{ + sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7)); +} + +static int +set_recmask (int mask) +{ + int devmask, i; + unsigned char regimageL, regimageR; + + devmask = mask & supported_rec_devices; + + switch (mixer_model) + { + case 3: + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { /* + * More than one devices selected. Drop the * + * previous selection + */ + devmask &= ~recmask; + } + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { /* + * More than one devices selected. Default to + * * mic + */ + devmask = SOUND_MASK_MIC; + } + + + if (devmask ^ recmask) /* + * Input source changed + */ + { + switch (devmask) + { + + case SOUND_MASK_MIC: + set_recsrc (SRC_MIC); + break; + + case SOUND_MASK_LINE: + set_recsrc (SRC_LINE); + break; + + case SOUND_MASK_CD: + set_recsrc (SRC_CD); + break; + + default: + set_recsrc (SRC_MIC); + } + } + + break; + + case 4: + if (!devmask) + devmask = SOUND_MASK_MIC; + + regimageL = regimageR = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if ((1 << i) & devmask) + { + regimageL |= sb16_recmasks_L[i]; + regimageR |= sb16_recmasks_R[i]; + } + sb_setmixer (SB16_IMASK_L, regimageL); + sb_setmixer (SB16_IMASK_R, regimageR); + break; + } + + recmask = devmask; + return recmask; +} + +static int +sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg))); + break; + + default: + return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg))); + } + else + switch (cmd & 0xff) /* + * Return parameters + */ + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, recmask); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, supported_devices); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, supported_devices & + ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER)); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, supported_rec_devices); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, mixer_caps); + break; + + default: + return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff)); + } + } + else + return RET_ERROR (EINVAL); +} + +static struct mixer_operations sb_mixer_operations = +{ + sb_mixer_ioctl +}; + +static void +sb_mixer_reset (void) +{ + int i; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + sb_mixer_set (i, levels[i]); + set_recmask (SOUND_MASK_MIC); +} + +/* + * Returns a code depending on whether a SG NX Pro was detected. + * 1 == Plain SB Pro + * 2 == SG NX Pro detected. + * 3 == SB16 + * + * Used to update message. + */ +int +sb_mixer_init (int major_model) +{ + int mixer_type = 0; + + sb_setmixer (0x00, 0); /* Reset mixer */ + + if (!(mixer_type = detect_mixer ())) + return 0; /* No mixer. Why? */ + + mixer_initialized = 1; + mixer_model = major_model; + + switch (major_model) + { + case 3: + mixer_caps = SOUND_CAP_EXCL_INPUT; +#ifdef __SGNXPRO__ + if (mixer_type == 2) /* A SGNXPRO was detected */ + { + supported_devices = SGNXPRO_MIXER_DEVICES; + supported_rec_devices = SGNXPRO_RECORDING_DEVICES; + iomap = &sgnxpro_mix; + } + else +#endif + { + supported_devices = SBPRO_MIXER_DEVICES; + supported_rec_devices = SBPRO_RECORDING_DEVICES; + iomap = &sbpro_mix; + mixer_type = 1; + } + break; + + case 4: + mixer_caps = 0; + supported_devices = SB16_MIXER_DEVICES; + supported_rec_devices = SB16_RECORDING_DEVICES; + iomap = &sb16_mix; + mixer_type = 3; + break; + + default: + printk ("SB Warning: Unsupported mixer type\n"); + return 0; + } + + if (num_mixers < MAX_MIXER_DEV) + mixer_devs[num_mixers++] = &sb_mixer_operations; + sb_mixer_reset (); + return mixer_type; +} + +#endif diff --git a/sys/i386/isa/sound/sb_mixer.h b/sys/i386/isa/sound/sb_mixer.h new file mode 100644 index 00000000000..1a5fc8adfd3 --- /dev/null +++ b/sys/i386/isa/sound/sb_mixer.h @@ -0,0 +1,221 @@ +/* + * sound/sb_mixer.h + * + * Definitions for the SB Pro and SB16 mixers + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified: + * Hunyue Yau Jan 6 1994 + * Added defines for the Sound Galaxy NX Pro mixer. + * + */ + +#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) + +/* Same as SB Pro, unless I find otherwise */ +#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES + +#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +/* SG NX Pro has treble and bass settings on the mixer. The 'speaker' + * channel is the COVOX/DisneySoundSource emulation volume control + * on the mixer. It does NOT control speaker volume. Should have own + * mask eventually? + */ +#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ + SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) + +#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_RECLEV | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE) + +/* + * Mixer registers + * + * NOTE! RECORD_SRC == IN_FILTER + */ + +/* + * Mixer registers of SB Pro + */ +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E +#define IRQ_NR 0x80 +#define DMA_NR 0x81 +#define IRQ_STAT 0x82 +#define OPSW 0x3c + +/* + * Additional registers on the SG NX Pro + */ +#define COVOX_VOL 0x42 +#define TREBLE_LVL 0x44 +#define BASS_LVL 0x46 + +#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ +#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ +#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ +#define FILT_OFF (1 << 5) + +#define MONO_DAC 0x00 +#define STEREO_DAC 0x02 + +/* + * Mixer registers of SB16 + */ +#define SB16_IMASK_L 0x3d +#define SB16_IMASK_R 0x3e + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +struct mixer_def { + unsigned int regno: 8; + unsigned int bitoffs:4; + unsigned int nbits:4; +}; + + +typedef struct mixer_def mixer_tab[32][2]; +typedef struct mixer_def mixer_ent; + +#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ + {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} + +#ifdef __SB_MIXER_C__ +mixer_tab sbpro_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; + +#ifdef __SGNXPRO__ +mixer_tab sgnxpro_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; +#endif + +mixer_tab sb16_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), +MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), +MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), +MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), +MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), +MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2) +}; + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x5a5a, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x4b4b, /* FM */ + 0x4b4b, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x0000, /* Mic */ + 0x4b4b, /* CD */ + 0x4b4b, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b}; /* Recording level */ + +static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x40, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x10, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x04, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00 /* SOUND_MIXER_RECLEV */ +}; + +static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x20, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x08, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x02, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00 /* SOUND_MIXER_RECLEV */ +}; + +/* + * Recording sources (SB Pro) + */ + +#define SRC_MIC 1 /* Select Microphone recording source */ +#define SRC_CD 3 /* Select CD recording source */ +#define SRC_LINE 7 /* Use Line-in for recording source */ + +#endif diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c new file mode 100644 index 00000000000..45f06c1edfb --- /dev/null +++ b/sys/i386/isa/sound/sequencer.c @@ -0,0 +1,1861 @@ +/* + * sound/sequencer.c + * + * The sequencer personality manager. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define SEQUENCER_C +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#ifndef EXCLUDE_SEQUENCER + +static int sequencer_ok = 0; +static struct sound_timer_operations *tmr; +static int tmr_no = -1; /* Currently selected timer */ +static int pending_timer = -1; /* For timer change operation */ + +/* + * Local counts for number of synth and MIDI devices. These are initialized + * by the sequencer_open. + */ +static int max_mididev = 0; +static int max_synthdev = 0; + +/* + * The seq_mode gives the operating mode of the sequencer: + * 1 = level1 (the default) + * 2 = level2 (extended capabilites) + */ + +#define SEQ_1 1 +#define SEQ_2 2 +static int seq_mode = SEQ_1; + +DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); +DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); + +static int midi_opened[MAX_MIDI_DEV] = +{0}; +static int midi_written[MAX_MIDI_DEV] = +{0}; + +unsigned long prev_input_time = 0; +int prev_event_time; +unsigned long seq_time = 0; + +#include "tuning.h" + +#define EV_SZ 8 +#define IEV_SZ 8 +static unsigned char *queue = NULL; +static unsigned char *iqueue = NULL; + +static volatile int qhead = 0, qtail = 0, qlen = 0; +static volatile int iqhead = 0, iqtail = 0, iqlen = 0; +static volatile int seq_playing = 0; +static int sequencer_busy = 0; +static int output_treshold; +static int pre_event_timeout; +static unsigned synth_open_mask; + +static int seq_queue (unsigned char *note); +static void seq_startplay (void); +static int seq_sync (void); +static void seq_reset (void); +static int pmgr_present[MAX_SYNTH_DEV] = +{0}; + +#if MAX_SYNTH_DEV > 15 +#error Too many synthesizer devices enabled. +#endif + +int +sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c = count, p = 0; + int ev_len; + unsigned long flags; + + dev = dev >> 4; + + ev_len = seq_mode == SEQ_1 ? 4 : 8; + + if (dev) /* + * Patch manager device + */ + return pmgr_read (dev - 1, file, buf, count); + + DISABLE_INTR (flags); + if (!iqlen) + { + DO_SLEEP (midi_sleeper, midi_sleep_flag, pre_event_timeout); + + if (!iqlen) + { + RESTORE_INTR (flags); + return 0; + } + } + + while (iqlen && c >= ev_len) + { + + COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], ev_len); + p += ev_len; + c -= ev_len; + + iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; + iqlen--; + } + RESTORE_INTR (flags); + + return count - c; +} + +static void +sequencer_midi_output (int dev) +{ + /* + * Currently NOP + */ +} + +void +seq_copy_to_input (unsigned char *event, int len) +{ + unsigned long flags; + + /* + * Verify that the len is valid for the current mode. + */ + + if (len != 4 && len != 8) + return; + if ((seq_mode == SEQ_1) != (len == 4)) + return; + + if (iqlen >= (SEQ_MAX_QUEUE - 1)) + return; /* Overflow */ + + DISABLE_INTR (flags); + memcpy (&iqueue[iqtail * IEV_SZ], event, len); + iqlen++; + iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; + + if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag)) + { + WAKE_UP (midi_sleeper, midi_sleep_flag); + } + RESTORE_INTR (flags); +} + +static void +sequencer_midi_input (int dev, unsigned char data) +{ + unsigned int tstamp; + unsigned char event[4]; + + if (data == 0xfe) /* Ignore active sensing */ + return; + + tstamp = GET_TIME () - seq_time; + if (tstamp != prev_input_time) + { + tstamp = (tstamp << 8) | SEQ_WAIT; + + seq_copy_to_input ((unsigned char *) &tstamp, 4); + prev_input_time = tstamp; + } + + event[0] = SEQ_MIDIPUTC; + event[1] = data; + event[2] = dev; + event[3] = 0; + + seq_copy_to_input (event, 4); +} + +void +seq_input_event (unsigned char *event, int len) +{ + unsigned long this_time; + + if (seq_mode == SEQ_2) + this_time = tmr->get_time (tmr_no); + else + this_time = GET_TIME () - seq_time; + + if (this_time != prev_input_time) + { + unsigned char tmp_event[8]; + + tmp_event[0] = EV_TIMING; + tmp_event[1] = TMR_WAIT_ABS; + tmp_event[2] = 0; + tmp_event[3] = 0; + *(unsigned long *) &tmp_event[4] = this_time; + + seq_copy_to_input (tmp_event, 8); + prev_input_time = this_time; + } + + seq_copy_to_input (event, len); +} + +int +sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned char event[EV_SZ], ev_code; + int p = 0, c, ev_size; + int err; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count)); + + if (mode == OPEN_READ) + return RET_ERROR (EIO); + + if (dev) /* + * Patch manager device + */ + return pmgr_write (dev - 1, file, buf, count); + + c = count; + + while (c >= 4) + { + COPY_FROM_USER (event, buf, p, 4); + ev_code = event[0]; + + if (ev_code == SEQ_FULLSIZE) + { + int err; + + dev = *(unsigned short *) &event[2]; + if (dev < 0 || dev >= max_synthdev) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return RET_ERROR (ENXIO); + + err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0); + if (err < 0) + return err; + + return err; + } + + if (ev_code >= 128) + { + if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) + { + printk ("Sequencer: Invalid level 2 event %x\n", ev_code); + return RET_ERROR (EINVAL); + } + + ev_size = 8; + + if (c < ev_size) + { + if (!seq_playing) + seq_startplay (); + return count - c; + } + + COPY_FROM_USER (&event[4], buf, p + 4, 4); + + } + else + { + if (seq_mode == SEQ_2) + { + printk ("Sequencer: 4 byte event in level 2 mode\n"); + return RET_ERROR (EINVAL); + } + ev_size = 4; + } + + if (event[0] == SEQ_MIDIPUTC) + { + + if (!midi_opened[event[2]]) + { + int mode; + int dev = event[2]; + + if (dev >= max_mididev) + { + printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev); + return RET_ERROR (ENXIO); + } + + mode = file->mode & O_ACCMODE; + + if ((err = midi_devs[dev]->open (dev, mode, + sequencer_midi_input, sequencer_midi_output)) < 0) + { + seq_reset (); + printk ("Sequencer Error: Unable to open Midi #%d\n", dev); + return err; + } + + midi_opened[dev] = 1; + } + + } + + if (!seq_queue (event)) + { + + if (!seq_playing) + seq_startplay (); + return count - c; + } + + p += ev_size; + c -= ev_size; + } + + if (!seq_playing) + seq_startplay (); + + return count; +} + +static int +seq_queue (unsigned char *note) +{ + + /* + * Test if there is space in the queue + */ + + if (qlen >= SEQ_MAX_QUEUE) + if (!seq_playing) + seq_startplay (); /* + * Give chance to drain the queue + */ + + if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) + { + /* + * Sleep until there is enough space on the queue + */ + DO_SLEEP (seq_sleeper, seq_sleep_flag, 0); + } + + if (qlen >= SEQ_MAX_QUEUE) + return 0; /* + * To be sure + */ + + memcpy (&queue[qtail * EV_SZ], note, EV_SZ); + + qtail = (qtail + 1) % SEQ_MAX_QUEUE; + qlen++; + + return 1; +} + +static int +extended_event (unsigned char *q) +{ + int dev = q[2]; + + if (dev < 0 || dev >= max_synthdev) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return RET_ERROR (ENXIO); + + switch (q[1]) + { + case SEQ_NOTEOFF: + synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]); + break; + + case SEQ_NOTEON: + if (q[4] > 127 && q[4] != 255) + return 0; + + synth_devs[dev]->start_note (dev, q[3], q[4], q[5]); + break; + + case SEQ_PGMCHANGE: + synth_devs[dev]->set_instr (dev, q[3], q[4]); + break; + + case SEQ_AFTERTOUCH: + synth_devs[dev]->aftertouch (dev, q[3], q[4]); + break; + + case SEQ_BALANCE: + synth_devs[dev]->panning (dev, q[3], (char) q[4]); + break; + + case SEQ_CONTROLLER: + synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]); + break; + + case SEQ_VOLMODE: + if (synth_devs[dev]->volume_method != NULL) + synth_devs[dev]->volume_method (dev, q[3]); + break; + + default: + return RET_ERROR (EINVAL); + } + + return 0; +} + +static int +find_voice (int dev, int chn, int note) +{ + unsigned short key; + int i; + + key = (chn << 8) | (note + 1); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if (synth_devs[dev]->alloc.map[i] == key) + return i; + + return -1; +} + +static int +alloc_voice (int dev, int chn, int note) +{ + unsigned short key; + int voice; + + key = (chn << 8) | (note + 1); + + voice = synth_devs[dev]->alloc_voice (dev, chn, note, + &synth_devs[dev]->alloc); + synth_devs[dev]->alloc.map[voice] = key; + return voice; +} + +static void +seq_chn_voice_event (unsigned char *event) +{ + unsigned char dev = event[1]; + unsigned char cmd = event[2]; + unsigned char chn = event[3]; + unsigned char note = event[4]; + unsigned char parm = event[5]; + int voice = -1; + + if ((int) dev > max_synthdev) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + if (seq_mode == SEQ_2) + if (synth_devs[dev]->alloc_voice) + voice = find_voice (dev, chn, note); + + if (cmd == MIDI_NOTEON && parm == 0) + { + cmd = MIDI_NOTEOFF; + parm = 64; + } + + switch (cmd) + { + case MIDI_NOTEON: + if (note > 127) + return; + + if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) + { + voice = alloc_voice (dev, chn, note); + } + + if (voice == -1) + voice = chn; + + if (seq_mode == SEQ_2) + { + synth_devs[dev]->set_instr (dev, voice, + synth_devs[dev]->chn_info[chn].pgm_num); + } + + synth_devs[dev]->start_note (dev, voice, note, parm); + break; + + case MIDI_NOTEOFF: + if (voice == -1) + voice = chn; + synth_devs[dev]->kill_note (dev, voice, note, parm); + break; + + case MIDI_KEY_PRESSURE: + /* To be implemented */ + break; + + default:; + } +} + +static void +seq_chn_common_event (unsigned char *event) +{ + unsigned char dev = event[1]; + unsigned char cmd = event[2]; + unsigned char chn = event[3]; + unsigned char p1 = event[4]; + + /* unsigned char p2 = event[5]; */ + unsigned short w14 = *(short *) &event[6]; + + if ((int) dev > max_synthdev) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + switch (cmd) + { + case MIDI_PGM_CHANGE: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].pgm_num = p1; + } + else + synth_devs[dev]->set_instr (dev, chn, p1); + break; + + case MIDI_CTL_CHANGE: + if (p1 == CTRL_MAIN_VOLUME) + { + w14 = (unsigned short) (((int) w14 * 16383) / 100); + p1 = CTL_MAIN_VOLUME; + } + if (p1 == CTRL_EXPRESSION) + { + w14 *= 128; + p1 = CTL_EXPRESSION; + } + + if (seq_mode == SEQ_2) + { + if (chn > 15 || p1 > 127) + break; + + synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0xff; + + if (dev < num_synths) + { + int val = w14 & 0xff; + + if (p1 < 64) /* Combine MSB and LSB */ + { + val = ((synth_devs[dev]-> + chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) + | (synth_devs[dev]-> + chn_info[chn].controllers[p1 | 32] & 0x7f); + p1 &= ~32; + } + else + val = synth_devs[dev]->chn_info[chn].controllers[p1]; + + synth_devs[dev]->controller (dev, chn, p1, val); + } + else + synth_devs[dev]->controller (dev, chn, p1, w14); + } + else + synth_devs[dev]->controller (dev, chn, p1, w14); + break; + + case MIDI_PITCH_BEND: + synth_devs[dev]->bender (dev, chn, w14); + break; + + default:; + } +} + +static int +seq_timing_event (unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned int parm = *(int *) &event[4]; + + if (seq_mode == SEQ_2) + { + int ret; + + if ((ret = tmr->event (tmr_no, event)) == TIMER_ARMED) + { + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) + { + unsigned long flags; + + DISABLE_INTR (flags); + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) + { + WAKE_UP (seq_sleeper, seq_sleep_flag); + } + RESTORE_INTR (flags); + } + } + return ret; + } + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + + /* + * NOTE! No break here. Execution of TMR_WAIT_REL continues in the + * next case (TMR_WAIT_ABS) + */ + + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + seq_playing = 1; + time = parm; + prev_event_time = time; + + request_sound_timer (time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) + { + unsigned long flags; + + DISABLE_INTR (flags); + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) + { + WAKE_UP (seq_sleeper, seq_sleep_flag); + } + RESTORE_INTR (flags); + } + + return TIMER_ARMED; + } + break; + + case TMR_START: + seq_time = GET_TIME (); + prev_input_time = 0; + prev_event_time = 0; + break; + + case TMR_STOP: + break; + + case TMR_CONTINUE: + break; + + case TMR_TEMPO: + break; + + case TMR_ECHO: + if (seq_mode == SEQ_2) + seq_copy_to_input (event, 8); + else + { + parm = (parm << 8 | SEQ_ECHO); + seq_copy_to_input ((unsigned char *) &parm, 4); + } + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static void +seq_local_event (unsigned char *event) +{ + /* unsigned char cmd = event[1]; */ + + printk ("seq_local_event() called. WHY????????\n"); +} + +static void +seq_startplay (void) +{ + int this_one; + unsigned long *delay; + unsigned char *q; + + while (qlen > 0) + { + + seq_playing = 1; + + qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; + qlen--; + + q = &queue[this_one * EV_SZ]; + + switch (q[0]) + { + case SEQ_NOTEOFF: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->kill_note (0, q[1], 255, q[3]); + break; + + case SEQ_NOTEON: + if (q[4] < 128 || q[4] == 255) + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->start_note (0, q[1], q[2], q[3]); + break; + + case SEQ_WAIT: + delay = (unsigned long *) q; /* + * Bytes 1 to 3 are containing the * + * delay in GET_TIME() + */ + *delay = (*delay >> 8) & 0xffffff; + + if (*delay > 0) + { + long time; + + seq_playing = 1; + time = *delay; + prev_event_time = time; + + request_sound_timer (time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) + { + unsigned long flags; + + DISABLE_INTR (flags); + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) + { + WAKE_UP (seq_sleeper, seq_sleep_flag); + } + RESTORE_INTR (flags); + } + /* + * The timer is now active and will reinvoke this function + * after the timer expires. Return to the caller now. + */ + return; + } + break; + + case SEQ_PGMCHANGE: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->set_instr (0, q[1], q[2]); + break; + + case SEQ_SYNCTIMER: /* + * Reset timer + */ + seq_time = GET_TIME (); + prev_input_time = 0; + prev_event_time = 0; + break; + + case SEQ_MIDIPUTC: /* + * Put a midi character + */ + if (midi_opened[q[2]]) + { + int dev; + + dev = q[2]; + + if (!midi_devs[dev]->putc (dev, q[1])) + { + /* + * Output FIFO is full. Wait one timer cycle and try again. + */ + + qlen++; + qhead = this_one; /* + * Restore queue + */ + seq_playing = 1; + request_sound_timer (-1); + return; + } + else + midi_written[dev] = 1; + } + break; + + case SEQ_ECHO: + seq_copy_to_input (q, 4); /* + * Echo back to the process + */ + break; + + case SEQ_PRIVATE: + if ((int) q[1] < max_synthdev) + synth_devs[q[1]]->hw_control (q[1], q); + break; + + case SEQ_EXTENDED: + extended_event (q); + break; + + case EV_CHN_VOICE: + seq_chn_voice_event (q); + break; + + case EV_CHN_COMMON: + seq_chn_common_event (q); + break; + + case EV_TIMING: + if (seq_timing_event (q) == TIMER_ARMED) + { + return; + } + break; + + case EV_SEQ_LOCAL: + seq_local_event (q); + break; + + default:; + } + + } + + seq_playing = 0; + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) + { + unsigned long flags; + + DISABLE_INTR (flags); + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) + { + WAKE_UP (seq_sleeper, seq_sleep_flag); + } + RESTORE_INTR (flags); + } + +} + +static void +reset_controllers (int dev, unsigned char *controller, int update_dev) +{ +#include "midi_ctrl.h" + + int i; + + for (i = 0; i < 128; i++) + controller[i] = ctrl_def_values[i]; +} + +static void +setup_mode2 (void) +{ + int dev; + + max_synthdev = num_synths; + + for (dev = 0; dev < num_midis; dev++) + if (midi_devs[dev]->converter != NULL) + { + synth_devs[max_synthdev++] = + midi_devs[dev]->converter; + } + + for (dev = 0; dev < max_synthdev; dev++) + { + int chn; + + for (chn = 0; chn < 16; chn++) + { + synth_devs[dev]->chn_info[chn].pgm_num = 0; + reset_controllers (dev, + synth_devs[dev]->chn_info[chn].controllers, + 0); + } + } + + max_mididev = 0; + seq_mode = SEQ_2; +} + +int +sequencer_open (int dev, struct fileinfo *file) +{ + int retval, mode, i; + int level, tmp; + + level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + DEB (printk ("sequencer_open(dev=%d)\n", dev)); + + if (!sequencer_ok) + { + printk ("Soundcard: Sequencer not initialized\n"); + return RET_ERROR (ENXIO); + } + + if (dev) /* + * Patch manager device + */ + { + int err; + + dev--; + + if (dev >= MAX_SYNTH_DEV) + return RET_ERROR (ENXIO); + if (pmgr_present[dev]) + return RET_ERROR (EBUSY); + if ((err = pmgr_open (dev)) < 0) + return err; /* + * Failed + */ + + pmgr_present[dev] = 1; + return err; + } + + if (sequencer_busy) + { + printk ("Sequencer busy\n"); + return RET_ERROR (EBUSY); + } + + max_mididev = num_midis; + max_synthdev = num_synths; + pre_event_timeout = 0; + seq_mode = SEQ_1; + + if (pending_timer != -1) + { + tmr_no = pending_timer; + pending_timer = -1; + } + + if (tmr_no == -1) /* Not selected yet */ + { + int i, best; + + best = -1; + for (i = 0; i < num_sound_timers; i++) + if (sound_timer_devs[i]->priority > best) + { + tmr_no = i; + best = sound_timer_devs[i]->priority; + } + + if (tmr_no == -1) /* Should not be */ + tmr_no = 0; + } + + tmr = sound_timer_devs[tmr_no]; + + if (level == 2) + { + printk ("Using timer #%d\n", tmr_no); + if (tmr == NULL) + { + printk ("sequencer: No timer for level 2\n"); + return RET_ERROR (ENXIO); + } + setup_mode2 (); + } + + if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) + if (!max_mididev) + { + printk ("Sequencer: No Midi devices. Input not possible\n"); + return RET_ERROR (ENXIO); + } + + if (!max_synthdev && !max_mididev) + return RET_ERROR (ENXIO); + + synth_open_mask = 0; + + for (i = 0; i < max_mididev; i++) + { + midi_opened[i] = 0; + midi_written[i] = 0; + } + + /* + * if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + */ + for (i = 0; i < max_synthdev; i++) /* + * Open synth devices + */ + if ((tmp = synth_devs[i]->open (i, mode)) < 0) + { + printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); + if (synth_devs[i]->midi_dev) + printk ("(Maps to midi dev #%d\n", synth_devs[i]->midi_dev); + } + else + { + synth_open_mask |= (1 << i); + if (synth_devs[i]->midi_dev) /* + * Is a midi interface + */ + midi_opened[synth_devs[i]->midi_dev] = 1; + } + + seq_time = GET_TIME (); + prev_input_time = 0; + prev_event_time = 0; + + if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) + { /* + * Initialize midi input devices + */ + for (i = 0; i < max_mididev; i++) + if (!midi_opened[i]) + { + if ((retval = midi_devs[i]->open (i, mode, + sequencer_midi_input, sequencer_midi_output)) >= 0) + midi_opened[i] = 1; + } + } + + if (seq_mode == SEQ_2) + { + tmr->open (tmr_no, seq_mode); + } + + sequencer_busy = 1; + RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); + RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); + output_treshold = SEQ_MAX_QUEUE / 2; + + for (i = 0; i < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0); + + return 0; +} + +void +seq_drain_midi_queues (void) +{ + int i, n; + + /* + * Give the Midi drivers time to drain their output queues + */ + + n = 1; + + while (!PROCESS_ABORTING (midi_sleeper, midi_sleep_flag) && n) + { + n = 0; + + for (i = 0; i < max_mididev; i++) + if (midi_opened[i] && midi_written[i]) + if (midi_devs[i]->buffer_status != NULL) + if (midi_devs[i]->buffer_status (i)) + n++; + + /* + * Let's have a delay + */ + if (n) + { + DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10); + } + } +} + +void +sequencer_release (int dev, struct fileinfo *file) +{ + int i; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + DEB (printk ("sequencer_release(dev=%d)\n", dev)); + + if (dev) /* + * Patch manager device + */ + { + dev--; + pmgr_release (dev); + pmgr_present[dev] = 0; + return; + } + + /* + * * Wait until the queue is empty + */ + + if (mode != OPEN_READ) + while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen) + { + seq_sync (); + } + + if (mode != OPEN_READ) + seq_drain_midi_queues (); /* + * Ensure the output queues are empty + */ + seq_reset (); + if (mode != OPEN_READ) + seq_drain_midi_queues (); /* + * Flush the all notes off messages + */ + + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) /* + * Actually opened + */ + if (synth_devs[i]) + { + synth_devs[i]->close (i); + + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 0; + } + + for (i = 0; i < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0); + + for (i = 0; i < max_mididev; i++) + if (midi_opened[i]) + midi_devs[i]->close (i); + + if (seq_mode == SEQ_2) + tmr->close (tmr_no); + + sequencer_busy = 0; +} + +static int +seq_sync (void) +{ + if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag)) + seq_startplay (); + + if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) /* + * Queue not + * empty + */ + { + DO_SLEEP (seq_sleeper, seq_sleep_flag, 0); + } + + return qlen; +} + +static void +midi_outc (int dev, unsigned char data) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int n; + + /* + * This routine sends one byte to the Midi channel. + */ + /* + * If the output Fifo is full, it waits until there + */ + /* + * is space in the queue + */ + + n = 300; /* + * Timeout in jiffies + */ + + while (n && !midi_devs[dev]->putc (dev, data)) + { + DO_SLEEP (seq_sleeper, seq_sleep_flag, 4); + n--; + } +} + +static void +seq_reset (void) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int i; + + int chn; + + sound_stop_timer (); + seq_time = GET_TIME (); + prev_input_time = 0; + prev_event_time = 0; + + qlen = qhead = qtail = 0; + iqlen = iqhead = iqtail = 0; + + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + synth_devs[i]->reset (i); + + if (seq_mode == SEQ_2) + { + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + for (chn = 0; chn < 16; chn++) + synth_devs[i]->controller (i, chn, 0xfe, 0); /* All notes off */ + } + else + { + for (i = 0; i < max_mididev; i++) + if (midi_written[i]) /* + * Midi used. Some notes may still be playing + */ + { + /* + * Sending just a ACTIVE SENSING message should be enough to stop all + * playing notes. Since there are devices not recognizing the + * active sensing, we have to send some all notes off messages also. + */ + midi_outc (i, 0xfe); + + for (chn = 0; chn < 16; chn++) + { + midi_outc (i, + (unsigned char) (0xb0 + (chn & 0xff))); /* + * Channel + * msg + */ + midi_outc (i, 0x7b); /* + * All notes off + */ + midi_outc (i, 0); /* + * Dummy parameter + */ + } + + midi_devs[i]->close (i); + + midi_written[i] = 0; + midi_opened[i] = 0; + } + } + + seq_playing = 0; + + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) + printk ("Sequencer Warning: Unexpected sleeping process\n"); + +} + +static void +seq_panic (void) +{ + /* + * This routine is called by the application in case the user + * wants to reset the system to the default state. + */ + + seq_reset (); + + /* + * Since some of the devices don't recognize the active sensing and + * all notes off messages, we have to shut all notes manually. + * + * TO BE IMPLEMENTED LATER + */ + + /* + * Also return the controllers to their default states + */ +} + +int +sequencer_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + int midi_dev, orig_dev; + int mode = file->mode & O_ACCMODE; + + orig_dev = dev = dev >> 4; + + switch (cmd) + { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + if (seq_mode != SEQ_2) + return RET_ERROR (EINVAL); + return tmr->ioctl (tmr_no, cmd, arg); + break; + + case SNDCTL_TMR_SELECT: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + if (seq_mode != SEQ_2) + return RET_ERROR (EINVAL); + pending_timer = IOCTL_IN (arg); + + if (pending_timer < 0 || pending_timer >= num_sound_timers) + { + pending_timer = -1; + return RET_ERROR (EINVAL); + } + + return IOCTL_OUT (arg, pending_timer); + break; + + case SNDCTL_SEQ_PANIC: + seq_panic (); + break; + + case SNDCTL_SEQ_SYNC: + if (dev) /* + * Patch manager + */ + return RET_ERROR (EIO); + + if (mode == OPEN_READ) + return 0; + while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag)) + seq_sync (); + if (qlen) + return RET_ERROR (EINTR); + else + return 0; + break; + + case SNDCTL_SEQ_RESET: + if (dev) /* + * Patch manager + */ + return RET_ERROR (EIO); + + seq_reset (); + return 0; + break; + + case SNDCTL_SEQ_TESTMIDI: + if (dev) /* + * Patch manager + */ + return RET_ERROR (EIO); + + midi_dev = IOCTL_IN (arg); + if (midi_dev >= max_mididev) + return RET_ERROR (ENXIO); + + if (!midi_opened[midi_dev]) + { + int err, mode; + + mode = file->mode & O_ACCMODE; + if ((err = midi_devs[midi_dev]->open (midi_dev, mode, + sequencer_midi_input, + sequencer_midi_output)) < 0) + return err; + } + + midi_opened[midi_dev] = 1; + + return 0; + break; + + case SNDCTL_SEQ_GETINCOUNT: + if (dev) /* + * Patch manager + */ + return RET_ERROR (EIO); + + if (mode == OPEN_WRITE) + return 0; + return IOCTL_OUT (arg, iqlen); + break; + + case SNDCTL_SEQ_GETOUTCOUNT: + + if (mode == OPEN_READ) + return 0; + return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen); + break; + + case SNDCTL_SEQ_CTRLRATE: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + /* + * If *arg == 0, just return the current rate + */ + if (seq_mode == SEQ_2) + return tmr->ioctl (tmr_no, cmd, arg); + + if (IOCTL_IN (arg) != 0) + return RET_ERROR (EINVAL); + + return IOCTL_OUT (arg, HZ); + break; + + case SNDCTL_SEQ_RESETSAMPLES: + dev = IOCTL_IN (arg); + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return RET_ERROR (EBUSY); + + if (!orig_dev && pmgr_present[dev]) + pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0); + + return synth_devs[dev]->ioctl (dev, cmd, arg); + break; + + case SNDCTL_SEQ_NRSYNTHS: + return IOCTL_OUT (arg, max_synthdev); + break; + + case SNDCTL_SEQ_NRMIDIS: + return IOCTL_OUT (arg, max_mididev); + break; + + case SNDCTL_SYNTH_MEMAVL: + { + int dev = IOCTL_IN (arg); + + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return RET_ERROR (EBUSY); + + return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg)); + } + break; + + case SNDCTL_FM_4OP_ENABLE: + { + int dev = IOCTL_IN (arg); + + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return RET_ERROR (ENXIO); + + synth_devs[dev]->ioctl (dev, cmd, arg); + return 0; + } + break; + + case SNDCTL_SYNTH_INFO: + { + struct synth_info inf; + int dev; + + IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf)); + dev = inf.device; + + if (dev < 0 || dev >= max_synthdev) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return RET_ERROR (EBUSY); + + return synth_devs[dev]->ioctl (dev, cmd, arg); + } + break; + + case SNDCTL_MIDI_INFO: + { + struct midi_info inf; + int dev; + + IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf)); + dev = inf.device; + + if (dev < 0 || dev >= max_mididev) + return RET_ERROR (ENXIO); + + IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf)); + return 0; + } + break; + + case SNDCTL_PMGR_IFACE: + { + struct patmgr_info *inf; + int dev, err; + + inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf)); + + IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf)); + dev = inf->device; + + if (dev < 0 || dev >= num_synths) + { + KERNEL_FREE (inf); + return RET_ERROR (ENXIO); + } + + if (!synth_devs[dev]->pmgr_interface) + { + KERNEL_FREE (inf); + return RET_ERROR (ENXIO); + } + + if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1) + { + KERNEL_FREE (inf); + return err; + } + + IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf)); + KERNEL_FREE (inf); + return 0; + } + break; + + case SNDCTL_PMGR_ACCESS: + { + struct patmgr_info *inf; + int dev, err; + + inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf)); + + IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf)); + dev = inf->device; + + if (dev < 0 || dev >= num_synths) + { + KERNEL_FREE (inf); + return RET_ERROR (ENXIO); + } + + if (!pmgr_present[dev]) + { + KERNEL_FREE (inf); + return RET_ERROR (ESRCH); + } + + if ((err = pmgr_access (dev, inf)) < 0) + { + KERNEL_FREE (inf); + return err; + } + + IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf)); + KERNEL_FREE (inf); + return 0; + } + break; + + case SNDCTL_SEQ_TRESHOLD: + { + int tmp = IOCTL_IN (arg); + + if (dev) /* + * Patch manager + */ + return RET_ERROR (EIO); + + if (tmp < 1) + tmp = 1; + if (tmp >= SEQ_MAX_QUEUE) + tmp = SEQ_MAX_QUEUE - 1; + output_treshold = tmp; + return 0; + } + break; + + case SNDCTL_MIDI_PRETIME: + { + int val = IOCTL_IN (arg); + + if (val < 0) + val = 0; + + val = (HZ * val) / 10; + pre_event_timeout = val; + return IOCTL_OUT (arg, val); + } + break; + + default: + if (dev) /* + * Patch manager + */ + return RET_ERROR (EIO); + + if (mode == OPEN_READ) + return RET_ERROR (EIO); + + if (!synth_devs[0]) + return RET_ERROR (ENXIO); + if (!(synth_open_mask & (1 << 0))) + return RET_ERROR (ENXIO); + return synth_devs[0]->ioctl (0, cmd, arg); + break; + } + + return RET_ERROR (EINVAL); +} + +#ifdef ALLOW_SELECT +int +sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +{ + unsigned long flags; + + dev = dev >> 4; + + switch (sel_type) + { + case SEL_IN: + if (!iqlen) + { + DISABLE_INTR (flags); + midi_sleep_flag.mode = WK_SLEEP; + select_wait (&midi_sleeper, wait); + RESTORE_INTR (flags); + return 0; + } + return 1; + break; + + case SEL_OUT: + if (qlen >= SEQ_MAX_QUEUE) + { + DISABLE_INTR (flags); + seq_sleep_flag.mode = WK_SLEEP; + select_wait (&seq_sleeper, wait); + RESTORE_INTR (flags); + return 0; + } + return 1; + break; + + case SEL_EX: + return 0; + } + + return 0; +} + +#endif + +void +sequencer_timer (void) +{ + seq_startplay (); +} + +int +note_to_freq (int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + int notes[] = + { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +unsigned long +compute_finetune (unsigned long base_freq, int bend, int range) +{ + unsigned long amount; + int negative, semitones, cents, multiplier = 1; + + if (!bend) + return base_freq; + if (!range) + return base_freq; + + if (!base_freq) + return base_freq; + + if (range >= 8192) + range = 8191; + + bend = bend * range / 8192; + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) + { + multiplier *= 4; + bend -= 2400; + } + + semitones = bend / 100; + cents = bend % 100; + + amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) + / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* + * Bend down + */ + else + return (base_freq * amount) / 10000; /* + * Bend up + */ +} + + +long +sequencer_init (long mem_start) +{ + + sequencer_ok = 1; + PERMANENT_MALLOC (unsigned char *, queue, SEQ_MAX_QUEUE * EV_SZ, mem_start); + PERMANENT_MALLOC (unsigned char *, iqueue, SEQ_MAX_QUEUE * IEV_SZ, mem_start); + + return mem_start; +} + +#else +/* + * Stub version + */ +int +sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +sequencer_open (int dev, struct fileinfo *file) +{ + return RET_ERROR (ENXIO); +} + +void +sequencer_release (int dev, struct fileinfo *file) +{ +} +int +sequencer_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + return RET_ERROR (EIO); +} + +int +sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig) +{ + return RET_ERROR (EIO); +} + +long +sequencer_init (long mem_start) +{ + return mem_start; +} + +int +sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +{ + return RET_ERROR (EIO); +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h new file mode 100644 index 00000000000..eccd9b67336 --- /dev/null +++ b/sys/i386/isa/sound/sound_calls.h @@ -0,0 +1,243 @@ +/* + * DMA buffer calls + */ + +int DMAbuf_open(int dev, int mode); +int DMAbuf_release(int dev, int mode); +int DMAbuf_getwrbuffer(int dev, char **buf, int *size); +int DMAbuf_getrdbuffer(int dev, char **buf, int *len); +int DMAbuf_rmchars(int dev, int buff_no, int c); +int DMAbuf_start_output(int dev, int buff_no, int l); +int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local); +long DMAbuf_init(long mem_start); +int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); +int DMAbuf_open_dma (int chan); +void DMAbuf_close_dma (int chan); +void DMAbuf_reset_dma (int chan); +void DMAbuf_inputintr(int dev); +void DMAbuf_outputintr(int dev, int underflow_flag); + +/* + * System calls for /dev/dsp and /dev/audio + */ + +int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int audio_open (int dev, struct fileinfo *file); +void audio_release (int dev, struct fileinfo *file); +int audio_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +long audio_init (long mem_start); + +/* + * System calls for the /dev/sequencer + */ + +int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sequencer_open (int dev, struct fileinfo *file); +void sequencer_release (int dev, struct fileinfo *file); +int sequencer_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +long sequencer_init (long mem_start); +void sequencer_timer(void); +int note_to_freq(int note_num); +unsigned long compute_finetune(unsigned long base_freq, int bend, int range); +void seq_input_event(unsigned char *event, int len); +void seq_copy_to_input (unsigned char *event, int len); + +#ifdef ALLOW_SELECT +int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait); +#endif + +/* + * System calls for the /dev/midi + */ + +int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int MIDIbuf_open (int dev, struct fileinfo *file); +void MIDIbuf_release (int dev, struct fileinfo *file); +int MIDIbuf_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); +long MIDIbuf_init(long mem_start); + +#ifdef ALLOW_SELECT +int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, select_table * wait); +#endif + +/* + * System calls for the generic midi interface. + * + */ + +long CMIDI_init (long mem_start); +int CMIDI_open (int dev, struct fileinfo *file); +int CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int CMIDI_close (int dev, struct fileinfo *file); + +/* + * + * Misc calls from various sources + */ + +/* From pro_midi.c */ + +long pro_midi_attach(long mem_start); +int pro_midi_open(int dev, int mode); +void pro_midi_close(int dev); +int pro_midi_write(int dev, snd_rw_buf *uio); +int pro_midi_read(int dev, snd_rw_buf *uio); + +/* From soundcard.c */ +long soundcard_init(long mem_start); +void tenmicrosec(void); +void request_sound_timer (int count); +void sound_stop_timer(void); +int snd_ioctl_return(int *addr, int value); +int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int)); +void snd_release_irq(int vect); +void sound_dma_malloc(int dev); +void sound_dma_free(int dev); + +/* From sound_switch.c */ +int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sound_open_sw (int dev, struct fileinfo *file); +void sound_release_sw (int dev, struct fileinfo *file); +int sound_ioctl_sw (int dev, struct fileinfo *file, + unsigned int cmd, unsigned long arg); + +/* From sb_dsp.c */ +int sb_dsp_detect (struct address_info *hw_config); +long sb_dsp_init (long mem_start, struct address_info *hw_config); +void sb_dsp_disable_midi(void); +int sb_get_irq(void); +void sb_free_irq(void); +int sb_dsp_command (unsigned char val); +int sb_reset_dsp (void); + +/* From sb16_dsp.c */ +void sb16_dsp_interrupt (int unused); +long sb16_dsp_init(long mem_start, struct address_info *hw_config); +int sb16_dsp_detect(struct address_info *hw_config); + +/* From sb16_midi.c */ +void sb16midiintr (int unit); +long attach_sb16midi(long mem_start, struct address_info * hw_config); +int probe_sb16midi(struct address_info *hw_config); +void sb_midi_interrupt(int dummy); + +/* From sb_midi.c */ +void sb_midi_init(int model); + +/* From sb_mixer.c */ +void sb_setmixer (unsigned int port, unsigned int value); +int sb_getmixer (unsigned int port); +void sb_mixer_set_stereo(int mode); +int sb_mixer_init(int major_model); + +/* From opl3.c */ +int opl3_detect (int ioaddr); +long opl3_init(long mem_start); + +/* From sb_card.c */ +long attach_sb_card(long mem_start, struct address_info *hw_config); +int probe_sb(struct address_info *hw_config); + +/* From adlib_card.c */ +long attach_adlib_card(long mem_start, struct address_info *hw_config); +int probe_adlib(struct address_info *hw_config); + +/* From pas_card.c */ +long attach_pas_card(long mem_start, struct address_info *hw_config); +int probe_pas(struct address_info *hw_config); +int pas_set_intr(int mask); +int pas_remove_intr(int mask); +unsigned char pas_read(int ioaddr); +void pas_write(unsigned char data, int ioaddr); + +/* From pas_audio.c */ +void pas_pcm_interrupt(unsigned char status, int cause); +long pas_pcm_init(long mem_start, struct address_info *hw_config); + +/* From pas_mixer.c */ +int pas_init_mixer(void); + +/* From pas_midi.c */ +long pas_midi_init(long mem_start); +void pas_midi_interrupt(void); + +/* From gus_card.c */ +long attach_gus_card(long mem_start, struct address_info * hw_config); +int probe_gus(struct address_info *hw_config); +int gus_set_midi_irq(int num); +void gusintr(int); +long attach_gus_db16(long mem_start, struct address_info * hw_config); +int probe_gus_db16(struct address_info *hw_config); + +/* From gus_wave.c */ +int gus_wave_detect(int baseaddr); +long gus_wave_init(long mem_start, int irq, int dma); +void gus_voice_irq(void); +unsigned char gus_read8 (int reg); +void gus_write8(int reg, unsigned int data); +void guswave_dma_irq(void); +void gus_delay(void); +int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg); + +/* From gus_midi.c */ +long gus_midi_init(long mem_start); +void gus_midi_interrupt(int dummy); + +/* From mpu401.c */ +long attach_mpu401(long mem_start, struct address_info * hw_config); +int probe_mpu401(struct address_info *hw_config); + +/* From uart6850.c */ +long attach_uart6850(long mem_start, struct address_info * hw_config); +int probe_uart6850(struct address_info *hw_config); + +/* From opl3.c */ +void enable_opl3_mode(int left, int right, int both); + +/* From patmgr.c */ +int pmgr_open(int dev); +void pmgr_release(int dev); +int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count); +int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count); +int pmgr_access(int dev, struct patmgr_info *rec); +int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2, + unsigned long parm3, unsigned long parm4); + +/* From ics2101.c */ +long ics2101_mixer_init(long mem_start); + +/* From sound_timer.c */ +void sound_timer_init(int io_base); +void sound_timer_interrupt(void); + +/* From ad1848.c */ +void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture); +int ad1848_detect (int io_base); +void ad1848_interrupt (int dev); +long attach_ms_sound(long mem_start, struct address_info * hw_config); +int probe_ms_sound(struct address_info *hw_config); + +/* From pss.c */ +int probe_pss (struct address_info *hw_config); +long attach_pss (long mem_start, struct address_info *hw_config); + +int pss_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int pss_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int pss_open (int dev, struct fileinfo *file); +void pss_release (int dev, struct fileinfo *file); +int pss_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int pss_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +long pss_init(long mem_start); diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h new file mode 100644 index 00000000000..6c53c8cdd0c --- /dev/null +++ b/sys/i386/isa/sound/sound_config.h @@ -0,0 +1,258 @@ +/* sound_config.h + * + * A driver for Soundcards, misc configuration parameters. + * + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "local.h" + +#if defined(ISC) || defined(SCO) || defined(SVR42) +#define GENERIC_SYSV +#endif + +/* + * Disable the AD1848 driver if there are no other drivers requiring it. + */ + +#if defined(EXCLUDE_GUS16) && defined(EXCLUDE_MSS) && defined(EXCLUDE_PSS) && defined(EXCLUDE_GUSMAX) +#define EXCLUDE_AD1848 +#endif + +#undef CONFIGURE_SOUNDCARD +#undef DYNAMIC_BUFFER + +#ifdef KERNEL_SOUNDCARD +#define CONFIGURE_SOUNDCARD +#define DYNAMIC_BUFFER +#undef LOADABLE_SOUNDCARD +#endif + +#ifdef EXCLUDE_SEQUENCER +#define EXCLUDE_MIDI +#define EXCLUDE_YM3812 +#define EXCLUDE_OPL3 +#endif + +#ifndef SND_DEFAULT_ENABLE +#define SND_DEFAULT_ENABLE 1 +#endif + +#ifdef CONFIGURE_SOUNDCARD + +/* ****** IO-address, DMA and IRQ settings **** + +If your card has nonstandard I/O address or IRQ number, change defines + for the following settings in your kernel Makefile */ + +#ifndef SBC_BASE +#define SBC_BASE 0x220 /* 0x220 is the factory default. */ +#endif + +#ifndef SBC_IRQ +#define SBC_IRQ 7 /* IQR7 is the factory default. */ +#endif + +#ifndef SBC_DMA +#define SBC_DMA 1 +#endif + +#ifndef SB16_DMA +#define SB16_DMA 6 +#endif + +#ifndef SB16MIDI_BASE +#define SB16MIDI_BASE 0x300 +#endif + +#ifndef PAS_BASE +#define PAS_BASE 0x388 +#endif + +#ifndef PAS_IRQ +#define PAS_IRQ 5 +#endif + +#ifndef PAS_DMA +#define PAS_DMA 3 +#endif + +#ifndef GUS_BASE +#define GUS_BASE 0x220 +#endif + +#ifndef GUS_IRQ +#define GUS_IRQ 15 +#endif + +#ifndef GUS_MIDI_IRQ +#define GUS_MIDI_IRQ GUS_IRQ +#endif + +#ifndef GUS_DMA +#define GUS_DMA 6 +#endif + +#ifndef MPU_BASE +#define MPU_BASE 0x330 +#endif + +#ifndef MPU_IRQ +#define MPU_IRQ 6 +#endif + +/* Echo Personal Sound System */ +#ifndef PSS_BASE +#define PSS_BASE 0x220 /* 0x240 or */ +#endif + +#ifndef PSS_IRQ +#define PSS_IRQ 7 +#endif + +#ifndef PSS_DMA +#define PSS_DMA 1 +#endif + +#ifndef MAX_REALTIME_FACTOR +#define MAX_REALTIME_FACTOR 4 +#endif + +/************* PCM DMA buffer sizes *******************/ + +/* If you are using high playback or recording speeds, the default buffersize + is too small. DSP_BUFFSIZE must be 64k or less. + + A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and + 4k for SB. + + If you change the DSP_BUFFSIZE, don't modify this file. + Use the make config command instead. */ + +#ifndef DSP_BUFFSIZE +#define DSP_BUFFSIZE (4096) +#endif + +#ifndef DSP_BUFFCOUNT +#define DSP_BUFFCOUNT 2 /* 2 is recommended. */ +#endif + +#define DMA_AUTOINIT 0x10 + +#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ + +/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the + driver. (There is no need to alter this) */ +#define SEQ_MAX_QUEUE 1024 + +#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ +/* 128 instruments for general MIDI setup and 16 unassigned */ + +/* + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequecer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +#define DSP_DEFAULT_SPEED 8000 + +#define ON 1 +#define OFF 0 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 2 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 + +struct fileinfo { + int mode; /* Open mode */ + }; + +struct address_info { + int io_base; + int irq; + int dma; +}; + +#define SYNTH_MAX_VOICES 32 + +struct voice_alloc_info { + int max_voice; + int used_voices; + int ptr; /* For device specific use */ + unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ + }; + +struct channel_info { + int pgm_num; + unsigned char controllers[128]; + }; + +/* + * Process wakeup reasons + */ +#define WK_NONE 0x00 +#define WK_WAKEUP 0x01 +#define WK_TIMEOUT 0x02 +#define WK_SIGNAL 0x04 +#define WK_SLEEP 0x08 + +#define OPEN_READ 1 +#define OPEN_WRITE 2 +#define OPEN_READWRITE 3 + +#include "os.h" +#include "sound_calls.h" +#include "dev_table.h" + +#ifndef DEB +#define DEB(x) + +#define TIMER_ARMED 121234 +#define TIMER_NOT_ARMED 1 + +#define FUTURE_VERSION +#endif + +#endif diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c new file mode 100644 index 00000000000..fe73aea93e0 --- /dev/null +++ b/sys/i386/isa/sound/sound_switch.c @@ -0,0 +1,528 @@ +/* + * sound/sound_switch.c + * + * The system call switch + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +struct sbc_device + { + int usecount; + }; + +static struct sbc_device sbc_devices[SND_NDEVS] = +{ + {0}}; + +static int in_use = 0; /* + + + * * * * Total # of open device files + * (excluding * * * minor 0) */ + +/* + * /dev/sndstatus -device + */ +static char *status_buf = NULL; +static int status_len, status_ptr; +static int status_busy = 0; + +static int +put_status (char *s) +{ + int l; + + for (l = 0; l < 256, s[l]; l++); /* + * l=strlen(s); + */ + + if (status_len + l >= 4000) + return 0; + + memcpy (&status_buf[status_len], s, l); + status_len += l; + + return 1; +} + +static int +put_status_int (unsigned int val, int radix) +{ + int l, v; + + static char hx[] = "0123456789abcdef"; + char buf[11]; + + if (!val) + return put_status ("0"); + + l = 0; + buf[10] = 0; + + while (val) + { + v = val % radix; + val = val / radix; + + buf[9 - l] = hx[v]; + l++; + } + + if (status_len + l >= 4000) + return 0; + + memcpy (&status_buf[status_len], &buf[10 - l], l); + status_len += l; + + return 1; +} + +static void +init_status (void) +{ + /* + * Write the status information to the status_buf and update status_len. + * There is a limit of 4000 bytes for the data. + */ + + int i; + + status_ptr = 0; + + put_status ("Sound Driver:" SOUND_VERSION_STRING + " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@" + SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")" + "\n"); + + if (!put_status ("Config options: ")) + return; + if (!put_status_int (SELECTED_SOUND_OPTIONS, 16)) + return; + + if (!put_status ("\n\nInstalled drivers: \n")) + return; + + for (i = 0; i < (num_sound_drivers - 1); i++) + { + if (!put_status ("Type ")) + return; + if (!put_status_int (sound_drivers[i].card_type, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (sound_drivers[i].name)) + return; + + if (!put_status ("\n")) + return; + } + + if (!put_status ("\n\nCard config: \n")) + return; + + for (i = 0; i < (num_sound_cards - 1); i++) + { + int drv; + + if (!snd_installed_cards[i].enabled) + if (!put_status ("(")) + return; + + /* + * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return; + * if (!put_status (": ")) return; + */ + + if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1) + if (!put_status (sound_drivers[drv].name)) + return; + + if (!put_status (" at 0x")) + return; + if (!put_status_int (snd_installed_cards[i].config.io_base, 16)) + return; + if (!put_status (" irq ")) + return; + if (!put_status_int (snd_installed_cards[i].config.irq, 10)) + return; + if (!put_status (" drq ")) + return; + if (!put_status_int (snd_installed_cards[i].config.dma, 10)) + return; + + if (!snd_installed_cards[i].enabled) + if (!put_status (")")) + return; + + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nPCM devices:\n")) + return; + + for (i = 0; i < num_audiodevs; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (audio_devs[i]->name)) + return; + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nSynth devices:\n")) + return; + + for (i = 0; i < num_synths; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (synth_devs[i]->info->name)) + return; + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nMidi devices:\n")) + return; + + for (i = 0; i < num_midis; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (midi_devs[i]->info.name)) + return; + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nMIDI Timers:\n")) + return; + + for (i = 0; i < num_sound_timers; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (sound_timer_devs[i]->info.name)) + return; + if (!put_status ("\n")) + return; + } + + if (!put_status ("\n")) + return; + if (!put_status_int (num_mixers, 10)) + return; + if (!put_status (" mixer(s) installed\n")) + return; +} + +static int +read_status (snd_rw_buf * buf, int count) +{ + /* + * Return at most 'count' bytes from the status_buf. + */ + int l, c; + + l = count; + c = status_len - status_ptr; + + if (l > c) + l = c; + if (l <= 0) + return 0; + + COPY_TO_USER (buf, 0, &status_buf[status_ptr], l); + status_ptr += l; + + return l; +} + +int +sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count)); + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + return read_status (buf, count); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_read (dev, file, buf, count); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_read (dev, file, buf, count); + break; + +#ifndef EXCLUDE_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_read (dev, file, buf, count); +#endif + +#ifndef EXCLUDE_PSS + case SND_DEV_PSS: + return pss_read (dev, file, buf, count); +#endif + + default: + printk ("Sound: Undefined minor device %d\n", dev); + } + + return RET_ERROR (EPERM); +} + +int +sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + + DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count)); + + switch (dev & 0x0f) + { + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_write (dev, file, buf, count); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_write (dev, file, buf, count); + break; + +#ifndef EXCLUDE_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_write (dev, file, buf, count); +#endif + +#ifndef EXCLUDE_PSS + case SND_DEV_PSS: + return pss_write (dev, file, buf, count); +#endif + + default: + return RET_ERROR (EPERM); + } + + return count; +} + +int +sound_open_sw (int dev, struct fileinfo *file) +{ + int retval; + + DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount)); + + if ((dev >= SND_NDEVS) || (dev < 0)) + { + printk ("Invalid minor device %d\n", dev); + return RET_ERROR (ENXIO); + } + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + if (status_busy) + return RET_ERROR (EBUSY); + status_busy = 1; + if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL) + return RET_ERROR (EIO); + status_len = status_ptr = 0; + init_status (); + break; + + case SND_DEV_CTL: + return 0; + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + if ((retval = sequencer_open (dev, file)) < 0) + return retval; + break; + +#ifndef EXCLUDE_MIDI + case SND_DEV_MIDIN: + if ((retval = MIDIbuf_open (dev, file)) < 0) + return retval; + break; +#endif + +#ifndef EXCLUDE_PSS + case SND_DEV_PSS: + if ((retval = pss_open (dev, file)) < 0) + return retval; + break; +#endif + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + if ((retval = audio_open (dev, file)) < 0) + return retval; + break; + + default: + printk ("Invalid minor device %d\n", dev); + return RET_ERROR (ENXIO); + } + + sbc_devices[dev].usecount++; + in_use++; + + return 0; +} + +void +sound_release_sw (int dev, struct fileinfo *file) +{ + + DEB (printk ("sound_release_sw(dev=%d)\n", dev)); + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + if (status_buf) + KERNEL_FREE (status_buf); + status_buf = NULL; + status_busy = 0; + break; + + case SND_DEV_CTL: + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + sequencer_release (dev, file); + break; + +#ifndef EXCLUDE_MIDI + case SND_DEV_MIDIN: + MIDIbuf_release (dev, file); + break; +#endif + +#ifndef EXCLUDE_PSS + case SND_DEV_PSS: + pss_release (dev, file); + break; +#endif + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + audio_release (dev, file); + break; + + default: + printk ("Sound error: Releasing unknown device 0x%02x\n", dev); + } + + sbc_devices[dev].usecount--; + in_use--; +} + +int +sound_ioctl_sw (int dev, struct fileinfo *file, + unsigned int cmd, unsigned long arg) +{ + DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + + if ((dev & 0x0f) != SND_DEV_CTL && num_mixers > 0) + if ((cmd >> 8) & 0xff == 'M') /* + * Mixer ioctl + */ + return mixer_devs[0]->ioctl (0, cmd, arg); + + switch (dev & 0x0f) + { + + case SND_DEV_CTL: + + if (!num_mixers) + return RET_ERROR (ENXIO); + + dev = dev >> 4; + + if (dev >= num_mixers) + return RET_ERROR (ENXIO); + + return mixer_devs[dev]->ioctl (dev, cmd, arg); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_ioctl (dev, file, cmd, arg); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_ioctl (dev, file, cmd, arg); + break; + +#ifndef EXCLUDE_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_ioctl (dev, file, cmd, arg); + break; +#endif + +#ifndef EXCLUDE_PSS + case SND_DEV_PSS: + return pss_ioctl (dev, file, cmd, arg); + break; +#endif + + default: + return RET_ERROR (EPERM); + break; + } + + return RET_ERROR (EPERM); +} + +#endif diff --git a/sys/i386/isa/sound/sound_timer.c b/sys/i386/isa/sound/sound_timer.c new file mode 100644 index 00000000000..c6544c82a7e --- /dev/null +++ b/sys/i386/isa/sound/sound_timer.c @@ -0,0 +1,406 @@ +/* + * sound/sound_timer.c + * + * Timer for the level 2 interface of the /dev/sequencer. Uses the + * 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define SEQUENCER_C +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812))) + +static volatile int initialized = 0, opened = 0, tmr_running = 0; +static volatile time_t tmr_offs, tmr_ctr; +static volatile unsigned long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile unsigned long curr_ticks; +static volatile unsigned long next_event_time; +static unsigned long prev_event_time; +static volatile int select_addr, data_addr; +static volatile int curr_timer = 0; +static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ + + +static void +timer_command (unsigned int addr, unsigned int val) +{ + int i; + + OUTB ((unsigned char) (addr & 0xff), select_addr); + + for (i = 0; i < 2; i++) + INB (select_addr); + + OUTB ((unsigned char) (val & 0xff), data_addr); + + for (i = 0; i < 2; i++) + INB (select_addr); +} + +static void +arm_timer (int timer, unsigned int interval) +{ + + curr_timer = timer; + + if (timer == 1) + { + gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */ + gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */ + timer_command (0x04, 0x01); /* Start timer 1 */ + } + else + { + gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */ + gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */ + timer_command (0x04, 0x02); /* Start timer 2 */ + } +} + +static unsigned long +tmr2ticks (int tmr_value) +{ + /* + * Convert timer ticks to MIDI ticks + */ + + unsigned long tmp; + unsigned long scale; + + tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ + + scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ + + return (tmp + (scale / 2)) / scale; +} + +static void +reprogram_timer (void) +{ + unsigned long usecs_per_tick; + int timer_no, resolution; + int divisor; + + usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); + + /* + * Don't kill the system by setting too high timer rate + */ + if (usecs_per_tick < 2000) + usecs_per_tick = 2000; + + if (usecs_per_tick > (256 * 80)) + { + timer_no = 2; + resolution = 320; /* usec */ + } + else + { + timer_no = 1; + resolution = 80; /* usec */ + } + + divisor = (usecs_per_tick + (resolution / 2)) / resolution; + usecs_per_tmr = divisor * resolution; + + arm_timer (timer_no, divisor); +} + +static void +tmr_reset (void) +{ + unsigned long flags; + + DISABLE_INTR (flags); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = 0xffffffff; + prev_event_time = 0; + curr_ticks = 0; + RESTORE_INTR (flags); +} + +static int +timer_open (int dev, int mode) +{ + if (opened) + return RET_ERROR (EBUSY); + + tmr_reset (); + curr_tempo = 60; + curr_timebase = HZ; + opened = 1; + reprogram_timer (); + + return 0; +} + +static void +timer_close (int dev) +{ + opened = tmr_running = 0; + gus_write8 (0x45, 0); /* Disable both timers */ +} + +static int +timer_event (int dev, unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset (); + tmr_running = 1; + reprogram_timer (); + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + reprogram_timer (); + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks (tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + reprogram_timer (); + } + break; + + case TMR_ECHO: + seq_copy_to_input (event, 8); + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static unsigned long +timer_get_time (int dev) +{ + if (!opened) + return 0; + + return curr_ticks; +} + +static int +timer_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + switch (cmd) + { + case SNDCTL_TMR_SOURCE: + return IOCTL_OUT (arg, TMR_INTERNAL); + break; + + case SNDCTL_TMR_START: + tmr_reset (); + tmr_running = 1; + return 0; + break; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + return 0; + break; + + case SNDCTL_TMR_CONTINUE: + tmr_running = 1; + return 0; + break; + + case SNDCTL_TMR_TIMEBASE: + { + int val = IOCTL_IN (arg); + + if (val) + { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + + return IOCTL_OUT (arg, curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val = IOCTL_IN (arg); + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks (tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + reprogram_timer (); + } + + return IOCTL_OUT (arg, curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + if (IOCTL_IN (arg) != 0) /* Can't change */ + return RET_ERROR (EINVAL); + + return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); + break; + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default: + } + + return RET_ERROR (EINVAL); +} + +static void +timer_arm (int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +static struct sound_timer_operations sound_timer = +{ + {"OPL-3/GUS Timer", 0}, + 1, /* Priority */ + 0, /* Local device link */ + timer_open, + timer_close, + timer_event, + timer_get_time, + timer_ioctl, + timer_arm +}; + +void +sound_timer_interrupt (void) +{ + gus_write8 (0x45, 0); /* Ack IRQ */ + timer_command (4, 0x80); /* Reset IRQ flags */ + + if (!opened) + return; + + if (curr_timer == 1) + gus_write8 (0x45, 0x04); /* Start timer 1 again */ + else + gus_write8 (0x45, 0x08); /* Start timer 2 again */ + + if (!tmr_running) + return; + + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks (tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = 0xffffffff; + sequencer_timer (); + } +} + +void +sound_timer_init (int io_base) +{ + int n; + + if (initialized) + return; /* There is already a similar timer */ + + select_addr = io_base; + data_addr = io_base + 1; + + initialized = 1; + +#if 1 + if (num_sound_timers >= MAX_TIMER_DEV) + n = 0; /* Overwrite the system timer */ + else + n = num_sound_timers++; +#else + n = 0; +#endif + + sound_timer_devs[n] = &sound_timer; +} + +#endif +#endif diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c new file mode 100644 index 00000000000..b9e6ee5f1b7 --- /dev/null +++ b/sys/i386/isa/sound/soundcard.c @@ -0,0 +1,401 @@ +/* + * sound/386bsd/soundcard.c + * + * Soundcard driver for 386BSD. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "dev_table.h" + +u_int snd1mask; +u_int snd2mask; +u_int snd3mask; +u_int snd4mask; +u_int snd5mask; +u_int snd6mask; +u_int snd7mask; +u_int snd8mask; +u_int snd9mask; + +#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;} + +static int timer_running = 0; + +static int soundcards_installed = 0; /* Number of installed + * soundcards */ +static int soundcard_configured = 0; + +static struct fileinfo files[SND_NDEVS]; + +int sndprobe (struct isa_device *dev); +int sndattach (struct isa_device *dev); +int sndopen (dev_t dev, int flags); +int sndclose (dev_t dev, int flags); +int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode); +int sndread (int dev, struct uio *uio); +int sndwrite (int dev, struct uio *uio); +int sndselect (int dev, int rw); +static void sound_mem_init(void); + +unsigned long +get_time(void) +{ + extern struct timeval time; + struct timeval timecopy; + int x; + + x = splclock(); + timecopy = time; + splx(x); + return timecopy.tv_usec/(1000000/HZ) + + (unsigned long)timecopy.tv_sec*HZ; +} + +int +sndread (int dev, struct uio *buf) +{ + int count = buf->uio_resid; + + dev = minor (dev); + + FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count)); +} + +int +sndwrite (int dev, struct uio *buf) +{ + int count = buf->uio_resid; + + dev = minor (dev); + + FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count)); +} + +int +sndopen (dev_t dev, int flags) +{ + int retval; + + dev = minor (dev); + + if (!soundcard_configured && dev) + { + printk ("SoundCard Error: The soundcard system has not been configured\n"); + FIX_RETURN (-ENODEV); + } + + files[dev].mode = 0; + + if (flags & FREAD && flags & FWRITE) + files[dev].mode = OPEN_READWRITE; + else if (flags & FREAD) + files[dev].mode = OPEN_READ; + else if (flags & FWRITE) + files[dev].mode = OPEN_WRITE; + + FIX_RETURN(sound_open_sw (dev, &files[dev])); +} + +int +sndclose (dev_t dev, int flags) +{ + + dev = minor (dev); + + sound_release_sw(dev, &files[dev]); + FIX_RETURN (0); +} + +int +sndioctl (dev_t dev, int cmd, caddr_t arg, int mode) +{ + dev = minor (dev); + + FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg)); +} + +int +sndselect (int dev, int rw) +{ + dev = minor (dev); + + DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + + FIX_RETURN (0); +} + +static short +ipri_to_irq (unsigned short ipri) +{ + /* + * Converts the ipri (bitmask) to the corresponding irq number + */ + int irq; + + for (irq = 0; irq < 16; irq++) + if (ipri == (1 << irq)) + return irq; + + return -1; /* Invalid argument */ +} + +int +sndprobe (struct isa_device *dev) +{ + struct address_info hw_config; + + hw_config.io_base = dev->id_iobase; + hw_config.irq = ipri_to_irq (dev->id_irq); + hw_config.dma = dev->id_drq; + + return sndtable_probe (dev->id_unit, &hw_config); +} + +int +sndattach (struct isa_device *dev) +{ + int i; + static int midi_initialized = 0; + static int seq_initialized = 0; + static int generic_midi_initialized = 0; + unsigned long mem_start = 0xefffffff; + struct address_info hw_config; + + hw_config.io_base = dev->id_iobase; + hw_config.irq = ipri_to_irq (dev->id_irq); + hw_config.dma = dev->id_drq; + + if (dev->id_unit) /* Card init */ + if (!sndtable_init_card (dev->id_unit, &hw_config)) + { + printf (" "); + return FALSE; + } + + /* + * Init the high level sound driver + */ + + if (!(soundcards_installed = sndtable_get_cardcount ())) + { + printf (" "); + return FALSE; /* No cards detected */ + } + + printf("\n"); + +#ifndef EXCLUDE_AUDIO + if (num_audiodevs) /* Audio devices present */ + { + mem_start = DMAbuf_init (mem_start); + mem_start = audio_init (mem_start); + sound_mem_init (); + } + + soundcard_configured = 1; +#endif + + if (num_midis && !midi_initialized) + { + midi_initialized = 1; + mem_start = MIDIbuf_init (mem_start); + } + + if ((num_midis + num_synths) && !seq_initialized) + { + seq_initialized = 1; + mem_start = sequencer_init (mem_start); + } + + return TRUE; +} + +void +tenmicrosec (void) +{ + int i; + + for (i = 0; i < 16; i++) + inb (0x80); +} + +void +request_sound_timer (int count) +{ + static int current = 0; + int tmp = count; + + if (count < 0) + timeout (sequencer_timer, 0, -count); + else + { + + if (count < current) + current = 0; /* Timer restarted */ + + count = count - current; + + current = tmp; + + if (!count) + count = 1; + + timeout (sequencer_timer, 0, count); + } + timer_running = 1; +} + +void +sound_stop_timer (void) +{ + if (timer_running) + untimeout (sequencer_timer, 0); + timer_running = 0; +} + +#ifndef EXCLUDE_AUDIO +static void +sound_mem_init (void) +{ + int i, dev; + unsigned long dma_pagesize; + static unsigned long dsp_init_mask = 0; + + for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ + if (!(dsp_init_mask & (1 << dev))) /* Not already done */ + if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0) + { + dsp_init_mask |= (1 << dev); + + if (sound_dma_automode[dev]) + { + sound_dma_automode[dev] = 0; /* Not possible with 386BSD */ + } + +#if 0 + if (sound_buffcounts[dev] == 1) + { + sound_buffcounts[dev] = 2; + sound_buffsizes[dev] /= 2; + } + + if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */ + sound_buffsizes[dev] = 65536; + + if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536) + dma_pagesize = 131072; /* 128k */ + else + dma_pagesize = 65536; + + /* More sanity checks */ + + if (sound_buffsizes[dev] > dma_pagesize) + sound_buffsizes[dev] = dma_pagesize; + sound_buffsizes[dev] &= 0xfffff000; /* Truncate to n*4k */ + if (sound_buffsizes[dev] < 4096) + sound_buffsizes[dev] = 4096; +#else + dma_pagesize = 4096; + sound_buffsizes[dev] = 4096; + sound_buffcounts[dev] = 16; /* 16*4k -> 64k */ +#endif + + /* Now allocate the buffers */ + + for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++) + { + /* + * The DMA buffer allocation algorithm hogs memory. We allocate + * a memory area which is two times the requires size. This + * guarantees that it contains at least one valid DMA buffer. + * + * This really needs some kind of finetuning. + */ + char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT); + unsigned long addr, rounded, start, end; + + if (tmpbuf == NULL) + { + printk ("snd: Unable to allocate %d bytes of buffer\n", + 2 * sound_buffsizes[dev]); + return; + } + + addr = kvtop (tmpbuf); + /* + * Align the start address if required + */ + start = (addr & ~(dma_pagesize - 1)); + end = ((addr+sound_buffsizes[dev]-1) & ~(dma_pagesize - 1)); + + if (start != end) + rounded = end; + else + rounded = addr; /* Fits to the same DMA page */ + + snd_raw_buf[dev][snd_raw_count[dev]] = + &tmpbuf[rounded - addr]; /* Compute offset */ + /* + * Use virtual address as the physical address, since + * isa_dmastart performs the phys address computation. + */ + snd_raw_buf_phys[dev][snd_raw_count[dev]] = + (unsigned long) snd_raw_buf[dev][snd_raw_count[dev]]; + } + } /* for dev */ + +} + +#endif + +struct isa_driver snddriver = +{sndprobe, sndattach, "snd"}; + +int +snd_ioctl_return (int *addr, int value) +{ + if (value < 0) + return value; /* Error */ + suword (addr, value); + return 0; +} + +int +snd_set_irq_handler (int interrupt_level, void(*hndlr)(int)) +{ + return 1; +} + +void +snd_release_irq(int vect) +{ +} + +#endif diff --git a/sys/i386/isa/sound/sys_timer.c b/sys/i386/isa/sound/sys_timer.c new file mode 100644 index 00000000000..1000045a42d --- /dev/null +++ b/sys/i386/isa/sound/sys_timer.c @@ -0,0 +1,304 @@ +/* + * sound/sys_timer.c + * + * The default timer for the Level 2 sequencer interface + * Uses the (100HZ) timer of kernel. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define SEQUENCER_C +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#ifndef EXCLUDE_SEQUENCER + +static volatile int opened = 0, tmr_running = 0; +static volatile time_t tmr_offs, tmr_ctr; +static volatile unsigned long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile unsigned long curr_ticks; +static volatile unsigned long next_event_time; +static unsigned long prev_event_time; + +static void poll_def_tmr (unsigned long dummy); + +DEFINE_TIMER (def_tmr, poll_def_tmr); + +static unsigned long +tmr2ticks (int tmr_value) +{ + /* + * Convert system timer ticks (HZ) to MIDI ticks + */ + + unsigned long tmp; + unsigned long scale; + + tmp = (tmr_value * 1000) / HZ;/* Convert to msecs */ + + scale = (60 * 1000) / (curr_tempo * curr_timebase); /* msecs per MIDI tick */ + + return (tmp + (scale / 2)) / scale; +} + +static void +poll_def_tmr (unsigned long dummy) +{ + + if (opened) + { + ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1); + + if (tmr_running) + { + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks (tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = 0xffffffff; + sequencer_timer (); + } + } + } +} + +static void +tmr_reset (void) +{ + unsigned long flags; + + DISABLE_INTR (flags); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = 0xffffffff; + prev_event_time = 0; + curr_ticks = 0; + RESTORE_INTR (flags); +} + +static int +def_tmr_open (int dev, int mode) +{ + if (opened) + return RET_ERROR (EBUSY); + + tmr_reset (); + curr_tempo = 60; + curr_timebase = HZ; + opened = 1; + + ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1); + + return 0; +} + +static void +def_tmr_close (int dev) +{ + opened = tmr_running = 0; +} + +static int +def_tmr_event (int dev, unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset (); + tmr_running = 1; + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks (tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input (event, 8); + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static unsigned long +def_tmr_get_time (int dev) +{ + if (!opened) + return 0; + + return curr_ticks; +} + +static int +def_tmr_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + switch (cmd) + { + case SNDCTL_TMR_SOURCE: + return IOCTL_OUT (arg, TMR_INTERNAL); + break; + + case SNDCTL_TMR_START: + tmr_reset (); + tmr_running = 1; + return 0; + break; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + return 0; + break; + + case SNDCTL_TMR_CONTINUE: + tmr_running = 1; + return 0; + break; + + case SNDCTL_TMR_TIMEBASE: + { + int val = IOCTL_IN (arg); + + if (val) + { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + + return IOCTL_OUT (arg, curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val = IOCTL_IN (arg); + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks (tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + } + + return IOCTL_OUT (arg, curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + if (IOCTL_IN (arg) != 0) /* Can't change */ + return RET_ERROR (EINVAL); + + return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); + break; + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default:; + } + + return RET_ERROR (EINVAL); +} + +static void +def_tmr_arm (int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +struct sound_timer_operations default_sound_timer = +{ + {"System Timer", 0}, + 0, /* Priority */ + 0, /* Local device link */ + def_tmr_open, + def_tmr_close, + def_tmr_event, + def_tmr_get_time, + def_tmr_ioctl, + def_tmr_arm +}; + +#endif +#endif diff --git a/sys/i386/isa/sound/tuning.h b/sys/i386/isa/sound/tuning.h new file mode 100644 index 00000000000..858e1fe6c61 --- /dev/null +++ b/sys/i386/isa/sound/tuning.h @@ -0,0 +1,29 @@ +#ifdef SEQUENCER_C + +unsigned short semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; +#else +extern unsigned short semitone_tuning[24]; +extern unsigned short cent_tuning[100]; +#endif diff --git a/sys/i386/isa/sound/uart6850.c b/sys/i386/isa/sound/uart6850.c new file mode 100644 index 00000000000..ca631352127 --- /dev/null +++ b/sys/i386/isa/sound/uart6850.c @@ -0,0 +1,323 @@ +/* + * sound/uart6850.c + * + * Copyright by Hannu Savolainen 1993 + * + * Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: + * added 6850 support, used with COVOX SoundMaster II and custom cards. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI) + +#define DATAPORT (uart6850_base) /* + * * * Midi6850 Data I/O Port on IBM + * */ +#define COMDPORT (uart6850_base+1) /* + * * * Midi6850 Command Port on IBM */ +#define STATPORT (uart6850_base+1) /* + * * * Midi6850 Status Port on IBM */ + +#define uart6850_status() INB(STATPORT) +#define input_avail() ((uart6850_status()&INPUT_AVAIL)) +#define output_ready() ((uart6850_status()&OUTPUT_READY)) +#define uart6850_cmd(cmd) OUTB(cmd, COMDPORT) +#define uart6850_read() INB(DATAPORT) +#define uart6850_write(byte) OUTB(byte, DATAPORT) + +#define OUTPUT_READY 0x02 /* + * * * Mask for Data Read Ready Bit */ +#define INPUT_AVAIL 0x01 /* + * * * Mask for Data Send Ready Bit */ + +#define UART_RESET 0x95 /* + * * * 6850 Total Reset Command */ +#define UART_MODE_ON 0x03 /* + * * * 6850 Send/Receive UART Mode */ + +static int uart6850_opened = 0; +static int uart6850_base = 0x330; +static int uart6850_irq; +static int uart6850_detected = 0; +static int my_dev; + +static int reset_uart6850 (void); +static void (*midi_input_intr) (int dev, unsigned char data); + +static void +uart6850_input_loop (void) +{ + int count; + + count = 10; + + while (count) /* + * Not timed out + */ + if (input_avail ()) + { + unsigned char c = uart6850_read (); + + count = 100; + + if (uart6850_opened & OPEN_READ) + midi_input_intr (my_dev, c); + } + else + while (!input_avail () && count) + count--; +} + +void +m6850intr (int unit) +{ + printk ("M"); + if (input_avail ()) + uart6850_input_loop (); +} + +/* + * It looks like there is no input interrupts in the UART mode. Let's try + * polling. + */ + +static void +poll_uart6850 (unsigned long dummy) +{ + unsigned long flags; + + DEFINE_TIMER (uart6850_timer, poll_uart6850); + + if (!(uart6850_opened & OPEN_READ)) + return; /* + * No longer required + */ + + DISABLE_INTR (flags); + + if (input_avail ()) + uart6850_input_loop (); + + ACTIVATE_TIMER (uart6850_timer, poll_uart6850, 1); /* + * Come back later + */ + + RESTORE_INTR (flags); +} + +static int +uart6850_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + if (uart6850_opened) + { + printk ("Midi6850: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + uart6850_cmd (UART_RESET); + + uart6850_input_loop (); + + midi_input_intr = input; + uart6850_opened = mode; + poll_uart6850 (0); /* + * Enable input polling + */ + + return 0; +} + +static void +uart6850_close (int dev) +{ + uart6850_cmd (UART_MODE_ON); + + uart6850_opened = 0; +} + +static int +uart6850_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + DISABLE_INTR (flags); + + if (input_avail ()) + uart6850_input_loop (); + + RESTORE_INTR (flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* + * Wait + */ + + if (!output_ready ()) + { + printk ("Midi6850: Timeout\n"); + return 0; + } + + uart6850_write (midi_byte); + return 1; +} + +static int +uart6850_command (int dev, unsigned char *midi_byte) +{ + return 1; +} + +static int +uart6850_start_read (int dev) +{ + return 0; +} + +static int +uart6850_end_read (int dev) +{ + return 0; +} + +static int +uart6850_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +uart6850_kick (int dev) +{ +} + +static int +uart6850_buffer_status (int dev) +{ + return 0; /* + * No data in buffers + */ +} + +#define MIDI_SYNTH_NAME "6850 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations uart6850_operations = +{ + {"6850 UART", 0, 0, SNDCARD_UART6850}, + &std_midi_synth, + uart6850_open, + uart6850_close, + uart6850_ioctl, + uart6850_out, + uart6850_start_read, + uart6850_end_read, + uart6850_kick, + uart6850_command, + uart6850_buffer_status +}; + + +long +attach_uart6850 (long mem_start, struct address_info *hw_config) +{ + int ok, timeout; + unsigned long flags; + + if (num_midis >= MAX_MIDI_DEV) + { + printk ("Sound: Too many midi devices detected\n"); + return mem_start; + } + + uart6850_base = hw_config->io_base; + uart6850_irq = hw_config->irq; + + if (!uart6850_detected) + return RET_ERROR (EIO); + + DISABLE_INTR (flags); + + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* + * Wait + */ + uart6850_cmd (UART_MODE_ON); + + ok = 1; + + RESTORE_INTR (flags); + + printk (" <6850 Midi Interface>"); + + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &uart6850_operations; + return mem_start; +} + +static int +reset_uart6850 (void) +{ + uart6850_read (); + return 1; /* + * OK + */ +} + + +int +probe_uart6850 (struct address_info *hw_config) +{ + int ok = 0; + + uart6850_base = hw_config->io_base; + uart6850_irq = hw_config->irq; + + if (snd_set_irq_handler (uart6850_irq, m6850intr) < 0) + return 0; + + ok = reset_uart6850 (); + + uart6850_detected = ok; + return ok; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/ulaw.h b/sys/i386/isa/sound/ulaw.h new file mode 100644 index 00000000000..be9f92d9984 --- /dev/null +++ b/sys/i386/isa/sound/ulaw.h @@ -0,0 +1,69 @@ +static unsigned char ulaw_dsp[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 5, 9, 13, 17, 21, 25, 29, 33, + 37, 41, 45, 49, 53, 57, 61, 65, + 68, 70, 72, 74, 76, 78, 80, 82, + 84, 86, 88, 90, 92, 94, 96, 98, + 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, + 115, 116, 116, 117, 117, 118, 118, 119, + 119, 120, 120, 121, 121, 122, 122, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 126, 126, 126, 126, 127, 127, + 127, 127, 127, 127, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 252, 248, 244, 240, 236, 232, 228, 224, + 220, 216, 212, 208, 204, 200, 196, 192, + 189, 187, 185, 183, 181, 179, 177, 175, + 173, 171, 169, 167, 165, 163, 161, 159, + 157, 156, 155, 154, 153, 152, 151, 150, + 149, 148, 147, 146, 145, 144, 143, 142, + 142, 141, 141, 140, 140, 139, 139, 138, + 138, 137, 137, 136, 136, 135, 135, 134, + 134, 134, 133, 133, 133, 133, 132, 132, + 132, 132, 131, 131, 131, 131, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 128, 128, 128, 128, +}; + +static unsigned char dsp_ulaw[] = { + 31, 31, 31, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 34, 34, 34, 35, + 35, 35, 35, 36, 36, 36, 36, 37, + 37, 37, 37, 38, 38, 38, 38, 39, + 39, 39, 39, 40, 40, 40, 40, 41, + 41, 41, 41, 42, 42, 42, 42, 43, + 43, 43, 43, 44, 44, 44, 44, 45, + 45, 45, 45, 46, 46, 46, 46, 47, + 47, 47, 47, 48, 48, 49, 49, 50, + 50, 51, 51, 52, 52, 53, 53, 54, + 54, 55, 55, 56, 56, 57, 57, 58, + 58, 59, 59, 60, 60, 61, 61, 62, + 62, 63, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 81, 83, 85, 87, 89, + 91, 93, 95, 99, 103, 107, 111, 119, + 255, 247, 239, 235, 231, 227, 223, 221, + 219, 217, 215, 213, 211, 209, 207, 206, + 205, 204, 203, 202, 201, 200, 199, 198, + 197, 196, 195, 194, 193, 192, 191, 191, + 190, 190, 189, 189, 188, 188, 187, 187, + 186, 186, 185, 185, 184, 184, 183, 183, + 182, 182, 181, 181, 180, 180, 179, 179, + 178, 178, 177, 177, 176, 176, 175, 175, + 175, 175, 174, 174, 174, 174, 173, 173, + 173, 173, 172, 172, 172, 172, 171, 171, + 171, 171, 170, 170, 170, 170, 169, 169, + 169, 169, 168, 168, 168, 168, 167, 167, + 167, 167, 166, 166, 166, 166, 165, 165, + 165, 165, 164, 164, 164, 164, 163, 163, + 163, 163, 162, 162, 162, 162, 161, 161, + 161, 161, 160, 160, 160, 160, 159, 159, +};