Hannu Savolainen's VoxWare original sound drivers, version 2.9.

This commit is contained in:
Steven Wallace 1994-10-01 01:33:47 +00:00
commit fbb347fe32
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/AMANCIO/; revision=3252
53 changed files with 23418 additions and 0 deletions

View File

@ -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.

View File

@ -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.
*
*/

80
sys/i386/isa/sound/README Normal file
View File

@ -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)

953
sys/i386/isa/sound/ad1848.c Normal file
View File

@ -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

View File

@ -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

424
sys/i386/isa/sound/audio.c Normal file
View File

@ -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

View File

@ -0,0 +1,830 @@
/*
* sound/configure.c - Configuration program for the Linux Sound Driver
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <stdio.h>
#define B(x) (1 << (x))
/*
* Option numbers
*/
#define OPT_PAS 0
#define OPT_SB 1
#define OPT_ADLIB 2
#define OPT_LAST_MUTUAL 2
#define OPT_GUS 3
#define OPT_MPU401 4
#define OPT_UART6850 5
#define OPT_PSS 6
#define OPT_GUS16 7
#define OPT_GUSMAX 8
#define OPT_MSS 9
#define OPT_HIGHLEVEL 10 /* This must be same than the next one */
#define OPT_SBPRO 10
#define OPT_SB16 11
#define OPT_AUDIO 12
#define OPT_MIDI_AUTO 13
#define OPT_MIDI 14
#define OPT_YM3812_AUTO 15
#define OPT_YM3812 16
#define OPT_SEQUENCER 17
#define OPT_LAST 17 /* Must be the same than the defined OPT */
#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)| \
B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)|B(OPT_MSS))
/*
* Options that have been disabled for some reason (incompletely implemented
* and/or tested). Don't remove from this list before looking at file
* experimental.txt for further info.
*/
#define DISABLED_OPTIONS (B(OPT_PSS))
typedef struct
{
unsigned long conditions;
unsigned long exclusive_options;
char macro[20];
int verify;
int alias;
int default_answ;
}
hw_entry;
/*
* The rule table for the driver options. The first field defines a set of
* options which must be selected before this entry can be selected. The
* second field is a set of options which are not allowed with this one. If
* the fourth field is zero, the option is selected without asking
* confirmation from the user.
*
* With this version of the rule table it is possible to select just one type of
* hardware.
*
* NOTE! Keep the following table and the questions array in sync with the
* option numbering!
*/
hw_entry hw_table[] =
{
/*
* 0
*/
{0, 0, "PAS", 1, 0, 0},
{0, 0, "SB", 1, 0, 0},
{0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0},
{0, 0, "GUS", 1, 0, 0},
{0, 0, "MPU401", 1, 0, 0},
{0, 0, "UART6850", 1, 0, 0},
{0, 0, "PSS", 1, 0, 0},
{B (OPT_GUS), 0, "GUS16", 1, 0, 0},
{B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0},
{0, 0, "MSS", 1, 0, 0},
{B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1},
{B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1},
{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0, 1},
{B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI, 0},
{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0, 1},
{B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1},
{B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0, 1}
};
char *questions[] =
{
"ProAudioSpectrum 16 support",
"SoundBlaster support",
"AdLib support",
"Gravis Ultrasound support",
"MPU-401 support (NOT for SB16)",
"6850 UART Midi support",
"PSS (ECHO-ADI2111) support",
"16 bit sampling option of GUS (_NOT_ GUS MAX)",
"GUS MAX support",
"Microsoft Sound System support",
"SoundBlaster Pro support",
"SoundBlaster 16 support",
"digitized voice support",
"This should not be asked",
"MIDI interface support",
"This should not be asked",
"FM synthesizer (YM3812/OPL-3) support",
"/dev/sequencer support",
"Should I die"
};
unsigned long selected_options = 0;
int sb_dma = 0;
int
can_select_option (int nr)
{
switch (nr)
{
case 0:
fprintf (stderr, "The SoundBlaster, AdLib and ProAudioSpectrum\n"
"CARDS cannot be installed at the same time.\n\n"
"However the PAS16 has a SB emulator so you could select"
"the SoundBlaster DRIVER with it.\n");
fprintf (stderr, " - ProAudioSpectrum 16\n");
fprintf (stderr, " - SoundBlaster / SB Pro\n");
fprintf (stderr, " (Could be selected with a PAS16 also)\n");
fprintf (stderr, " - AdLib\n");
fprintf (stderr, "\nDon't enable SoundBlaster if you have GUS at 0x220!\n\n");
break;
case OPT_LAST_MUTUAL + 1:
fprintf (stderr, "\nThe following cards should work with any other cards.\n"
"CAUTION! Don't enable MPU-401 if you don't have it.\n");
break;
case OPT_HIGHLEVEL:
fprintf (stderr, "\nSelect one or more of the following options\n");
break;
}
if (hw_table[nr].conditions)
if (!(hw_table[nr].conditions & selected_options))
return 0;
if (hw_table[nr].exclusive_options)
if (hw_table[nr].exclusive_options & selected_options)
return 0;
if (DISABLED_OPTIONS & B (nr))
return 0;
return 1;
}
int
think_positively (int def_answ)
{
char answ[512];
int len;
if ((len = read (0, &answ, sizeof (answ))) < 1)
{
fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
perror ("stdin");
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (-1);
}
if (len < 2) /*
* There is an additional LF at the end
*/
return def_answ;
answ[len - 1] = 0;
if (!strcmp (answ, "y") || !strcmp (answ, "Y"))
return 1;
return 0;
}
int
ask_value (char *format, int default_answer)
{
char answ[512];
int len, num;
play_it_again_Sam:
if ((len = read (0, &answ, sizeof (answ))) < 1)
{
fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
perror ("stdin");
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (-1);
}
if (len < 2) /*
* There is an additional LF at the end
*/
return default_answer;
answ[len - 1] = 0;
if (sscanf (answ, format, &num) != 1)
{
fprintf (stderr, "Illegal format. Try again: ");
goto play_it_again_Sam;
}
return num;
}
int
main (int argc, char *argv[])
{
int i, num, def_size, full_driver = 1;
char answ[10];
printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n");
fprintf (stderr, "\nConfiguring the sound support\n\n");
fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? ");
if (think_positively (0))
{
/*
* Select all but some most dangerous cards. These cards are difficult to
* detect reliably or conflict with some other cards (SCSI, Mitsumi)
*/
selected_options = 0xffffffff &
~(B (OPT_MPU401) | B (OPT_UART6850) | B (OPT_PSS)) &
~DISABLED_OPTIONS;
fprintf (stderr, "Note! MPU-401, PSS and 6850 UART drivers not enabled\n");
full_driver = 1;
}
else
{
fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?");
if (think_positively (0))
{
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (0);
}
/*
* Partial driver
*/
full_driver = 0;
for (i = 0; i <= OPT_LAST; i++)
if (can_select_option (i))
{
if (!(selected_options & B (i))) /*
* Not selected yet
*/
if (!hw_table[i].verify)
{
if (hw_table[i].alias)
selected_options |= B (hw_table[i].alias);
else
selected_options |= B (i);
}
else
{
int def_answ = hw_table[i].default_answ;
fprintf (stderr,
def_answ ? " %s (y/n) ? " : " %s (n/y) ? ",
questions[i]);
if (think_positively (def_answ))
if (hw_table[i].alias)
selected_options |= B (hw_table[i].alias);
else
selected_options |= B (i);
}
}
}
if (selected_options & B (OPT_SBPRO))
{
fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
if (think_positively (0))
printf("#define __SGNXPRO__\n");
}
if (selected_options & B (OPT_SB16))
selected_options |= B (OPT_SBPRO);
if (selected_options & B (OPT_PSS))
{
genld_again:
fprintf
(stderr,
"if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
"then you must include the LD in the kernel.\n"
"(do you wish to include a LD) ? ");
if (think_positively (0))
{
char path[512];
fprintf (stderr,
"Enter the path to your LD file (pwd is sound): ");
scanf ("%s", path);
fprintf (stderr, "including LD file %s\n", path);
selected_options |= B (OPT_SB) | B (OPT_MPU401) | B (OPT_ADLIB);
/* Gen LD header */
{
int fd;
int count;
char c;
int i = 0;
if ((fd = open (path, 0)) > 0)
{
FILE *sf = fopen ("synth-ld.h", "w");
fprintf (sf, "/* automaticaly generated by configure */\n");
fprintf (sf, "unsigned char pss_synth[] = {\n");
while (1)
{
count = read (fd, &c, 1);
if (count == 0)
break;
if (i != 0 && (i % 10) == 0)
fprintf (sf, "\n");
fprintf (sf, "0x%02x,", c & 0xFFL);
i++;
}
fprintf (sf, "};\n"
"#define pss_synthLen %d\n", i);
fclose (sf);
close (fd);
}
else
{
fprintf (stderr, "couldn't open %s as the ld file\n",
path);
fprintf (stderr, "try again with correct path? ");
if (think_positively (1))
goto genld_again;
}
}
}
else
{
FILE *sf = fopen ("synth-ld.h", "w");
fprintf (sf, "/* automaticaly generated by configure */\n");
fprintf (sf, "unsigned char pss_synth[1];\n"
"#define pss_synthLen 0\n");
fclose (sf);
}
}
if (!(selected_options & ANY_DEVS))
{
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n\n");
exit (0);
}
else
printf ("#define KERNEL_SOUNDCARD\n");
for (i = 0; i <= OPT_LAST; i++)
if (!hw_table[i].alias)
if (selected_options & B (i))
printf ("#undef EXCLUDE_%s\n", hw_table[i].macro);
else
printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
/*
* IRQ and DMA settings
*/
printf ("\n");
#if defined(linux)
if ((selected_options & B (OPT_SB)) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
{
fprintf (stderr, "\nI/O base for SB?\n"
"The factory default is 220\n"
"Enter the SB I/O base: ");
num = ask_value ("%x", 0x220);
fprintf (stderr, "SB I/O base set to %03x\n", num);
printf ("#define SBC_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for SoundBlaster?\n"
"The IRQ address is defined by the jumpers on your card.\n"
"The factory default is either 5 or 7 (depending on the model).\n"
"Valid values are 9(=2), 5, 7 and 10.\n"
"Enter the value: ");
num = ask_value ("%d", 7);
if (num != 9 && num != 5 && num != 7 && num != 10)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "SoundBlaster IRQ set to %d\n", num);
printf ("#define SBC_IRQ %d\n", num);
if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_PSS)))
{
fprintf (stderr, "\nDMA channel for SoundBlaster?\n"
"For SB 1.0, 1.5 and 2.0 this MUST be 1\n"
"SB Pro supports DMA channels 0, 1 and 3 (jumper)\n"
"For SB16 give the 8 bit DMA# here\n"
"The default value is 1\n"
"Enter the value: ");
num = ask_value ("%d", 1);
if (num < 0 || num > 3)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 1;
}
fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
printf ("#define SBC_DMA %d\n", num);
sb_dma = num;
}
if (selected_options & B (OPT_SB16))
{
fprintf (stderr, "\n16 bit DMA channel for SoundBlaster 16?\n"
"Possible values are 5, 6 or 7\n"
"The default value is 6\n"
"Enter the value: ");
num = ask_value ("%d", 6);
if ((num < 5 || num > 7) && (num != sb_dma))
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 6;
}
fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
printf ("#define SB16_DMA %d\n", num);
fprintf (stderr, "\nI/O base for SB16 Midi?\n"
"Possible values are 300 and 330\n"
"The factory default is 330\n"
"Enter the SB16 Midi I/O base: ");
num = ask_value ("%x", 0x330);
fprintf (stderr, "SB16 Midi I/O base set to %03x\n", num);
printf ("#define SB16MIDI_BASE 0x%03x\n", num);
}
}
if (selected_options & B (OPT_PAS))
{
if (selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
{
fprintf (stderr, "\nIRQ number for ProAudioSpectrum?\n"
"The recommended value is the IRQ used under DOS.\n"
"Please refer to the ProAudioSpectrum User's Guide.\n"
"The default value is 10.\n"
"Enter the value: ");
num = ask_value ("%d", 10);
if (num == 6 || num < 3 || num > 15 || num == 2) /*
* Illegal
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 10;
}
fprintf (stderr, "ProAudioSpectrum IRQ set to %d\n", num);
printf ("#define PAS_IRQ %d\n", num);
}
if (selected_options & B (OPT_AUDIO))
{
fprintf (stderr, "\nDMA number for ProAudioSpectrum?\n"
"The recommended value is the DMA channel under DOS.\n"
"Please refer to the ProAudioSpectrum User's Guide.\n"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nProAudioSpectrum DMA set to %d\n", num);
printf ("#define PAS_DMA %d\n", num);
}
}
if (selected_options & B (OPT_GUS))
{
fprintf (stderr, "\nI/O base for Gravis Ultrasound?\n"
"Valid choices are 210, 220, 230, 240, 250 or 260\n"
"The factory default is 220\n"
"Enter the GUS I/O base: ");
num = ask_value ("%x", 0x220);
if ((num > 0x260) || ((num & 0xf0f) != 0x200) || ((num & 0x0f0) > 0x060))
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 0x220;
}
if ((selected_options & B (OPT_SB)) && (num == 0x220))
{
fprintf (stderr, "FATAL ERROR!!!!!!!!!!!!!!\n"
"\t0x220 cannot be used if SoundBlaster is enabled.\n"
"\tRun the config again.\n");
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (-1);
}
fprintf (stderr, "GUS I/O base set to %03x\n", num);
printf ("#define GUS_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for Gravis UltraSound?\n"
"The recommended value is the IRQ used under DOS.\n"
"Please refer to the Gravis Ultrasound User's Guide.\n"
"The default value is 15.\n"
"Enter the value: ");
num = ask_value ("%d", 15);
if (num == 6 || num < 3 || num > 15 || num == 2) /*
* Invalid
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 15;
}
fprintf (stderr, "Gravis UltraSound IRQ set to %d\n", num);
printf ("#define GUS_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for Gravis UltraSound?\n"
"The recommended value is the DMA channel under DOS.\n"
"Please refer to the Gravis Ultrasound User's Guide.\n"
"The default value is 6\n"
"Enter the value: ");
num = ask_value ("%d", 6);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 6;
}
fprintf (stderr, "\nGravis UltraSound DMA set to %d\n", num);
printf ("#define GUS_DMA %d\n", num);
}
if (selected_options & B (OPT_MPU401))
{
fprintf (stderr, "\nI/O base for MPU-401?\n"
"The factory default is 330\n"
"Enter the MPU-401 I/O base: ");
num = ask_value ("%x", 0x330);
fprintf (stderr, "MPU-401 I/O base set to %03x\n", num);
printf ("#define MPU_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for MPU-401?\n"
"Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
"The default value is 9.\n"
"Enter the value: ");
num = ask_value ("%d", 9);
if (num == 6 || num < 3 || num > 15) /*
* Used for floppy
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 5;
}
fprintf (stderr, "MPU-401 IRQ set to %d\n", num);
printf ("#define MPU_IRQ %d\n", num);
}
if (selected_options & B (OPT_UART6850))
{
fprintf (stderr, "\nI/O base for 6850 UART Midi?\n"
"Be carefull. No defaults.\n"
"Enter the 6850 UART I/O base: ");
num = ask_value ("%x", 0);
if (num == 0)
{
/*
* Invalid value entered
*/
printf ("#define EXCLUDE_UART6850\n");
}
else
{
fprintf (stderr, "6850 UART I/O base set to %03x\n", num);
printf ("#define U6850_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for 6850 UART?\n"
"Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
"The default value is 5.\n"
"Enter the value: ");
num = ask_value ("%d", 5);
if (num == 6 || num < 3 || num > 15) /*
* Used for floppy
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 5;
}
fprintf (stderr, "6850 UART IRQ set to %d\n", num);
printf ("#define U6850_IRQ %d\n", num);
}
}
if (selected_options & B (OPT_PSS))
{
fprintf (stderr, "\nI/O base for PSS?\n"
"The factory default is 220\n"
"Enter the PSS I/O base: ");
num = ask_value ("%x", 0x220);
fprintf (stderr, "PSS I/O base set to %03x\n", num);
printf ("#define PSS_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for PSS?\n"
"Valid numbers are: 3, 4, 5, 7, 9(=2) or 10.\n"
"The default value is 10.\n"
"Enter the value: ");
num = ask_value ("%d", 10);
if (num == 6 || num < 3 || num > 15) /* Used for floppy */
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "PSS IRQ set to %d\n", num);
printf ("#define PSS_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for ECHO-PSS?\n"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nECHO-PSS DMA set to %d\n", num);
printf ("#define PSS_DMA %d\n", num);
}
if (selected_options & B (OPT_MSS))
{
fprintf (stderr, "\nI/O base for MSS (MS Sound System)?\n"
"The factory default is 530\n"
"Other possible values are 604, E80 or F40\n"
"Enter the MSS I/O base: ");
num = ask_value ("%x", 0x530);
fprintf (stderr, "MSS I/O base set to %03x\n", num);
printf ("#define MSS_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for MSS?\n"
"Valid numbers are: 7, 9(=2), 10 and 11.\n"
"The default value is 10.\n"
"Enter the value: ");
num = ask_value ("%d", 10);
if (num == 6 || num < 3 || num > 15) /* Used for floppy */
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "MSS IRQ set to %d\n", num);
printf ("#define MSS_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for MSS?\n"
"Valid values are 1 and 3 (sometimes 0)"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nMSS DMA set to %d\n", num);
printf ("#define MSS_DMA %d\n", num);
}
if (selected_options & B (OPT_GUS16))
{
fprintf (stderr, "\nI/O base for GUS16 (GUS 16 bit sampling option)?\n"
"The factory default is 530\n"
"Other possible values are 604, E80 or F40\n"
"Enter the GUS16 I/O base: ");
num = ask_value ("%x", 0x530);
fprintf (stderr, "GUS16 I/O base set to %03x\n", num);
printf ("#define GUS16_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for GUS16?\n"
"Valid numbers are: 3, 4, 5, 7, or 9(=2).\n"
"The default value is 7.\n"
"Enter the value: ");
num = ask_value ("%d", 7);
if (num == 6 || num < 3 || num > 15) /* Used for floppy */
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "GUS16 IRQ set to %d\n", num);
printf ("#define GUS16_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for GUS16?\n"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num < 0 || num > 3)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nGUS16 DMA set to %d\n", num);
printf ("#define GUS16_DMA %d\n", num);
}
#endif
if (selected_options & B (OPT_AUDIO))
{
def_size = 16384;
if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_SB16)))
def_size = 32768;
#ifndef __386BSD__
if ((selected_options & (B (OPT_PAS) | B (OPT_PAS) | B (OPT_GUS16) | B (OPT_GUSMAX) |
B (OPT_MSS) | B (OPT_PSS))) &&
!full_driver)
def_size = 65536; /*
* PAS16 or SB16
*/
#endif
fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n"
"%d is recommended value for this configuration.\n"
"Enter the value: ", def_size);
num = ask_value ("%d", def_size);
if (num != 4096 && num != 16384 && num != 32768 && num != 65536)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = def_size;
}
fprintf (stderr, "The DMA buffer size set to %d\n", num);
printf ("#define DSP_BUFFSIZE %d\n", num);
}
printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
fprintf (stderr, "The sound driver is now configured.\n");
#if defined(SCO) || defined(ISC) || defined(SYSV)
fprintf (stderr, "Remember to update the System file\n");
#endif
exit (0);
}

View File

@ -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

View File

@ -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_ */

964
sys/i386/isa/sound/dmabuf.c Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
};

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 ""

View File

@ -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 */
};

View File

@ -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

View File

@ -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

View File

@ -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

1722
sys/i386/isa/sound/mpu401.c Normal file

File diff suppressed because it is too large Load Diff

1225
sys/i386/isa/sound/opl3.c Normal file

File diff suppressed because it is too large Load Diff

260
sys/i386/isa/sound/opl3.h Normal file
View File

@ -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}}
};

306
sys/i386/isa/sound/os.h Normal file
View File

@ -0,0 +1,306 @@
#ifndef _OS_H_
#define _OS_H_
/*
* OS specific settings for 386BSD
*
* This chould be used as an example when porting the driver to a new
* operating systems.
*
* What you should do is to rewrite the soundcard.c and os.h (this file).
* You should create a new subdirectory and put these two files there.
* In addition you have to do a makefile.<OS>.
*
* If you have to make changes to other than these two files, please contact me
* before making the changes. It's possible that I have already made the
* change.
*/
/*
* Insert here the includes required by your kernel.
*/
#include "param.h"
#include "systm.h"
#include "ioctl.h"
#include "tty.h"
#include "proc.h"
#include "user.h"
#include "conf.h"
#include "file.h"
#include "uio.h"
/* #include "kernel.h" */
#include "syslog.h"
#include "errno.h"
#include "malloc.h"
#include "buf.h"
#include "i386/isa/isa_device.h"
/* These few lines are used by 386BSD (only??). */
#if NSND > 0
#define KERNEL_SOUNDCARD
#else
#undef KERNEL_SOUNDCARD
#endif
/*
* Rest of the file is compiled only if the driver is really required.
*/
#ifdef CONFIGURE_SOUNDCARD
/*
* select() is currently implemented in Linux specific way. Don't enable.
* I don't remember what the SHORT_BANNERS means so forget it.
*/
#undef ALLOW_SELECT
#define SHORT_BANNERS
/* The soundcard.h could be in a nonstandard place so inclyde it here. */
#include "soundcard.h"
/*
* Here is the first portability problem. Every OS has it's own way to
* pass a pointer to the buffer in read() and write() calls. In Linux it's
* just a char*. In BSD it's struct uio. This parameter is passed to
* all functions called from read() or write(). Since nothing can be
* assumed about this structure, the driver uses set of macros for
* accessing the user buffer.
*
* The driver reads/writes bytes in the user buffer sequentially which
* means that calls like uiomove() can be used.
*
* snd_rw_buf is the type which is passed to the device file specific
* read() and write() calls.
*
* The following macros are used to move date to and from the
* user buffer. These macros should be used only when the
* target or source parameter has snd_rw_buf type.
* The offs parameter is a offset relative to the beginning of
* the user buffer. In Linux the offset is required but for example
* BSD passes the offset info in the uio structure. It could be usefull
* if these macros verify that the offs parameter and the value in
* the snd_rw_buf structure are equal.
*/
typedef struct uio snd_rw_buf;
/*
* Move bytes from the buffer which the application given in a
* write() call.
* offs is position relative to the beginning of the buffer in
* user space. The count is number of bytes to be moved.
*/
#define COPY_FROM_USER(target, source, offs, count) \
if (uiomove(target, count, source)) { \
printf ("sb: Bad copyin()!\n"); \
} else
/* Like COPY_FOM_USER but for writes. */
#define COPY_TO_USER(target, offs, source, count) \
if (uiomove(source, count, target)) { \
printf ("sb: Bad copyout()!\n"); \
} else
/*
* The following macros are like COPY_*_USER but work just with one byte (8bit),
* short (16 bit) or long (32 bit) at a time.
* The same restrictions apply than for COPY_*_USER
*/
#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, addr);}
#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, addr);}
#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, addr);}
#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, addr);}
/*
* The way how the ioctl arguments are passed is another nonportable thing.
* In Linux the argument is just a pointer directly to the user segment. On
* 386bsd the data is already moved to the kernel space. The following
* macros should handle the difference.
*/
/*
* IOCTL_FROM_USER is used to copy a record pointed by the argument to
* a buffer in the kernel space. On 386bsd it can be done just by calling
* memcpy. With Linux a memcpy_from_fs should be called instead.
* Parameters of the following macros are like in the COPY_*_USER macros.
*/
/*
* When the ioctl argument points to a record or array (longer than 32 bits),
* the macros IOCTL_*_USER are used. It's assumed that the source and target
* parameters are direct memory addresses.
*/
#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);}
#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);}
/* The following macros are used if the ioctl argument points to 32 bit int */
#define IOCTL_IN(arg) (*(int*)arg)
#define IOCTL_OUT(arg, ret) *(int*)arg = ret
/*
* When the driver displays something to the console, printk() will be called.
* The name can be changed here.
*/
#define printk printf
/*
* The following macros define an interface to the process management.
*/
struct snd_wait {
int mode; int aborting;
};
/*
* DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define
* a structure which can be passed as a parameter to a sleep(). The second
* parameter is name of a flag variable (must be defined as int).
*/
#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \
static volatile struct snd_wait flag = {0}
/* Like the above but defines an array of wait queues and flags */
#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \
static volatile struct snd_wait flag = {{0}}
#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
#define SET_ABORT_FLAG(q, f) f.aborting = 1
#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP)
/*
* This driver handles interrupts little bit nonstandard way. The following
* macro is used to test if the current process has received a signal which
* is aborts the process. This macro is called from close() to see if the
* buffers should be discarded. If this kind info is not available, a constant
* 1 or 0 could be returned (1 should be better than 0).
* I'm not sure if the following is correct for 386BSD.
*/
#define PROCESS_ABORTING(q, f) (f.aborting | curproc->p_sig)
/*
* The following macro calls sleep. It should be implemented such that
* the process is resumed if it receives a signal. The following is propably
* not the way how it should be done on 386bsd.
* The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(),
* and the second is a workarea parameter. The third is a timeout
* in ticks. Zero means no timeout.
*/
#define DO_SLEEP(q, f, time_limit) \
{ \
int flag, chn; \
f.mode = WK_SLEEP; \
q = &chn; \
flag=tsleep(&chn, (PRIBIO-5)|PCATCH, "sndint", time_limit); \
if(flag == ERESTART) f.aborting = 1;\
else f.aborting = 0;\
f.mode &= ~WK_SLEEP; \
}
/* An the following wakes up a process */
#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wakeup(q);}
/*
* Timing macros. This driver assumes that there is a timer running in the
* kernel. The timer should return a value which is increased once at every
* timer tick. The macro HZ should return the number of such ticks/sec.
*/
#ifndef HZ
extern int hz;
#define HZ hz
#endif
/*
* GET_TIME() returns current value of the counter incremented at timer
* ticks. This can overflow, so the timeout might be real big...
*
*/
unsigned long get_time(void);
#define GET_TIME() get_time()
/*#define GET_TIME() (lbolt) /* Returns current time (1/HZ secs since boot) */
/*
* The following three macros are called before and after atomic
* code sequences. The flags parameter has always type of unsigned long.
* The macro DISABLE_INTR() should ensure that all interrupts which
* may invoke any part of the driver (timer, soundcard interrupts) are
* disabled.
* RESTORE_INTR() should return the interrupt status back to the
* state when DISABLE_INTR() was called. The flags parameter is
* a variable which can carry 32 bits of state information between
* DISABLE_INTR() and RESTORE_INTR() calls.
*/
#define DISABLE_INTR(flags) flags = splhigh()
#define RESTORE_INTR(flags) splx(flags)
/*
* INB() and OUTB() should be obvious. NOTE! The order of
* paratemeters of OUTB() is different than on some other
* operating systems.
*/
#define INB inb
/*
* The outb(0, 0x80) is just for slowdown. It's bit unsafe since
* this address could be used for something usefull.
*/
#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);}
/* memcpy() was not defined og 386bsd. Lets define it here */
#define memcpy(d, s, c) bcopy(s, d, c)
/*
* When a error (such as EINVAL) is returned by a function,
* the following macro is used. The driver assumes that a
* error is signalled by returning a negative value.
*/
#define RET_ERROR(err) -(err)
/*
KERNEL_MALLOC() allocates requested number of memory and
KERNEL_FREE is used to free it.
These macros are never called from interrupt, in addition the
nbytes will never be more than 4096 bytes. Generally the driver
will allocate memory in blocks of 4k. If the kernel has just a
page level memory allocation, 4K can be safely used as the size
(the nbytes parameter can be ignored).
*/
#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK)
#define KERNEL_FREE(addr) free(addr, M_TEMP)
/*
* The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr)
* returns size bytes of
* (kernel virtual) memory which will never get freed by the driver.
* This macro is called only during boot. The linux_ptr is a linux specific
* parameter which should be ignored in other operating systems.
* The mem_ptr is a pointer variable where the macro assigns pointer to the
* memory area. The type is the type of the mem_ptr.
*/
#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \
{mem_ptr = (typecast)malloc(size, M_DEVBUF, M_NOWAIT); \
if (!mem_ptr)panic("SOUND: Cannot allocate memory\n");}
/*
* The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if
* required. The name is the variable/name to be used and the proc is
* the procedure to be called when the timer expires.
*/
#define DEFINE_TIMER(name, proc)
/*
* The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks.
*/
#define ACTIVATE_TIMER(name, proc, time) \
timeout(proc, 0, time);
/*
* The rest of this file is not complete yet. The functions using these
* macros will not work
*/
#define ALLOC_DMA_CHN(chn) (0)
#define RELEASE_DMA_CHN(chn) (0)
#define DMA_MODE_READ 0
#define DMA_MODE_WRITE 1
#define RELEASE_IRQ(irq_no)
#endif
#endif

250
sys/i386/isa/sound/pas.h Normal file
View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

264
sys/i386/isa/sound/patmgr.c Normal file
View File

@ -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

924
sys/i386/isa/sound/pss.c Normal file
View File

@ -0,0 +1,924 @@
/* Marc.Hoffman@analog.com
This is a pss driver.
it is based on Greg.Yukna@analog.com @file{host} for DOG
Unfortunately I can't distribute the ld file needed to
make the pss card to emulate the SB stuff.
I have provided a simple interface to the PSS unlike the
DOG version. to download a new algorithim just cat it to
/dev/pss 14,9.
You really need to rebuild this with the synth.ld file
get the <synth>.ld from your dos directory maybe
voyetra\dsp001.ld
ld2inc < synth.ld > synth-ld.h
(make config does the same).
rebuild
Okay if you blow things away no problem just
main(){ioctl(open("/dev/pss"),SNDCTL_PSS_RESET)};
and everything will be okay.
At first I was going to wory about applications that were using
the sound stuff and disallow the use of /dev/pss. But for
now I figured it doesn't matter.
And if you change algos all the other applications running die off
due to DMA problems. Yeah just pull the plug and watch em die.
If the registers get hosed
main(){ioctl(open("/dev/pss"),SNDCTL_PSS_SETUP_REGISTERS)};
Probably everything else can be done via mmap
Oh if you want to develope code for the ADSP-21xx or Program the
1848 just send me mail and I will hook you up.
marc.hoffman@analog.com
*/
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PSS)
#ifndef PSS_MSS_BASE
#define PSS_MSS_BASE 0
#endif
#ifndef PSS_MPU_BASE
#define PSS_MPU_BASE 0
#endif
#ifndef PSS_MPU_IRQ
#define PSS_MPU_IRQ 0
#endif
#undef DEB
#define DEB(x) x
#include "pss.h"
static int pss_ok = 0;
static int sb_ok = 0;
static int pss_base;
static int pss_irq;
static int pss_dma;
static int gamePort = 0;
static int sbInt;
static int cdPol;
static int cdAddr = 0; /* 0x340; */
static int cdInt = 10;
/* Define these by hand in local.h */
static int wssAddr = PSS_MSS_BASE;
static int midiAddr = PSS_MPU_BASE;
static int midiInt = PSS_MPU_IRQ;
static int SoundPortAddress;
static int SoundPortData;
static int speaker = 1;
static struct pss_speaker default_speaker =
{0, 0, 0, PSS_STEREO};
DEFINE_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
#include "synth-ld.h"
static int pss_download_boot (unsigned char *block, int size);
static int pss_reset_dsp (void);
static inline void
pss_outpw (unsigned short port, unsigned short value)
{
__asm__ __volatile__ ("outw %w0, %w1"
: /* no outputs */
:"a" (value), "d" (port));
}
static inline unsigned int
pss_inpw (unsigned short port)
{
unsigned int _v;
__asm__ __volatile__ ("inw %w1,%w0"
:"=a" (_v):"d" (port), "0" (0));
return _v;
}
static void
PSS_write (int data)
{
int i, limit;
limit = GET_TIME () + 10; /* The timeout is 0.1 secods */
/*
* Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
* called while interrupts are disabled. This means that the timer is
* disabled also. However the timeout situation is a abnormal condition.
* Normally the DSP should be ready to accept commands after just couple of
* loops.
*/
for (i = 0; i < 5000000 && GET_TIME () < limit; i++)
{
if (pss_inpw (pss_base + PSS_STATUS) & PSS_WRITE_EMPTY)
{
pss_outpw (pss_base + PSS_DATA, data);
return;
}
}
printk ("PSS: DSP Command (%04x) Timeout.\n", data);
printk ("IRQ conflict???\n");
}
static void
pss_setaddr (int addr, int configAddr)
{
int val;
val = pss_inpw (configAddr);
val &= ADDR_MASK;
val |= (addr << 4);
pss_outpw (configAddr, val);
}
/*_____ pss_checkint
This function tests an interrupt number to see if
it is availible. It takes the interrupt button
as it's argument and returns TRUE if the interrupt
is ok.
*/
static int
pss_checkint (int intNum)
{
int val;
int ret;
int i;
/*_____ Set the interrupt bits */
switch (intNum)
{
case 3:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_3_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 5:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_5_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 7:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_7_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 9:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_9_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 10:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_10_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 11:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_11_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 12:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_12_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
default:
printk ("unknown interupt selected. %d\n", intNum);
return 0;
}
/*_____ Set the interrupt test bit */
val = pss_inpw (pss_base + PSS_CONFIG);
val |= INT_TEST_BIT;
pss_outpw (pss_base + PSS_CONFIG, val);
/*_____ Check if the interrupt is in use */
/*_____ Do it a few times in case there is a delay */
ret = 0;
for (i = 0; i < 5; i++)
{
val = pss_inpw (pss_base + PSS_CONFIG);
if (val & INT_TEST_PASS)
{
ret = 1;
break;
}
}
/*_____ Clear the Test bit and the interrupt bits */
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_TEST_BIT_MASK;
val &= INT_MASK;
pss_outpw (pss_base + PSS_CONFIG, val);
return (ret);
}
/*____ pss_setint
This function sets the correct bits in the
configuration register to
enable the chosen interrupt.
*/
static void
pss_setint (int intNum, int configAddress)
{
int val;
switch (intNum)
{
case 0:
val = pss_inpw (configAddress);
val &= INT_MASK;
pss_outpw (configAddress, val);
break;
case 3:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_3_BITS;
pss_outpw (configAddress, val);
break;
case 5:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_5_BITS;
pss_outpw (configAddress, val);
break;
case 7:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_7_BITS;
pss_outpw (configAddress, val);
break;
case 9:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_9_BITS;
pss_outpw (configAddress, val);
break;
case 10:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_10_BITS;
pss_outpw (configAddress, val);
break;
case 11:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_11_BITS;
pss_outpw (configAddress, val);
break;
case 12:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_12_BITS;
pss_outpw (configAddress, val);
break;
default:
printk ("pss_setint unkown int\n");
}
}
/*____ pss_setsbint
This function sets the correct bits in the
SoundBlaster configuration PSS register to
enable the chosen interrupt.
It takes a interrupt button as its argument.
*/
static void
pss_setsbint (int intNum)
{
int val;
int sbConfigAddress;
sbConfigAddress = pss_base + SB_CONFIG;
switch (intNum)
{
case 3:
val = pss_inpw (sbConfigAddress);
val &= INT_MASK;
val |= INT_3_BITS;
pss_outpw (sbConfigAddress, val);
break;
case 5:
val = pss_inpw (sbConfigAddress);
val &= INT_MASK;
val |= INT_5_BITS;
pss_outpw (sbConfigAddress, val);
break;
case 7:
val = pss_inpw (sbConfigAddress);
val &= INT_MASK;
val |= INT_7_BITS;
pss_outpw (sbConfigAddress, val);
break;
default:
printk ("pss_setsbint: unknown_int\n");
}
}
/*____ pss_setsbdma
This function sets the correct bits in the
SoundBlaster configuration PSS register to
enable the chosen DMA channel.
It takes a DMA button as its argument.
*/
static void
pss_setsbdma (int dmaNum)
{
int val;
int sbConfigAddress;
sbConfigAddress = pss_base + SB_CONFIG;
switch (dmaNum)
{
case 1:
val = pss_inpw (sbConfigAddress);
val &= DMA_MASK;
val |= DMA_1_BITS;
pss_outpw (sbConfigAddress, val);
break;
default:
printk ("Personal Sound System ERROR! pss_setsbdma: unknown_dma\n");
}
}
/*____ pss_setwssdma
This function sets the correct bits in the
WSS configuration PSS register to
enable the chosen DMA channel.
It takes a DMA button as its argument.
*/
static void
pss_setwssdma (int dmaNum)
{
int val;
int wssConfigAddress;
wssConfigAddress = pss_base + PSS_WSS_CONFIG;
switch (dmaNum)
{
case 0:
val = pss_inpw (wssConfigAddress);
val &= DMA_MASK;
val |= DMA_0_BITS;
pss_outpw (wssConfigAddress, val);
break;
case 1:
val = pss_inpw (wssConfigAddress);
val &= DMA_MASK;
val |= DMA_1_BITS;
pss_outpw (wssConfigAddress, val);
break;
case 3:
val = pss_inpw (wssConfigAddress);
val &= DMA_MASK;
val |= DMA_3_BITS;
pss_outpw (wssConfigAddress, val);
break;
default:
printk ("Personal Sound System ERROR! pss_setwssdma: unknown_dma\n");
}
}
/*_____ SetSpeakerOut
This function sets the Volume, Bass, Treble and Mode of
the speaker out channel.
*/
void
pss_setspeaker (struct pss_speaker *spk)
{
PSS_write (SET_MASTER_COMMAND);
if (spk->volume > PHILLIPS_VOL_MAX)
spk->volume = PHILLIPS_VOL_MAX;
if (spk->volume < PHILLIPS_VOL_MIN)
spk->volume = PHILLIPS_VOL_MIN;
PSS_write (MASTER_VOLUME_LEFT
| (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_VOLUME_RIGHT
| (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
if (spk->bass > PHILLIPS_BASS_MAX)
spk->bass = PHILLIPS_BASS_MAX;
if (spk->bass < PHILLIPS_BASS_MIN)
spk->bass = PHILLIPS_BASS_MIN;
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_BASS
| (PHILLIPS_BASS_CONSTANT + spk->bass / PHILLIPS_BASS_STEP));
if (spk->treb > PHILLIPS_TREBLE_MAX)
spk->treb = PHILLIPS_TREBLE_MAX;
if (spk->treb < PHILLIPS_TREBLE_MIN)
spk->treb = PHILLIPS_TREBLE_MIN;
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_TREBLE
| (PHILLIPS_TREBLE_CONSTANT + spk->treb / PHILLIPS_TREBLE_STEP));
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_SWITCH | spk->mode);
}
static void
pss_init1848 (void)
{
/*_____ Wait for 1848 to init */
while (INB (SoundPortAddress) & SP_IN_INIT);
/*_____ Wait for 1848 to autocal */
OUTB (SoundPortAddress, SP_TEST_AND_INIT);
while (INB (SoundPortData) & AUTO_CAL_IN_PROG);
}
static int
pss_configure_registers_to_look_like_sb (void)
{
pss_setaddr (wssAddr, pss_base + PSS_WSS_CONFIG);
SoundPortAddress = wssAddr + 4;
SoundPortData = wssAddr + 5;
DEB (printk ("Turning Game Port %s.\n",
gamePort ? "On" : "Off"));
/*_____ Turn on the Game port */
if (gamePort)
pss_outpw (pss_base + PSS_STATUS,
pss_inpw (pss_base + PSS_STATUS) | GAME_BIT);
else
pss_outpw (pss_base + PSS_STATUS,
pss_inpw (pss_base + PSS_STATUS) & GAME_BIT_MASK);
DEB (printk ("PSS attaching base %x irq %d dma %d\n",
pss_base, pss_irq, pss_dma));
/* Check if sb is enabled if it is check the interrupt */
pss_outpw (pss_base + SB_CONFIG, 0);
if (pss_irq != 0)
{
DEB (printk ("PSS Emulating Sound Blaster ADDR %04x\n", pss_base));
DEB (printk ("PSS SBC: attaching base %x irq %d dma %d\n",
SBC_BASE, SBC_IRQ, SBC_DMA));
if (pss_checkint (SBC_IRQ) == 0)
{
printk ("PSS! attach: int_error\n");
return 0;
}
pss_setsbint (SBC_IRQ);
pss_setsbdma (SBC_DMA);
sb_ok = 1;
}
else
{
sb_ok = 0;
printk ("PSS: sound blaster error init\n");
}
/* Check if cd is enabled if it is check the interrupt */
pss_outpw (pss_base + CD_CONFIG, 0);
if (cdAddr != 0)
{
DEB (printk ("PSS:CD drive %x irq: %d", cdAddr, cdInt));
if (cdInt != 0)
{
if (pss_checkint (cdInt) == 0)
{
printk ("Can't allocate cdInt %d\n", cdInt);
}
else
{
int val;
printk ("CD poll ");
pss_setaddr (cdAddr, pss_base + CD_CONFIG);
pss_setint (cdInt, pss_base + CD_CONFIG);
/* set the correct bit in the
configuration register to
set the irq polarity for the CD-Rom.
NOTE: This bit is in the address config
field, It must be configured after setting
the CD-ROM ADDRESS!!! */
val = pss_inpw (pss_base + CD_CONFIG);
pss_outpw (pss_base + CD_CONFIG, 0);
val &= CD_POL_MASK;
if (cdPol)
val |= CD_POL_BIT;
pss_outpw (pss_base + CD_CONFIG, val);
}
}
}
/* Check if midi is enabled if it is check the interrupt */
pss_outpw (pss_base + MIDI_CONFIG, 0);
if (midiAddr != 0)
{
printk ("midi init %x %d\n", midiAddr, midiInt);
if (pss_checkint (midiInt) == 0)
{
printk ("midi init int error %x %d\n", midiAddr, midiInt);
}
else
{
pss_setaddr (midiAddr, pss_base + MIDI_CONFIG);
pss_setint (midiInt, pss_base + MIDI_CONFIG);
}
}
return 1;
}
long
attach_pss (long mem_start, struct address_info *hw_config)
{
if (pss_ok)
{
if (hw_config)
{
printk (" <PSS-ESC614>");
}
return mem_start;
}
pss_ok = 1;
if (pss_configure_registers_to_look_like_sb () == 0)
return mem_start;
if (sb_ok)
if (pss_synthLen
&& pss_download_boot (pss_synth, pss_synthLen))
{
if (speaker)
pss_setspeaker (&default_speaker);
pss_ok = 1;
}
else
pss_reset_dsp ();
return mem_start;
}
int
probe_pss (struct address_info *hw_config)
{
pss_base = hw_config->io_base;
pss_irq = hw_config->irq;
pss_dma = hw_config->dma;
if ((pss_inpw (pss_base + 4) & 0xff00) == 0x4500)
{
attach_pss (0, hw_config);
return 1;
}
printk (" fail base %x irq %d dma %d\n", pss_base, pss_irq, pss_dma);
return 0;
}
static int
pss_reattach (void)
{
pss_ok = 0;
attach_pss (0, 0);
return 1;
}
static int
pss_reset_dsp ()
{
unsigned long i, limit = GET_TIME () + 10;
pss_outpw (pss_base + PSS_CONTROL, 0x2000);
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
pss_inpw (pss_base + PSS_CONTROL);
pss_outpw (pss_base + PSS_CONTROL, 0x0000);
return 1;
}
static int
pss_download_boot (unsigned char *block, int size)
{
int i, limit, val, count;
printk ("PSS: downloading boot code synth.ld... ");
/*_____ Warn DSP software that a boot is coming */
pss_outpw (pss_base + PSS_DATA, 0x00fe);
limit = GET_TIME () + 10;
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
if (pss_inpw (pss_base + PSS_DATA) == 0x5500)
break;
pss_outpw (pss_base + PSS_DATA, *block++);
pss_reset_dsp ();
printk ("start ");
count = 1;
while (1)
{
int j;
for (j = 0; j < 327670; j++)
{
/*_____ Wait for BG to appear */
if (pss_inpw (pss_base + PSS_STATUS) & PSS_FLAG3)
break;
}
if (j == 327670)
{
/* It's ok we timed out when the file was empty */
if (count >= size)
break;
else
{
printk ("\nPSS: DownLoad timeout problems, byte %d=%d\n",
count, size);
return 0;
}
}
/*_____ Send the next byte */
pss_outpw (pss_base + PSS_DATA, *block++);
count++;
}
/*_____ Why */
pss_outpw (pss_base + PSS_DATA, 0);
limit = GET_TIME () + 10;
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
val = pss_inpw (pss_base + PSS_STATUS);
printk ("downloaded\n");
limit = GET_TIME () + 10;
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
{
val = pss_inpw (pss_base + PSS_STATUS);
if (val & 0x4000)
break;
}
/* now read the version */
for (i = 0; i < 32000; i++)
{
val = pss_inpw (pss_base + PSS_STATUS_REG);
if (val & PSS_READ_FULL)
break;
}
if (i == 32000)
return 0;
val = pss_inpw (pss_base + PSS_DATA_REG);
return 1;
}
/* The following is a simple device driver for the pss.
All I really care about is comunication to and from the pss.
The ability to reinitialize the <synth.ld> This will be
default when release is choosen.
SNDCTL_PSS_DOWNLOAD:
Okay we need to creat new minor numbers for the
DOWNLOAD functionality.
14,0x19 -- /dev/pssld where a read operation would output the
current ld to user space
where a write operation would effectively
download a new ld.
14,0x09 -- /dev/psecho would open up a comunication path to the
esc614 asic. Given the ability to send
messages to the asic and recive messages too.
All messages would get read and written in the
same manner. It would be up to the application
and the ld to maintain a relationship
of what the messages mean.
for this device we need to implement select. */
#define CODE_BUFFER_LEN (64*1024)
static char *code_buffer;
static int code_length;
static int lock_pss = 0;
int
pss_open (int dev, struct fileinfo *file)
{
int mode;
DEB (printk ("pss_open\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
if (lock_pss)
return 0;
lock_pss = 1;
dev = dev >> 4;
mode = file->mode & O_ACCMODE;
if (mode == O_WRONLY)
{
printk ("pss-open for WRONLY\n");
code_length = 0;
}
RESET_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
return 1;
}
void
pss_release (int dev, struct fileinfo *file)
{
int mode;
DEB (printk ("pss_release\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
mode = file->mode & O_ACCMODE;
if (mode == O_WRONLY && code_length > 0)
{
#ifdef linux
/* This just allows interrupts while the conversion is running */
__asm__ ("sti");
#endif
if (!pss_download_boot (code_buffer, code_length))
{
pss_reattach ();
}
}
lock_pss = 0;
}
int
pss_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
int c, p;
DEB (printk ("pss_read\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
p = 0;
c = count;
return count - c;
}
int
pss_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
DEB (printk ("pss_write\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
if (count) /* Flush output */
{
COPY_FROM_USER (&code_buffer[code_length], buf, 0, count);
code_length += count;
}
return count;
}
int
pss_ioctl (int dev, struct fileinfo *file,
unsigned int cmd, unsigned int arg)
{
DEB (printk ("pss_ioctl dev=%d cmd=%x\n", dev, cmd));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
switch (cmd)
{
case SNDCTL_PSS_RESET:
pss_reattach ();
return 1;
case SNDCTL_PSS_SETUP_REGISTERS:
pss_configure_registers_to_look_like_sb ();
return 1;
case SNDCTL_PSS_SPEAKER:
{
struct pss_speaker params;
COPY_FROM_USER (&params, (char *) arg, 0, sizeof (struct pss_speaker));
pss_setspeaker (&params);
return 0;
}
default:
return RET_ERROR (EIO);
}
}
/* This is going to be used to implement
waiting on messages sent from the DSP and to the
DSP when comunication is used via the pss directly.
We need to find out if the pss can generate a diffrent
interupt other than the one it has been setup for.
This way we can carry on a conversation with the pss
on a seprate chanel. This would be usefull for debugging. */
pss_select (int dev, struct fileinfo * file, int sel_type, select_table * wait)
{
return 0;
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
switch (sel_type)
{
case SEL_IN:
select_wait (&pss_sleeper, wait);
return 0;
break;
case SEL_OUT:
select_wait (&pss_sleeper, wait);
return 0;
break;
case SEL_EX:
return 0;
}
return 0;
}
long
pss_init (long mem_start)
{
DEB (printk ("pss_init\n"));
if (pss_ok)
{
code_buffer = mem_start;
mem_start += CODE_BUFFER_LEN;
}
return mem_start;
}
#endif

371
sys/i386/isa/sound/pss.h Normal file
View File

@ -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

28
sys/i386/isa/sound/sb.h Normal file
View File

@ -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

View File

@ -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

View File

@ -0,0 +1,304 @@
/*
* sound/sb16_midi.c
*
* The low level driver for the MPU-401 UART emulation of the SB16.
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "sound_config.h"
#ifdef CONFIGURE_SOUNDCARD
#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI)
#include "sb.h"
#define DATAPORT (sb16midi_base)
#define COMDPORT (sb16midi_base+1)
#define STATPORT (sb16midi_base+1)
#define sb16midi_status() INB(STATPORT)
#define input_avail() (!(sb16midi_status()&INPUT_AVAIL))
#define output_ready() (!(sb16midi_status()&OUTPUT_READY))
#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT)
#define sb16midi_read() INB(DATAPORT)
#define sb16midi_write(byte) OUTB(byte, DATAPORT)
#define OUTPUT_READY 0x40
#define INPUT_AVAIL 0x80
#define MPU_ACK 0xFE
#define MPU_RESET 0xFF
#define UART_MODE_ON 0x3F
static int sb16midi_opened = 0;
static int sb16midi_base = 0x330;
static int sb16midi_detected = 0;
static int my_dev;
extern int sbc_base;
static int reset_sb16midi (void);
static void (*midi_input_intr) (int dev, unsigned char data);
static void
sb16midi_input_loop (void)
{
while (input_avail ())
{
unsigned char c = sb16midi_read ();
if (sb16midi_opened & OPEN_READ)
midi_input_intr (my_dev, c);
}
}
void
sb16midiintr (int unit)
{
if (input_avail ())
sb16midi_input_loop ();
}
static int
sb16midi_open (int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
if (sb16midi_opened)
{
return RET_ERROR (EBUSY);
}
sb16midi_input_loop ();
midi_input_intr = input;
sb16midi_opened = mode;
return 0;
}
static void
sb16midi_close (int dev)
{
sb16midi_opened = 0;
}
static int
sb16midi_out (int dev, unsigned char midi_byte)
{
int timeout;
unsigned long flags;
/*
* Test for input since pending input seems to block the output.
*/
DISABLE_INTR (flags);
if (input_avail ())
sb16midi_input_loop ();
RESTORE_INTR (flags);
/*
* Sometimes it takes about 13000 loops before the output becomes ready
* (After reset). Normally it takes just about 10 loops.
*/
for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
* Wait
*/
if (!output_ready ())
{
printk ("MPU-401: Timeout\n");
return 0;
}
sb16midi_write (midi_byte);
return 1;
}
static int
sb16midi_start_read (int dev)
{
return 0;
}
static int
sb16midi_end_read (int dev)
{
return 0;
}
static int
sb16midi_ioctl (int dev, unsigned cmd, unsigned arg)
{
return RET_ERROR (EINVAL);
}
static void
sb16midi_kick (int dev)
{
}
static int
sb16midi_buffer_status (int dev)
{
return 0; /*
* No data in buffers
*/
}
#define MIDI_SYNTH_NAME "SoundBlaster 16 Midi"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static struct midi_operations sb16midi_operations =
{
{"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI},
&std_midi_synth,
sb16midi_open,
sb16midi_close,
sb16midi_ioctl,
sb16midi_out,
sb16midi_start_read,
sb16midi_end_read,
sb16midi_kick,
NULL,
sb16midi_buffer_status,
NULL
};
long
attach_sb16midi (long mem_start, struct address_info *hw_config)
{
int ok, timeout;
unsigned long flags;
sb16midi_base = hw_config->io_base;
if (!sb16midi_detected)
return RET_ERROR (EIO);
DISABLE_INTR (flags);
for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
* Wait
*/
sb16midi_cmd (UART_MODE_ON);
ok = 0;
for (timeout = 50000; timeout > 0 && !ok; timeout--)
if (input_avail ())
if (sb16midi_read () == MPU_ACK)
ok = 1;
RESTORE_INTR (flags);
if (num_midis >= MAX_MIDI_DEV)
{
printk ("Sound: Too many midi devices detected\n");
return mem_start;
}
printk (" <SoundBlaster MPU-401>");
std_midi_synth.midi_dev = my_dev = num_midis;
midi_devs[num_midis++] = &sb16midi_operations;
return mem_start;
}
static int
reset_sb16midi (void)
{
unsigned long flags;
int ok, timeout, n;
/*
* Send the RESET command. Try again if no success at the first time.
*/
ok = 0;
DISABLE_INTR (flags);
for (n = 0; n < 2 && !ok; n++)
{
for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
* Wait
*/
sb16midi_cmd (MPU_RESET); /*
* Send MPU-401 RESET Command
*/
/*
* Wait at least 25 msec. This method is not accurate so let's make the
* loop bit longer. Cannot sleep since this is called during boot.
*/
for (timeout = 50000; timeout > 0 && !ok; timeout--)
if (input_avail ())
if (sb16midi_read () == MPU_ACK)
ok = 1;
}
sb16midi_opened = 0;
if (ok)
sb16midi_input_loop (); /*
* Flush input before enabling interrupts
*/
RESTORE_INTR (flags);
return ok;
}
int
probe_sb16midi (struct address_info *hw_config)
{
int ok = 0;
int i;
extern int sbc_major;
if (sbc_major < 4)
return 0; /* Not a SB16 */
sb16midi_base = hw_config->io_base;
if (sb_get_irq () < 0)
return 0;
ok = reset_sb16midi ();
sb16midi_detected = ok;
return ok;
}
#endif
#endif

View File

@ -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

863
sys/i386/isa/sound/sb_dsp.c Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,401 @@
/*
* sound/386bsd/soundcard.c
*
* Soundcard driver for 386BSD.
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "sound_config.h"
#ifdef CONFIGURE_SOUNDCARD
#include "dev_table.h"
u_int snd1mask;
u_int snd2mask;
u_int snd3mask;
u_int snd4mask;
u_int snd5mask;
u_int snd6mask;
u_int snd7mask;
u_int snd8mask;
u_int snd9mask;
#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
static int timer_running = 0;
static int soundcards_installed = 0; /* Number of installed
* soundcards */
static int soundcard_configured = 0;
static struct fileinfo files[SND_NDEVS];
int sndprobe (struct isa_device *dev);
int sndattach (struct isa_device *dev);
int sndopen (dev_t dev, int flags);
int sndclose (dev_t dev, int flags);
int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode);
int sndread (int dev, struct uio *uio);
int sndwrite (int dev, struct uio *uio);
int sndselect (int dev, int rw);
static void sound_mem_init(void);
unsigned long
get_time(void)
{
extern struct timeval time;
struct timeval timecopy;
int x;
x = splclock();
timecopy = time;
splx(x);
return timecopy.tv_usec/(1000000/HZ) +
(unsigned long)timecopy.tv_sec*HZ;
}
int
sndread (int dev, struct uio *buf)
{
int count = buf->uio_resid;
dev = minor (dev);
FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count));
}
int
sndwrite (int dev, struct uio *buf)
{
int count = buf->uio_resid;
dev = minor (dev);
FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count));
}
int
sndopen (dev_t dev, int flags)
{
int retval;
dev = minor (dev);
if (!soundcard_configured && dev)
{
printk ("SoundCard Error: The soundcard system has not been configured\n");
FIX_RETURN (-ENODEV);
}
files[dev].mode = 0;
if (flags & FREAD && flags & FWRITE)
files[dev].mode = OPEN_READWRITE;
else if (flags & FREAD)
files[dev].mode = OPEN_READ;
else if (flags & FWRITE)
files[dev].mode = OPEN_WRITE;
FIX_RETURN(sound_open_sw (dev, &files[dev]));
}
int
sndclose (dev_t dev, int flags)
{
dev = minor (dev);
sound_release_sw(dev, &files[dev]);
FIX_RETURN (0);
}
int
sndioctl (dev_t dev, int cmd, caddr_t arg, int mode)
{
dev = minor (dev);
FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg));
}
int
sndselect (int dev, int rw)
{
dev = minor (dev);
DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
FIX_RETURN (0);
}
static short
ipri_to_irq (unsigned short ipri)
{
/*
* Converts the ipri (bitmask) to the corresponding irq number
*/
int irq;
for (irq = 0; irq < 16; irq++)
if (ipri == (1 << irq))
return irq;
return -1; /* Invalid argument */
}
int
sndprobe (struct isa_device *dev)
{
struct address_info hw_config;
hw_config.io_base = dev->id_iobase;
hw_config.irq = ipri_to_irq (dev->id_irq);
hw_config.dma = dev->id_drq;
return sndtable_probe (dev->id_unit, &hw_config);
}
int
sndattach (struct isa_device *dev)
{
int i;
static int midi_initialized = 0;
static int seq_initialized = 0;
static int generic_midi_initialized = 0;
unsigned long mem_start = 0xefffffff;
struct address_info hw_config;
hw_config.io_base = dev->id_iobase;
hw_config.irq = ipri_to_irq (dev->id_irq);
hw_config.dma = dev->id_drq;
if (dev->id_unit) /* Card init */
if (!sndtable_init_card (dev->id_unit, &hw_config))
{
printf (" <Driver not configured>");
return FALSE;
}
/*
* Init the high level sound driver
*/
if (!(soundcards_installed = sndtable_get_cardcount ()))
{
printf (" <No such hardware>");
return FALSE; /* No cards detected */
}
printf("\n");
#ifndef EXCLUDE_AUDIO
if (num_audiodevs) /* Audio devices present */
{
mem_start = DMAbuf_init (mem_start);
mem_start = audio_init (mem_start);
sound_mem_init ();
}
soundcard_configured = 1;
#endif
if (num_midis && !midi_initialized)
{
midi_initialized = 1;
mem_start = MIDIbuf_init (mem_start);
}
if ((num_midis + num_synths) && !seq_initialized)
{
seq_initialized = 1;
mem_start = sequencer_init (mem_start);
}
return TRUE;
}
void
tenmicrosec (void)
{
int i;
for (i = 0; i < 16; i++)
inb (0x80);
}
void
request_sound_timer (int count)
{
static int current = 0;
int tmp = count;
if (count < 0)
timeout (sequencer_timer, 0, -count);
else
{
if (count < current)
current = 0; /* Timer restarted */
count = count - current;
current = tmp;
if (!count)
count = 1;
timeout (sequencer_timer, 0, count);
}
timer_running = 1;
}
void
sound_stop_timer (void)
{
if (timer_running)
untimeout (sequencer_timer, 0);
timer_running = 0;
}
#ifndef EXCLUDE_AUDIO
static void
sound_mem_init (void)
{
int i, dev;
unsigned long dma_pagesize;
static unsigned long dsp_init_mask = 0;
for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */
if (!(dsp_init_mask & (1 << dev))) /* Not already done */
if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0)
{
dsp_init_mask |= (1 << dev);
if (sound_dma_automode[dev])
{
sound_dma_automode[dev] = 0; /* Not possible with 386BSD */
}
#if 0
if (sound_buffcounts[dev] == 1)
{
sound_buffcounts[dev] = 2;
sound_buffsizes[dev] /= 2;
}
if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */
sound_buffsizes[dev] = 65536;
if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
dma_pagesize = 131072; /* 128k */
else
dma_pagesize = 65536;
/* More sanity checks */
if (sound_buffsizes[dev] > dma_pagesize)
sound_buffsizes[dev] = dma_pagesize;
sound_buffsizes[dev] &= 0xfffff000; /* Truncate to n*4k */
if (sound_buffsizes[dev] < 4096)
sound_buffsizes[dev] = 4096;
#else
dma_pagesize = 4096;
sound_buffsizes[dev] = 4096;
sound_buffcounts[dev] = 16; /* 16*4k -> 64k */
#endif
/* Now allocate the buffers */
for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
{
/*
* The DMA buffer allocation algorithm hogs memory. We allocate
* a memory area which is two times the requires size. This
* guarantees that it contains at least one valid DMA buffer.
*
* This really needs some kind of finetuning.
*/
char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT);
unsigned long addr, rounded, start, end;
if (tmpbuf == NULL)
{
printk ("snd: Unable to allocate %d bytes of buffer\n",
2 * sound_buffsizes[dev]);
return;
}
addr = kvtop (tmpbuf);
/*
* Align the start address if required
*/
start = (addr & ~(dma_pagesize - 1));
end = ((addr+sound_buffsizes[dev]-1) & ~(dma_pagesize - 1));
if (start != end)
rounded = end;
else
rounded = addr; /* Fits to the same DMA page */
snd_raw_buf[dev][snd_raw_count[dev]] =
&tmpbuf[rounded - addr]; /* Compute offset */
/*
* Use virtual address as the physical address, since
* isa_dmastart performs the phys address computation.
*/
snd_raw_buf_phys[dev][snd_raw_count[dev]] =
(unsigned long) snd_raw_buf[dev][snd_raw_count[dev]];
}
} /* for dev */
}
#endif
struct isa_driver snddriver =
{sndprobe, sndattach, "snd"};
int
snd_ioctl_return (int *addr, int value)
{
if (value < 0)
return value; /* Error */
suword (addr, value);
return 0;
}
int
snd_set_irq_handler (int interrupt_level, void(*hndlr)(int))
{
return 1;
}
void
snd_release_irq(int vect)
{
}
#endif

View File

@ -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

View File

@ -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

View File

@ -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

69
sys/i386/isa/sound/ulaw.h Normal file
View File

@ -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,
};