mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-19 06:44:31 +00:00
This is the Linux generic soundcard driver, version 1.0c. Supports
SBlaster/Adlib/ProAudio Spectrum/Gravis/etc cards. This is a BETA test driver, please test it and get back to me!
This commit is contained in:
parent
dad544ec41
commit
6b8afe4d37
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=653
27
sys/i386/isa/sound/COPYING
Normal file
27
sys/i386/isa/sound/COPYING
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright by Hannu Savolainen 1993
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Unmodified version of this file (COPYING) must be provided with
|
||||
* any source or binary distributions.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
51
sys/i386/isa/sound/HOWTO_MIDI
Normal file
51
sys/i386/isa/sound/HOWTO_MIDI
Normal file
|
@ -0,0 +1,51 @@
|
|||
The following file describes the procedure for adding modules to MIDI
|
||||
Please READ the main documentation files for the driver first!!!
|
||||
|
||||
|
||||
Example: We have a sound card with a MIDI chip & port on it
|
||||
and, we call it the 'MYBLASTER' card:
|
||||
|
||||
**************************************************************************
|
||||
|
||||
0: Run 'configure'. Select the MIDI on CHIP support option.
|
||||
|
||||
1: Write a midi driver module; 'blast_midi.c'
|
||||
(with functions for open,close,read,write,attach.)
|
||||
|
||||
1a: Write all functions except the 'attach' the way you want.
|
||||
|
||||
For the 'attach' function, look at a model in the 'pro_midi.c'
|
||||
file. Just dup it in the same fashion. For the 'generic_midi_operations'
|
||||
structure which is required, see file 'dev_table.h'.
|
||||
|
||||
2: We called the 'attach' function: 'blast_attach'.
|
||||
|
||||
Go to the file 'dev_table.h' and add your driver name and the function
|
||||
pointer ( which is 'blast_attach' ) to the 'midi_supported' table.
|
||||
|
||||
3: You are almost set. Go and make a reference
|
||||
to an 'exclude constant'; say EXLCUDE_BLAST_MIDI in your module
|
||||
(refer to the 'pro_midi.c' file for model). Also make sure to
|
||||
add the constant to the file 'sound_config.h' (for example, look
|
||||
where the constant EXCLUDE_PRO_MIDI is in the file.)
|
||||
|
||||
4: Add the line
|
||||
|
||||
#define ALL_EXTERNAL_TO_ME
|
||||
|
||||
as the 1st line of your 'blast_midi.c' file. This happily, makes
|
||||
you ignorant of everything else specific to the driver! :).
|
||||
|
||||
4a: And of course, don't forget to make a device :). Note that your
|
||||
minor number should be = ( 15 + position of your driver in the
|
||||
'midi_supported' table in the 'dev_table.h' file ).
|
||||
|
||||
Eg: Your driver is the second one in the table. viz midi_supported[1].
|
||||
Your device minor number should be ( 15 + 1 = 16 ). Then, make the
|
||||
reference to your device as, say CMIDI_DEV_BLAST in the file
|
||||
'sound_config.h'. Also add this in 'soundcard.c' for the open, read,
|
||||
write and close routines. See files for example using CMIDI_DEV_PRO
|
||||
(which is the ProAudioSpectrum on chip MIDI).
|
||||
|
||||
5: You are all set. If ever you have problems, follow the same model
|
||||
as the file 'pro_midi.c', except for substituting your own functions!
|
17
sys/i386/isa/sound/README
Normal file
17
sys/i386/isa/sound/README
Normal file
|
@ -0,0 +1,17 @@
|
|||
CAUTION!
|
||||
|
||||
This is a prototype version of the Linux Sound Driver for FreeBSD.
|
||||
|
||||
The official and supported version is 1.0c.
|
||||
|
||||
This version 'should work' but there may be some bugs and the programmers
|
||||
API may change before the final version.
|
||||
|
||||
There are some additional programs for GUS owners in the
|
||||
gustest subdirectory of this directory, namely a module
|
||||
(.MOD, .STM and .669) player and a patch file loader.
|
||||
Additionally, there is a midithru program which allows
|
||||
you to play the synth on the soundcard with a midi keyboard
|
||||
(also usable for OPL-3 owners).
|
||||
|
||||
Hannu & FreeBSD team.
|
38
sys/i386/isa/sound/RELNOTES
Normal file
38
sys/i386/isa/sound/RELNOTES
Normal file
|
@ -0,0 +1,38 @@
|
|||
Welcome to use the Linux sound driver for FreeBSD. This
|
||||
driver supports the SoundBlaster, SB Pro, Pro Audio Spectrum 16,
|
||||
AdLib and Gravis UltraSound sound cards.
|
||||
|
||||
In addition there is rather limited support for MPU-401
|
||||
(and compatible) midi cards. Also, the OPL-3 synthesizer
|
||||
of the SB Pro and PAS16 cards is now supported in the 4 OP
|
||||
modes.
|
||||
|
||||
Most of the features of the /dev/sequencer device file are
|
||||
available just for GUS owners.
|
||||
|
||||
The SoundBlaster 16 and SB 16 ASP cards are not supported,
|
||||
though they may work in mono mode with speeds < 22 kHz.
|
||||
The OPL-3 chicp of the SB 16 should work (without problems?).
|
||||
Is there anybody willing to implement the SB 16 support
|
||||
(have the SB 16 and the SDK for it)?
|
||||
|
||||
Since this driver is a sound driver, it does not contain support
|
||||
for SCSI/CD-ROM/Joystick -devices.
|
||||
|
||||
Known bugs
|
||||
----------
|
||||
|
||||
- It's not possible to open /dev/dsp (or /dev/audio) while the
|
||||
/dev/sequencer is open for output and GUS is the only soundcard
|
||||
installed. It's possible if /dev/dsp is opened before /dev/sequencer
|
||||
but at this time the GUS is not available for access via /dev/sequencer.
|
||||
This is a limitation of the driver.
|
||||
- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
|
||||
It uses by default the I/O port 0x330, which is also used by the
|
||||
Adaptec 1542 SCSI adapter.
|
||||
- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
|
||||
^C and playing again should solve this problem. This is probably caused by
|
||||
incompatibilities between the GUS and certain VLB motherboards. Try to avoid
|
||||
switching between VTs while patches are being loaded to the GUS.
|
||||
- There is a skeleton of the patch manager support. It doesn't work in
|
||||
this version.
|
191
sys/i386/isa/sound/RELNOTES.Linux
Normal file
191
sys/i386/isa/sound/RELNOTES.Linux
Normal file
|
@ -0,0 +1,191 @@
|
|||
Release notes for the Linux Sound Driver 1.99.9
|
||||
-----------------------------------------------
|
||||
|
||||
******** THIS IS A BETA TEST RELEASE ********
|
||||
which means that there can be some untested things. In theory
|
||||
there is a risk that this driver causes some trouble to your system.
|
||||
You should not use this driver before backing up your disks.
|
||||
|
||||
|
||||
|
||||
|
||||
Welcome to use the Gravis UltraSound driver for Linux. This
|
||||
driver still supports the same cards than version 1.0c
|
||||
(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib).
|
||||
In addition there is rather limited support for MPU-401
|
||||
(and compatible) midi cards. Also the OPL-3 synthesizer
|
||||
of the SB Pro and PAS16 cards is now supported in the 4 OP
|
||||
modes.
|
||||
Most of the features of the /dev/sequencer device file are
|
||||
available just for GUS owners.
|
||||
|
||||
The SoundBlaster 16 and SB 16 ASP cards are not supported.
|
||||
They could work in mono mode with speeds < 22 kHz.
|
||||
The OPL-3 chicp of the SB 16 should work (without problems?).
|
||||
Is there anybody willing to implement the SB 16 support
|
||||
(have the SB 16 and the SDK for it)?
|
||||
|
||||
|
||||
This is the first version of the driver which has almost
|
||||
all of the features which I have planned to include into
|
||||
version 2.0. Some features are still missing and some ones
|
||||
doesn't work.
|
||||
|
||||
NOTE! There are separate driver for CD-ROMS supported by
|
||||
some soundcards. The driver for CDU31A (Fusion 16) is
|
||||
called cdu31a-0.6.diff.z. It will be contained in the
|
||||
Linux version 0.99.12. The driver for the CD-ROM of SB Pro
|
||||
is sbpcd0.4.tar.gz (these were the latest versions when I wrote
|
||||
this). These files should be at least at sunsite.unc.edu.
|
||||
As far as I know, there is no driver for the SCSI interface of PAS16
|
||||
(yet).
|
||||
|
||||
There is also a driver for joystick. Look for file joystick-0.5.tar.gz
|
||||
(sunsite).
|
||||
|
||||
Since this driver is a sound driver, it will not contain support
|
||||
for SCSI/CD-ROM/Joystick -devices.
|
||||
|
||||
Compatibility with the earlier versions
|
||||
---------------------------------------
|
||||
|
||||
This is just like the version 1.99.7/1.99.8. There is just some minor
|
||||
enhancements. Most of them are portability fixes. If you are porting
|
||||
this driver to any OS, please look at the 386bsd/os.h. There is some
|
||||
new macros and some macros have more parameters. In addition this file
|
||||
contains some usefull comments.
|
||||
|
||||
**** There is some ISC and 386bsd stuff in this driver. Please stay away ****
|
||||
This stuff is here just because I want to be in sync with the porters. These
|
||||
ports don't work yet.
|
||||
|
||||
The ioctl() interface has changed completely since version 1.0c. All
|
||||
programs using this driver must be at least recompiled.
|
||||
The snd-util-1.99.6 package contains some utilities for this version.
|
||||
|
||||
The version 1.0c and earlier used a 'nonportable' ioctl calling scheme
|
||||
where the input argument was passed by value and the output value was
|
||||
returned as the functional return. For example setting the speed of
|
||||
/dev/dsp were done as the following:
|
||||
|
||||
int actual_speed;
|
||||
actual_speed = ioctl(fd, SOUND_PCM_WRITE_RATE, 44100);
|
||||
|
||||
After version 1.99.0 this must be done as the following:
|
||||
|
||||
int actual_speed = 44100;
|
||||
ioctl(fd, SOUND_PCM_WRITE_RATE, &actual_speed);
|
||||
|
||||
If you have an application written for the version 1.0, you should search
|
||||
for the strings SNDCTL_ and SOUND_ and to check the parameters.
|
||||
|
||||
Since the this version will support more than one synthesizer devices
|
||||
at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition
|
||||
there is some new fields which must be initialized. Look at the sbiset.c in
|
||||
the snd-util-1.99.6 package for further info.
|
||||
|
||||
The GUS patch format has changed since the version 1.99.3. You have to
|
||||
use latest versions of the programs in the sound/gustest directory. In
|
||||
addition the version 0.4g of the Adagio package supports this format.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
There is also some changes which make this version more usable than
|
||||
the version 1.0c.
|
||||
|
||||
- /dev/dsp and /dev/audio
|
||||
|
||||
The DMA buffering is now little bit more intelligent than earlier. The
|
||||
buffer size is selected run-time so that a buffer holds data for 0.5 to
|
||||
1.0 seconds of recording or playback. This makes recording more comfortable
|
||||
than with version 1.0. With the previous version there was sometimes more
|
||||
than 10 seconds of delay before the driver returned the first input byte.
|
||||
|
||||
There is also support for more than one digitized voice devices. The device
|
||||
files /dev/dsp1 and /dev/audio1 (minor 19 and 20) are available with PAS16.
|
||||
The /dev/dsp (/dev/audio) is connected to the PCM circuit of the PAS16 itself
|
||||
and the /dev/dsp1 (/dev/audio1) to the SB emulation of PAS16 card. Two
|
||||
dsp/audio devices are available also if you have combination of SB and GUS.
|
||||
With GUS and PAS16 you will have even three dsp/audio devices. These devices
|
||||
can be used independently and can be active at the same time (3 channels
|
||||
at the same time propably don't work).
|
||||
|
||||
The dsp/audio support of PAS16 should be much cleaner now since the
|
||||
constant clicking sound between the DMA blocks (about once per second) has
|
||||
been eliminated.
|
||||
|
||||
The stereo playback of GUS doesn't work perfectly. There is lot of
|
||||
clicking in the output.
|
||||
|
||||
- /dev/mixer
|
||||
|
||||
No changes.
|
||||
|
||||
There is no mixer for the GUS yet.
|
||||
|
||||
- /dev/sequencer
|
||||
|
||||
This part has the most changes. Mostly to support the rich
|
||||
features of the Gravis UltraSound. There is also the support
|
||||
for the OPL-3 synthesizer chip.
|
||||
|
||||
- /dev/sndstat
|
||||
|
||||
This is a new devicefile for debugging purposes. A better place for
|
||||
it is in the /proc -directory but I was just too lazy to implement it
|
||||
properly. The /dev/sndstat (major 14, minor 6) is a file which returns
|
||||
info about the current configuration (see the example below). If you
|
||||
send me a error/problem report, please include a printout from this
|
||||
device to your message (cat /dev/sndstat).
|
||||
|
||||
------ cut here --- cat /dev/sndstat example --------
|
||||
Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi)
|
||||
Config options: 0x00000d4b
|
||||
|
||||
Major number: 14
|
||||
HW config:
|
||||
Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6
|
||||
Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3
|
||||
Type 2: SoundBlaster at 0x220 irq 7 drq 1
|
||||
Type 1: AdLib at 0x388 irq 0 drq 0
|
||||
|
||||
PCM devices:
|
||||
00: Gravis UltraSound
|
||||
01: Pro Audio Spectrum
|
||||
02: SoundBlaster 2.0
|
||||
|
||||
Synth devices:
|
||||
00: Gravis UltraSound
|
||||
01: Yamaha OPL-3
|
||||
|
||||
Midi devices:
|
||||
00: Gravis UltraSound
|
||||
01: Pro Audio Spectrum
|
||||
|
||||
Mixer(s) installed
|
||||
------ cut here ---- End of Example -----------
|
||||
|
||||
|
||||
Known bugs
|
||||
----------
|
||||
|
||||
- There was clicking during stereo playback to /dev/dsp with GUS.
|
||||
* Fixed in 1.99.9 *
|
||||
- It's not possible to open /dev/dsp (or /dev/audio) while the
|
||||
/dev/sequencer is open for output and GUS is the only soundcard
|
||||
installed. It's possible if /dev/dsp is opened before /dev/sequencer
|
||||
but at this time the GUS is not available for access via /dev/sequencer.
|
||||
This is a limitation of the driver.
|
||||
- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
|
||||
It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI
|
||||
adapter.
|
||||
- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
|
||||
^C and playing again should solve this problem. This is propably caused by
|
||||
incompatibilities between GUS and certain VLB motherboards. Try to avoid
|
||||
switching between VTs while patches are being loaded to the GUS.
|
||||
- There was some problems with GUS and Mitsumi CD in version 1.99.8. Fixed
|
||||
in 1.99.9.
|
||||
- /dev/audio sounded like stereo with GUS. Fixed in 1.99.9.
|
||||
- There is a skeleton of the patch manager support. It don't work in
|
||||
this version.
|
32
sys/i386/isa/sound/adlib_card.c
Normal file
32
sys/i386/isa/sound/adlib_card.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
/*
|
||||
* linux/kernel/chr_drv/sound/adlib_card.c
|
||||
*
|
||||
* Detection routine for the AdLib card.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
|
||||
|
||||
long
|
||||
attach_adlib_card (long mem_start, struct address_info *hw_config)
|
||||
{
|
||||
|
||||
if (opl3_detect (FM_MONO))
|
||||
{
|
||||
mem_start = opl3_init (mem_start);
|
||||
}
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
int
|
||||
probe_adlib (struct address_info *hw_config)
|
||||
{
|
||||
return opl3_detect (FM_MONO);
|
||||
}
|
||||
|
||||
#endif
|
278
sys/i386/isa/sound/audio.c
Normal file
278
sys/i386/isa/sound/audio.c
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/audio.c
|
||||
*
|
||||
* Device file manager for /dev/audio
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
#ifndef EXCLUDE_AUDIO
|
||||
|
||||
#include "ulaw.h"
|
||||
|
||||
#define ON 1
|
||||
#define OFF 0
|
||||
|
||||
static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a
|
||||
* incomplete output block */
|
||||
static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV];
|
||||
static char *wr_dma_buf[MAX_DSP_DEV];
|
||||
|
||||
int
|
||||
audio_open (int dev, struct fileinfo *file)
|
||||
{
|
||||
int mode;
|
||||
int ret;
|
||||
|
||||
dev = dev >> 4;
|
||||
mode = file->mode & O_ACCMODE;
|
||||
|
||||
if ((ret = DMAbuf_open (dev, mode)) < 0)
|
||||
return ret;
|
||||
|
||||
wr_buff_no[dev] = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
audio_release (int dev, struct fileinfo *file)
|
||||
{
|
||||
int mode;
|
||||
|
||||
dev = dev >> 4;
|
||||
mode = file->mode & O_ACCMODE;
|
||||
|
||||
if (wr_buff_no[dev] >= 0)
|
||||
{
|
||||
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
||||
|
||||
wr_buff_no[dev] = -1;
|
||||
}
|
||||
|
||||
DMAbuf_release (dev, mode);
|
||||
}
|
||||
|
||||
#ifdef NO_INLINE_ASM
|
||||
static void
|
||||
translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
buff[i] = table[buff[i]];
|
||||
}
|
||||
|
||||
#else
|
||||
extern inline void
|
||||
translate_bytes (const void *table, void *buff, unsigned long n)
|
||||
{
|
||||
__asm__ ("cld\n"
|
||||
"1:\tlodsb\n\t"
|
||||
"xlatb\n\t"
|
||||
"stosb\n\t"
|
||||
"loop 1b\n\t":
|
||||
:"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
|
||||
:"bx", "cx", "di", "si", "ax");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
int c, p, l;
|
||||
int err;
|
||||
|
||||
dev = dev >> 4;
|
||||
|
||||
p = 0;
|
||||
c = count;
|
||||
|
||||
if (!count) /* Flush output */
|
||||
{
|
||||
if (wr_buff_no[dev] >= 0)
|
||||
{
|
||||
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
||||
|
||||
wr_buff_no[dev] = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (c)
|
||||
{ /* Perform output blocking */
|
||||
if (wr_buff_no[dev] < 0) /* There is no incomplete buffers */
|
||||
{
|
||||
if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0)
|
||||
return wr_buff_no[dev];
|
||||
wr_buff_ptr[dev] = 0;
|
||||
}
|
||||
|
||||
l = c;
|
||||
if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
|
||||
l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
|
||||
|
||||
COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
|
||||
|
||||
/* Insert local processing here */
|
||||
|
||||
#ifdef linux
|
||||
/* This just allows interrupts while the conversion is running */
|
||||
__asm__ ("sti");
|
||||
#endif
|
||||
translate_bytes (ulaw_dsp, &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
|
||||
|
||||
c -= l;
|
||||
p += l;
|
||||
wr_buff_ptr[dev] += l;
|
||||
|
||||
if (wr_buff_ptr[dev] >= wr_buff_size[dev])
|
||||
{
|
||||
if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
|
||||
return err;
|
||||
|
||||
wr_buff_no[dev] = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
int c, p, l;
|
||||
char *dmabuf;
|
||||
int buff_no;
|
||||
|
||||
dev = dev >> 4;
|
||||
p = 0;
|
||||
c = count;
|
||||
|
||||
while (c)
|
||||
{
|
||||
if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
|
||||
return buff_no;
|
||||
|
||||
if (l > c)
|
||||
l = c;
|
||||
|
||||
/* Insert any local processing here. */
|
||||
#ifdef linux
|
||||
/* This just allows interrupts while the conversion is running */
|
||||
__asm__ ("sti");
|
||||
#endif
|
||||
|
||||
translate_bytes (dsp_ulaw, dmabuf, l);
|
||||
|
||||
COPY_TO_USER (buf, p, dmabuf, l);
|
||||
|
||||
DMAbuf_rmchars (dev, buff_no, l);
|
||||
|
||||
p += l;
|
||||
c -= l;
|
||||
}
|
||||
|
||||
return count - c;
|
||||
}
|
||||
|
||||
int
|
||||
audio_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg)
|
||||
{
|
||||
dev = dev >> 4;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case SNDCTL_DSP_SYNC:
|
||||
if (wr_buff_no[dev] >= 0)
|
||||
{
|
||||
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
||||
|
||||
wr_buff_no[dev] = -1;
|
||||
}
|
||||
return DMAbuf_ioctl (dev, cmd, arg, 0);
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_POST:
|
||||
if (wr_buff_no[dev] >= 0)
|
||||
{
|
||||
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
||||
|
||||
wr_buff_no[dev] = -1;
|
||||
}
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_RESET:
|
||||
wr_buff_no[dev] = -1;
|
||||
return DMAbuf_ioctl (dev, cmd, arg, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
#if 1
|
||||
return RET_ERROR (EIO);
|
||||
#else
|
||||
return DMAbuf_ioctl (dev, cmd, arg, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
long
|
||||
audio_init (long mem_start)
|
||||
{
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub versions */
|
||||
|
||||
int
|
||||
audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
audio_open (int dev, struct fileinfo *file)
|
||||
{
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
void
|
||||
audio_release (int dev, struct fileinfo *file)
|
||||
{
|
||||
};
|
||||
int
|
||||
audio_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
long
|
||||
audio_init (long mem_start)
|
||||
{
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
83
sys/i386/isa/sound/dev_table.c
Normal file
83
sys/i386/isa/sound/dev_table.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/dev_table.c
|
||||
*
|
||||
* Device call tables.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#define _DEV_TABLE_C_
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
long
|
||||
sndtable_init (long mem_start)
|
||||
{
|
||||
int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
|
||||
|
||||
for (i = 0; i < (n - 1); i++)
|
||||
if (supported_drivers[i].probe (&supported_drivers[i].config))
|
||||
{
|
||||
#ifndef SHORT_BANNERS
|
||||
printk ("snd%d",
|
||||
supported_drivers[i].card_type);
|
||||
#endif
|
||||
|
||||
mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
|
||||
#ifndef SHORT_BANNERS
|
||||
printk (" at 0x%03x irq %d drq %d\n",
|
||||
supported_drivers[i].config.io_base,
|
||||
supported_drivers[i].config.irq,
|
||||
supported_drivers[i].config.dma);
|
||||
#endif
|
||||
}
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
int
|
||||
sndtable_probe (int unit, struct address_info *hw_config)
|
||||
{
|
||||
int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
|
||||
|
||||
if (!unit)
|
||||
return TRUE;
|
||||
|
||||
for (i = 0; i < (n - 1); i++)
|
||||
if (supported_drivers[i].card_type == unit)
|
||||
return supported_drivers[i].probe (hw_config);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
sndtable_init_card (int unit, struct address_info *hw_config)
|
||||
{
|
||||
int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
|
||||
|
||||
if (!unit)
|
||||
{
|
||||
if (sndtable_init (0) != 0)
|
||||
panic ("snd: Invalid memory allocation\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < (n - 1); i++)
|
||||
if (supported_drivers[i].card_type == unit)
|
||||
{
|
||||
if (supported_drivers[i].attach (0, hw_config) != 0)
|
||||
panic ("snd#: Invalid memory allocation\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
sndtable_get_cardcount (void)
|
||||
{
|
||||
return num_dspdevs + num_mixers + num_synths + num_midis;
|
||||
}
|
||||
|
||||
#endif
|
229
sys/i386/isa/sound/dev_table.h
Normal file
229
sys/i386/isa/sound/dev_table.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
linux/kernel/chr_drv/sound/dev_table.h
|
||||
|
||||
Global definitions for device call tables
|
||||
|
||||
(C) Hannu Savolainen 1992
|
||||
See COPYING for further details. Should be distributed with this file.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _DEV_TABLE_H_
|
||||
#define _DEV_TABLE_H_
|
||||
|
||||
/*
|
||||
* NOTE! NOTE! NOTE! NOTE!
|
||||
*
|
||||
* If you modify this file, please check the dev_table.c also.
|
||||
*
|
||||
* NOTE! NOTE! NOTE! NOTE!
|
||||
*/
|
||||
|
||||
struct card_info {
|
||||
int card_type; /* From soundcard.c */
|
||||
char *name;
|
||||
long (*attach) (long mem_start, struct address_info *hw_config);
|
||||
int (*probe) (struct address_info *hw_config);
|
||||
struct address_info config;
|
||||
};
|
||||
|
||||
/** UWM -- new MIDI structure here.. **/
|
||||
|
||||
struct generic_midi_info{
|
||||
char *name; /* Name of the MIDI device.. */
|
||||
long (*attach) (long mem_start);
|
||||
};
|
||||
|
||||
struct audio_operations {
|
||||
char name[32];
|
||||
int (*open) (int dev, int mode);
|
||||
void (*close) (int dev);
|
||||
void (*output_block) (int dev, unsigned long buf, int count, int intrflag);
|
||||
void (*start_input) (int dev, unsigned long buf, int count, int intrflag);
|
||||
int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local);
|
||||
int (*prepare_for_input) (int dev, int bufsize, int nbufs);
|
||||
int (*prepare_for_output) (int dev, int bufsize, int nbufs);
|
||||
void (*reset) (int dev);
|
||||
void (*halt_xfer) (int dev);
|
||||
int (*has_output_drained)(int dev);
|
||||
void (*copy_from_user)(int dev, char *localbuf, int localoffs,
|
||||
snd_rw_buf *userbuf, int useroffs, int len);
|
||||
};
|
||||
|
||||
struct mixer_operations {
|
||||
int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
|
||||
};
|
||||
|
||||
struct synth_operations {
|
||||
struct synth_info *info;
|
||||
int synth_type;
|
||||
int synth_subtype;
|
||||
|
||||
int (*open) (int dev, int mode);
|
||||
void (*close) (int dev);
|
||||
int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
|
||||
int (*kill_note) (int dev, int voice, int velocity);
|
||||
int (*start_note) (int dev, int voice, int note, int velocity);
|
||||
int (*set_instr) (int dev, int voice, int instr);
|
||||
void (*reset) (int dev);
|
||||
void (*hw_control) (int dev, unsigned char *event);
|
||||
int (*load_patch) (int dev, int format, snd_rw_buf *addr,
|
||||
int offs, int count, int pmgr_flag);
|
||||
void (*aftertouch) (int dev, int voice, int pressure);
|
||||
void (*controller) (int dev, int voice, int ctrl_num, int value);
|
||||
void (*panning) (int dev, int voice, int value);
|
||||
int (*pmgr_interface) (int dev, struct patmgr_info *info);
|
||||
};
|
||||
|
||||
struct midi_operations {
|
||||
struct midi_info info;
|
||||
int (*open) (int dev, int mode);
|
||||
void (*close) (int dev);
|
||||
int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
|
||||
int (*putc) (int dev, unsigned char data);
|
||||
int (*start_read) (int dev);
|
||||
int (*end_read) (int dev);
|
||||
void (*kick)(int dev);
|
||||
int (*command) (int dev, unsigned char data);
|
||||
int (*buffer_status) (int dev);
|
||||
};
|
||||
|
||||
/** UWM -- new structure for MIDI **/
|
||||
|
||||
struct generic_midi_operations {
|
||||
struct midi_info info;
|
||||
int (*open) (int dev, int mode);
|
||||
void (*close) (int dev);
|
||||
int (*write) (int dev, struct uio *data);
|
||||
int (*read) (int dev, struct uio *data);
|
||||
};
|
||||
|
||||
#ifndef ALL_EXTERNAL_TO_ME
|
||||
|
||||
#ifdef _MIDI_TABLE_C_
|
||||
|
||||
/** UWM **/
|
||||
struct generic_midi_operations * generic_midi_devs[MAX_MIDI_DEV] = {NULL};
|
||||
int num_generic_midis = 0, pro_midi_dev = 0;
|
||||
|
||||
struct generic_midi_info midi_supported[] = {
|
||||
|
||||
#ifndef EXCLUDE_PRO_MIDI
|
||||
{"ProAudioSpectrum MV101",pro_midi_attach}
|
||||
#endif
|
||||
};
|
||||
|
||||
int num_midi_drivers =
|
||||
sizeof (midi_supported) / sizeof(struct generic_midi_info);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _DEV_TABLE_C_
|
||||
struct audio_operations * dsp_devs[MAX_DSP_DEV] = {NULL}; int num_dspdevs = 0;
|
||||
struct mixer_operations * mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
|
||||
struct synth_operations * synth_devs[MAX_SYNTH_DEV] = {NULL}; int num_synths = 0;
|
||||
struct midi_operations * midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
|
||||
|
||||
|
||||
# ifndef EXCLUDE_MPU401
|
||||
int mpu401_dev = 0;
|
||||
# endif
|
||||
|
||||
/*
|
||||
* Note! The detection order is significant. Don't change it.
|
||||
*/
|
||||
|
||||
struct card_info supported_drivers[] = {
|
||||
#ifndef EXCLUDE_MPU401
|
||||
{SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401,
|
||||
{MPU_BASE, MPU_IRQ, 0}},
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_GUS
|
||||
{SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus,
|
||||
{GUS_BASE, GUS_IRQ, GUS_DMA}},
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_PAS
|
||||
{SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas,
|
||||
{PAS_BASE, PAS_IRQ, PAS_DMA}},
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_SB
|
||||
{SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb,
|
||||
{SBC_BASE, SBC_IRQ, SBC_DMA}},
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_YM3812
|
||||
{SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib,
|
||||
{FM_MONO, 0, 0}},
|
||||
#endif
|
||||
{0, "*?*", NULL}
|
||||
};
|
||||
|
||||
int num_sound_drivers =
|
||||
sizeof(supported_drivers) / sizeof (struct card_info);
|
||||
|
||||
|
||||
# ifndef EXCLUDE_AUDIO
|
||||
int sound_buffcounts[MAX_DSP_DEV] = {0};
|
||||
long sound_buffsizes[MAX_DSP_DEV] = {0};
|
||||
int sound_dsp_dmachan[MAX_DSP_DEV] = {0};
|
||||
int sound_dma_automode[MAX_DSP_DEV] = {0};
|
||||
# endif
|
||||
#else
|
||||
extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
|
||||
extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
|
||||
extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
|
||||
extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
|
||||
# ifndef EXCLUDE_MPU401
|
||||
extern int mpu401_dev;
|
||||
# endif
|
||||
|
||||
extern struct card_info supported_drivers[];
|
||||
extern int num_sound_drivers;
|
||||
|
||||
# ifndef EXCLUDE_AUDIO
|
||||
extern int sound_buffcounts[MAX_DSP_DEV];
|
||||
extern long sound_buffsizes[MAX_DSP_DEV];
|
||||
extern int sound_dsp_dmachan[MAX_DSP_DEV];
|
||||
extern int sound_dma_automode[MAX_DSP_DEV];
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
long sndtable_init(long mem_start);
|
||||
int sndtable_get_cardcount (void);
|
||||
long CMIDI_init(long mem_start); /* */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* If external to me.... :) */
|
||||
|
||||
#ifdef ALL_EXTERNAL_TO_ME
|
||||
|
||||
extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs;
|
||||
extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
|
||||
extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths;
|
||||
extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
|
||||
extern struct generic_midi_operations *generic_midi_devs[];
|
||||
extern int num_generic_midis, pro_midi_dev;
|
||||
|
||||
#ifndef EXCLUDE_MPU401
|
||||
extern int mpu401_dev;
|
||||
#endif
|
||||
|
||||
extern struct generic_midi_info midi_supported[];
|
||||
extern struct card_info supported_drivers[];
|
||||
extern int num_sound_drivers;
|
||||
extern int num_midi_drivers;
|
||||
#ifndef EXCLUDE_AUDIO
|
||||
extern int sound_buffcounts[MAX_DSP_DEV];
|
||||
extern long sound_buffsizes[MAX_DSP_DEV];
|
||||
extern int sound_dsp_dmachan[MAX_DSP_DEV];
|
||||
extern int sound_dma_automode[MAX_DSP_DEV];
|
||||
#endif
|
||||
|
||||
#endif
|
773
sys/i386/isa/sound/dmabuf.c
Normal file
773
sys/i386/isa/sound/dmabuf.c
Normal file
|
@ -0,0 +1,773 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/dmabuf.c
|
||||
*
|
||||
* The DMA buffer manager for digitized voice applications
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#include "sound_calls.h"
|
||||
|
||||
#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
|
||||
|
||||
#define MAX_SUB_BUFFERS 16
|
||||
|
||||
/*
|
||||
* The DSP channel can be used either for input or output. Variable
|
||||
* 'dma_mode' will be set when the program calls read or write first time
|
||||
* after open. Current version doesn't support mode changes without closing
|
||||
* and reopening the device. Support for this feature may be implemented in a
|
||||
* future version of this driver.
|
||||
*/
|
||||
|
||||
#define DMODE_NONE 0
|
||||
#define DMODE_OUTPUT 1
|
||||
#define DMODE_INPUT 2
|
||||
#define DMODE_INIT 3
|
||||
|
||||
DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]);
|
||||
|
||||
static int dma_mode[MAX_DSP_DEV] =
|
||||
{0}; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
|
||||
|
||||
static volatile int dmabuf_interrupted[MAX_DSP_DEV] =
|
||||
{0};
|
||||
|
||||
#ifdef ISC
|
||||
/* I don't like this. */
|
||||
#undef INTERRUPTIBLE_SLEEP_ON
|
||||
#define INTERRUPTIBLE_SLEEP_ON(A,F) { \
|
||||
A = F = 1; \
|
||||
if (sleep(&(A), (PZERO + 5) | PCATCH)) { \
|
||||
A = F = 0; \
|
||||
dmabuf_interrupted[dev] = 1; \
|
||||
dev_busy[dev] = 0; \
|
||||
dma_reset(dev); \
|
||||
dmabuf_interrupted[dev] = 0; \
|
||||
/* longjmp(u.u_qsav, 1); Where it goes??? */ \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pointers to raw buffers
|
||||
*/
|
||||
|
||||
char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
|
||||
unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
|
||||
int snd_raw_count[MAX_DSP_DEV];
|
||||
|
||||
/*
|
||||
* Device state tables
|
||||
*/
|
||||
|
||||
static int dev_busy[MAX_DSP_DEV];
|
||||
static int dev_active[MAX_DSP_DEV];
|
||||
static int dev_qlen[MAX_DSP_DEV];
|
||||
static int dev_qhead[MAX_DSP_DEV];
|
||||
static int dev_qtail[MAX_DSP_DEV];
|
||||
static int dev_underrun[MAX_DSP_DEV];
|
||||
static int bufferalloc_done[MAX_DSP_DEV] =
|
||||
{0};
|
||||
|
||||
/*
|
||||
* Logical buffers for each devices
|
||||
*/
|
||||
|
||||
static int dev_nbufs[MAX_DSP_DEV]; /* # of logical buffers ( >=
|
||||
* sound_buffcounts[dev] */
|
||||
static int dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS];
|
||||
static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS];
|
||||
static char *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS];
|
||||
static int dev_buffsize[MAX_DSP_DEV];
|
||||
|
||||
static void
|
||||
reorganize_buffers (int dev)
|
||||
{
|
||||
/*
|
||||
* This routine breaks the physical device buffers to logical ones.
|
||||
*/
|
||||
|
||||
unsigned long i, p, n;
|
||||
unsigned long sr, nc, sz, bsz;
|
||||
|
||||
sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
|
||||
nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
|
||||
sz = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
|
||||
|
||||
if (sr < 1 || nc < 1 || sz < 1)
|
||||
{
|
||||
printk ("SOUND: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
|
||||
sr = DSP_DEFAULT_SPEED;
|
||||
nc = 1;
|
||||
sz = 8;
|
||||
}
|
||||
|
||||
sz /= 8; /* Convert # of bits -> # of bytes */
|
||||
|
||||
sz = sr * nc * sz;
|
||||
|
||||
/*
|
||||
* Compute a buffer size not exeeding 1 second.
|
||||
*/
|
||||
|
||||
bsz = sound_buffsizes[dev];
|
||||
|
||||
while (bsz > sz)
|
||||
bsz >>= 1; /* Divide by 2 */
|
||||
|
||||
if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev])
|
||||
bsz >>= 1; /* Need at least 2 buffers */
|
||||
|
||||
dev_buffsize[dev] = bsz;
|
||||
n = 0;
|
||||
|
||||
/*
|
||||
* Now computing addresses for the logical buffers
|
||||
*/
|
||||
|
||||
for (i = 0; i < snd_raw_count[dev]; i++)
|
||||
{
|
||||
p = 0;
|
||||
|
||||
while ((p + bsz) <= sound_buffsizes[dev])
|
||||
{
|
||||
dev_buf[dev][n] = snd_raw_buf[dev][i] + p;
|
||||
dev_buf_phys[dev][n] = snd_raw_buf_phys[dev][i] + p;
|
||||
p += bsz;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
dev_nbufs[dev] = n;
|
||||
|
||||
for (i = 0; i < dev_nbufs[dev]; i++)
|
||||
{
|
||||
dev_counts[dev][i] = 0;
|
||||
}
|
||||
|
||||
bufferalloc_done[dev] = 1;
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_open (int dev, int mode)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (dev >= num_dspdevs)
|
||||
{
|
||||
printk ("PCM device %d not installed.\n", dev);
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
if (dev_busy[dev])
|
||||
return RET_ERROR (EBUSY);
|
||||
|
||||
if (!dsp_devs[dev])
|
||||
{
|
||||
printk ("DSP device %d not initialized\n", dev);
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
if (sound_buffcounts[dev] <= 0)
|
||||
return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
|
||||
|
||||
if ((retval = dsp_devs[dev]->open (dev, mode)) < 0)
|
||||
return retval;
|
||||
|
||||
dev_underrun[dev] = 0;
|
||||
|
||||
dev_busy[dev] = 1;
|
||||
|
||||
reorganize_buffers (dev);
|
||||
bufferalloc_done[dev] = 0;
|
||||
|
||||
dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dma_reset (int dev)
|
||||
{
|
||||
dsp_devs[dev]->reset (dev);
|
||||
|
||||
dev_qlen[dev] = 0;
|
||||
dev_qhead[dev] = 0;
|
||||
dev_qtail[dev] = 0;
|
||||
dev_active[dev] = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dma_sync (int dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long time;
|
||||
int timed_out;
|
||||
|
||||
if (dma_mode[dev] == DMODE_OUTPUT)
|
||||
{
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
timed_out = 0;
|
||||
time = GET_TIME ();
|
||||
|
||||
while ((!(PROCESS_ABORTING || dmabuf_interrupted[dev]) && !timed_out)
|
||||
&& dev_qlen[dev])
|
||||
{
|
||||
REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]);
|
||||
INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
|
||||
if ((GET_TIME () - time) > (10 * HZ))
|
||||
timed_out = 1;
|
||||
}
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
/*
|
||||
* Some devices such as GUS have huge amount of on board RAM for the
|
||||
* audio data. We have to wait util the device has finished playing.
|
||||
*/
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
if (dsp_devs[dev]->has_output_drained) /* Device has hidden buffers */
|
||||
{
|
||||
while (!(PROCESS_ABORTING || dmabuf_interrupted[dev])
|
||||
&& !dsp_devs[dev]->has_output_drained (dev))
|
||||
{
|
||||
REQUEST_TIMEOUT (HZ / 4, dev_sleeper[dev]);
|
||||
INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
|
||||
}
|
||||
}
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
return dev_qlen[dev];
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_release (int dev, int mode)
|
||||
{
|
||||
|
||||
if (!(PROCESS_ABORTING || dmabuf_interrupted[dev])
|
||||
&& (dma_mode[dev] == DMODE_OUTPUT))
|
||||
{
|
||||
dma_sync (dev);
|
||||
}
|
||||
|
||||
dma_reset (dev);
|
||||
|
||||
if (!dev_active[dev])
|
||||
dsp_devs[dev]->close (dev);
|
||||
|
||||
dma_mode[dev] = DMODE_NONE;
|
||||
dev_busy[dev] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_getrdbuffer (int dev, char **buf, int *len)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!bufferalloc_done[dev])
|
||||
reorganize_buffers (dev);
|
||||
|
||||
if (!dma_mode[dev])
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = dsp_devs[dev]->prepare_for_input (dev,
|
||||
dev_buffsize[dev], dev_nbufs[dev])) < 0)
|
||||
return err;
|
||||
dma_mode[dev] = DMODE_INPUT;
|
||||
}
|
||||
|
||||
if (dma_mode[dev] != DMODE_INPUT)
|
||||
return RET_ERROR (EBUSY); /* Can't change mode on fly */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
if (!dev_qlen[dev])
|
||||
{
|
||||
if (!dev_active[dev])
|
||||
{
|
||||
dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 0);
|
||||
dev_active[dev] = 1;
|
||||
}
|
||||
|
||||
/* Wait for the next block */
|
||||
#ifdef CRYPTO
|
||||
REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]);
|
||||
#else
|
||||
REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]);
|
||||
#endif
|
||||
INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
|
||||
}
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
if (!dev_qlen[dev])
|
||||
return RET_ERROR (EINTR);
|
||||
|
||||
*buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]];
|
||||
*len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]];
|
||||
|
||||
return dev_qhead[dev];
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_rmchars (int dev, int buff_no, int c)
|
||||
{
|
||||
int p = dev_counts[dev][dev_qhead[dev]] + c;
|
||||
|
||||
if (p >= dev_buffsize[dev])
|
||||
{ /* This buffer is now empty */
|
||||
dev_counts[dev][dev_qhead[dev]] = 0;
|
||||
dev_qlen[dev]--;
|
||||
dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
|
||||
}
|
||||
else
|
||||
dev_counts[dev][dev_qhead[dev]] = p;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
|
||||
{
|
||||
char *dmabuf;
|
||||
int buff_no, c, err;
|
||||
|
||||
/*
|
||||
* This routine returns at most 'count' bytes from the dsp input buffers.
|
||||
* Returns negative value if there is an error.
|
||||
*/
|
||||
|
||||
if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &c)) < 0)
|
||||
return buff_no;
|
||||
|
||||
if (c > count)
|
||||
c = count;
|
||||
|
||||
COPY_TO_USER (user_buf, 0, dmabuf, c);
|
||||
|
||||
if ((err = DMAbuf_rmchars (dev, buff_no, c)) < 0)
|
||||
return err;
|
||||
return c;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SNDCTL_DSP_RESET:
|
||||
dma_reset (dev);
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_SYNC:
|
||||
dma_sync (dev);
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_GETBLKSIZE:
|
||||
if (!bufferalloc_done[dev])
|
||||
reorganize_buffers (dev);
|
||||
|
||||
return IOCTL_OUT (arg, dev_buffsize[dev]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return dsp_devs[dev]->ioctl (dev, cmd, arg, local);
|
||||
}
|
||||
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_getwrbuffer (int dev, char **buf, int *size)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!bufferalloc_done[dev])
|
||||
reorganize_buffers (dev);
|
||||
|
||||
if (!dma_mode[dev])
|
||||
{
|
||||
int err;
|
||||
|
||||
dma_mode[dev] = DMODE_OUTPUT;
|
||||
if ((err = dsp_devs[dev]->prepare_for_output (dev,
|
||||
dev_buffsize[dev], dev_nbufs[dev])) < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (dma_mode[dev] != DMODE_OUTPUT)
|
||||
return RET_ERROR (EBUSY); /* Can't change mode on fly */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
if (dev_qlen[dev] == dev_nbufs[dev])
|
||||
{
|
||||
if (!dev_active[dev])
|
||||
{
|
||||
printk ("Soundcard warning: DMA not activated %d/%d\n",
|
||||
dev_qlen[dev], dev_nbufs[dev]);
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
/* Wait for free space */
|
||||
REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]); /* Overestimated timeout */
|
||||
INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
|
||||
}
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
if (dev_qlen[dev] == dev_nbufs[dev])
|
||||
return RET_ERROR (EIO); /* We have got signal (?) */
|
||||
|
||||
*buf = dev_buf[dev][dev_qtail[dev]];
|
||||
*size = dev_buffsize[dev];
|
||||
dev_counts[dev][dev_qtail[dev]] = 0;
|
||||
|
||||
return dev_qtail[dev];
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_start_output (int dev, int buff_no, int l)
|
||||
{
|
||||
if (buff_no != dev_qtail[dev])
|
||||
printk ("Soundcard warning: DMA buffers out of sync %d != %d\n", buff_no, dev_qtail[dev]);
|
||||
|
||||
dev_qlen[dev]++;
|
||||
|
||||
dev_counts[dev][dev_qtail[dev]] = l;
|
||||
|
||||
dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
|
||||
|
||||
if (!dev_active[dev])
|
||||
{
|
||||
dev_active[dev] = 1;
|
||||
dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
|
||||
{
|
||||
int chan = sound_dsp_dmachan[dev];
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* This function is not as portable as it should be.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The count must be one less than the actual size. This is handled by
|
||||
* set_dma_addr()
|
||||
*/
|
||||
|
||||
if (sound_dma_automode[dev])
|
||||
{ /* Auto restart mode. Transfer the whole
|
||||
* buffer */
|
||||
#ifdef linux
|
||||
DISABLE_INTR (flags);
|
||||
disable_dma (chan);
|
||||
clear_dma_ff (chan);
|
||||
set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
|
||||
set_dma_addr (chan, snd_raw_buf_phys[dev][0]);
|
||||
set_dma_count (chan, sound_buffsizes[dev]);
|
||||
enable_dma (chan);
|
||||
RESTORE_INTR (flags);
|
||||
#else
|
||||
|
||||
#ifdef __386BSD__
|
||||
printk ("sound: Invalid DMA mode for device %d\n", dev);
|
||||
|
||||
isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
|
||||
snd_raw_buf_phys[dev][0],
|
||||
sound_buffsizes[dev],
|
||||
chan);
|
||||
#else
|
||||
#ifdef ISC
|
||||
printk ("sound: Invalid DMA mode for device %d\n", dev);
|
||||
dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) | DMAMODE_AUTO,
|
||||
snd_raw_buf_phys[dev][0], count - 1);
|
||||
dma_enable (chan);
|
||||
#else
|
||||
# error This routine is not valid for this OS.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef linux
|
||||
DISABLE_INTR (flags);
|
||||
disable_dma (chan);
|
||||
clear_dma_ff (chan);
|
||||
set_dma_mode (chan, dma_mode);
|
||||
set_dma_addr (chan, physaddr);
|
||||
set_dma_count (chan, count);
|
||||
enable_dma (chan);
|
||||
RESTORE_INTR (flags);
|
||||
#else
|
||||
#ifdef __386BSD__
|
||||
isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
|
||||
physaddr,
|
||||
count,
|
||||
chan);
|
||||
#else
|
||||
|
||||
#ifdef ISC
|
||||
dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
|
||||
physaddr, count - 1);
|
||||
dma_enable (chan);
|
||||
#else
|
||||
# error This routine is not valid for this OS.
|
||||
#endif /* !ISC */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
long
|
||||
DMAbuf_init (long mem_start)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* In this version the DMA buffer allocation is done by sound_mem_init()
|
||||
* which is called by init/main.c
|
||||
*/
|
||||
|
||||
for (i = 0; i < MAX_DSP_DEV; i++)
|
||||
{
|
||||
dev_qlen[i] = 0;
|
||||
dev_qhead[i] = 0;
|
||||
dev_qtail[i] = 0;
|
||||
dev_active[i] = 0;
|
||||
dev_busy[i] = 0;
|
||||
bufferalloc_done[i] = 0;
|
||||
}
|
||||
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_outputintr (int dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_active[dev] = 0;
|
||||
dev_qlen[dev]--;
|
||||
dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
|
||||
|
||||
if (dev_qlen[dev])
|
||||
{
|
||||
dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 1);
|
||||
dev_active[dev] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dev_busy[dev])
|
||||
{
|
||||
dev_underrun[dev]++;
|
||||
dsp_devs[dev]->halt_xfer (dev);
|
||||
}
|
||||
else
|
||||
{ /* Device has been closed */
|
||||
dsp_devs[dev]->close (dev);
|
||||
}
|
||||
}
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
if (dev_sleep_flag[dev])
|
||||
{
|
||||
dev_sleep_flag[dev] = 0;
|
||||
WAKE_UP (dev_sleeper[dev]);
|
||||
}
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_inputintr (int dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_active[dev] = 0;
|
||||
if (!dev_busy[dev])
|
||||
{
|
||||
dsp_devs[dev]->close (dev);
|
||||
}
|
||||
else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
|
||||
{
|
||||
dev_underrun[dev]++;
|
||||
dsp_devs[dev]->halt_xfer (dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
dev_qlen[dev]++;
|
||||
dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
|
||||
|
||||
dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 1);
|
||||
dev_active[dev] = 1;
|
||||
}
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
if (dev_sleep_flag[dev])
|
||||
{
|
||||
dev_sleep_flag[dev] = 0;
|
||||
WAKE_UP (dev_sleeper[dev]);
|
||||
}
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_open_dma (int dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int chan = sound_dsp_dmachan[dev];
|
||||
|
||||
if (ALLOC_DMA_CHN (chan))
|
||||
{
|
||||
printk ("Unable to grab DMA%d for the audio driver\n", chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
#ifdef linux
|
||||
disable_dma (chan);
|
||||
clear_dma_ff (chan);
|
||||
#endif
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_close_dma (int dev)
|
||||
{
|
||||
int chan = sound_dsp_dmachan[dev];
|
||||
|
||||
DMAbuf_reset_dma (chan);
|
||||
RELEASE_DMA_CHN (chan);
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_reset_dma (int chan)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* The sound_mem_init() is called by mem_init() immediately after mem_map is
|
||||
* initialized and before free_page_list is created.
|
||||
*
|
||||
* This routine allocates DMA buffers at the end of available physical memory (
|
||||
* <16M) and marks pages reserved at mem_map.
|
||||
*/
|
||||
|
||||
#else
|
||||
/* Stub versions if audio services not included */
|
||||
|
||||
int
|
||||
DMAbuf_open (int dev, int mode)
|
||||
{
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_release (int dev, int mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_getwrbuffer (int dev, char **buf, int *size)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_getrdbuffer (int dev, char **buf, int *len)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_rmchars (int dev, int buff_no, int c)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_start_output (int dev, int buff_no, int l)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
long
|
||||
DMAbuf_init (long mem_start)
|
||||
{
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
|
||||
{
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
DMAbuf_open_dma (int chan)
|
||||
{
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_close_dma (int chan)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_reset_dma (int chan)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_inputintr (int dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
DMAbuf_outputintr (int dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
28
sys/i386/isa/sound/finetune.h
Normal file
28
sys/i386/isa/sound/finetune.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifdef SEQUENCER_C
|
||||
/*
|
||||
* Copyright Hannu Savolainen 1993
|
||||
* See COPYING for further details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
unsigned short finetune_table[128] =
|
||||
{
|
||||
/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499,
|
||||
/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567,
|
||||
/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637,
|
||||
/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707,
|
||||
/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777,
|
||||
/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848,
|
||||
/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919,
|
||||
/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991,
|
||||
/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063,
|
||||
/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136,
|
||||
/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210,
|
||||
/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284,
|
||||
/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358,
|
||||
/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433,
|
||||
/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509,
|
||||
/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585
|
||||
};
|
||||
#else
|
||||
extern unsigned short finetune_table[128];
|
||||
#endif
|
183
sys/i386/isa/sound/gus_card.c
Normal file
183
sys/i386/isa/sound/gus_card.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
|
||||
/*
|
||||
* linux/kernel/chr_drv/sound/gus_card.c
|
||||
*
|
||||
* Detection routine for the Gravis Ultrasound.
|
||||
*
|
||||
* (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
|
||||
|
||||
#include "gus_hw.h"
|
||||
|
||||
void gusintr (int);
|
||||
|
||||
int gus_base, gus_irq, gus_dma;
|
||||
|
||||
static int
|
||||
set_gus_irq (int interrupt_level)
|
||||
{
|
||||
int retcode;
|
||||
|
||||
#ifdef linux
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = gusintr;
|
||||
|
||||
#ifdef SND_SA_INTERRUPT
|
||||
sa.sa_flags = SA_INTERRUPT;
|
||||
#else
|
||||
sa.sa_flags = 0;
|
||||
#endif
|
||||
|
||||
sa.sa_mask = 0;
|
||||
sa.sa_restorer = NULL;
|
||||
|
||||
retcode = irqaction (interrupt_level, &sa);
|
||||
|
||||
if (retcode < 0)
|
||||
{
|
||||
printk ("GUS: IRQ%d already in use\n", interrupt_level);
|
||||
}
|
||||
|
||||
#else
|
||||
/* # error Unimplemented for this OS */
|
||||
#endif
|
||||
return retcode;
|
||||
}
|
||||
|
||||
int
|
||||
gus_set_midi_irq (int interrupt_level)
|
||||
{
|
||||
int retcode;
|
||||
|
||||
#ifdef linux
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = gus_midi_interrupt;
|
||||
|
||||
#ifdef SND_SA_INTERRUPT
|
||||
sa.sa_flags = SA_INTERRUPT;
|
||||
#else
|
||||
sa.sa_flags = 0;
|
||||
#endif
|
||||
|
||||
sa.sa_mask = 0;
|
||||
sa.sa_restorer = NULL;
|
||||
|
||||
retcode = irqaction (interrupt_level, &sa);
|
||||
|
||||
if (retcode < 0)
|
||||
{
|
||||
printk ("GUS: IRQ%d already in use\n", interrupt_level);
|
||||
}
|
||||
|
||||
#else
|
||||
/* # error Unimplemented for this OS */
|
||||
#endif
|
||||
return retcode;
|
||||
}
|
||||
|
||||
long
|
||||
attach_gus_card (long mem_start, struct address_info *hw_config)
|
||||
{
|
||||
int io_addr;
|
||||
|
||||
set_gus_irq (hw_config->irq);
|
||||
|
||||
if (gus_wave_detect (hw_config->io_base)) /* Try first the default */
|
||||
{
|
||||
mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
|
||||
#ifndef EXCLUDE_MIDI
|
||||
mem_start = gus_midi_init (mem_start);
|
||||
#endif
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
#ifndef EXCLUDE_GUS_IODETECT
|
||||
|
||||
/*
|
||||
* Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
|
||||
*/
|
||||
|
||||
for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
|
||||
if (io_addr != hw_config->io_base) /* Already tested */
|
||||
if (gus_wave_detect (io_addr))
|
||||
{
|
||||
printk (" WARNING! GUS found at %03x, config was %03x ", io_addr, hw_config->io_base);
|
||||
mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
|
||||
#ifndef EXCLUDE_MIDI
|
||||
mem_start = gus_midi_init (mem_start);
|
||||
#endif
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return mem_start; /* Not detected */
|
||||
}
|
||||
|
||||
int
|
||||
probe_gus (struct address_info *hw_config)
|
||||
{
|
||||
int io_addr;
|
||||
|
||||
if (gus_wave_detect (hw_config->io_base))
|
||||
return 1;
|
||||
|
||||
#ifndef EXCLUDE_GUS_IODETECT
|
||||
|
||||
/*
|
||||
* Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
|
||||
*/
|
||||
|
||||
for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
|
||||
if (io_addr != hw_config->io_base) /* Already tested */
|
||||
if (gus_wave_detect (io_addr))
|
||||
return 1;
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gusintr (int unit)
|
||||
{
|
||||
unsigned char src;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!(src = INB (u_IrqStatus)))
|
||||
return;
|
||||
|
||||
if (src & DMA_TC_IRQ)
|
||||
{
|
||||
guswave_dma_irq ();
|
||||
}
|
||||
|
||||
if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
|
||||
{
|
||||
#ifndef EXCLUDE_MIDI
|
||||
gus_midi_interrupt (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
|
||||
{
|
||||
printk ("T");
|
||||
gus_write8 (0x45, 0); /* Timer control */
|
||||
}
|
||||
|
||||
if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
|
||||
{
|
||||
gus_voice_irq ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
35
sys/i386/isa/sound/gus_hw.h
Normal file
35
sys/i386/isa/sound/gus_hw.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
/*
|
||||
* I/O addresses
|
||||
*/
|
||||
|
||||
#define u_Base (gus_base + 0x000)
|
||||
#define u_Mixer u_Base
|
||||
#define u_Status (gus_base + 0x006)
|
||||
#define u_TimerControl (gus_base + 0x008)
|
||||
#define u_TimerData (gus_base + 0x009)
|
||||
#define u_IRQDMAControl (gus_base + 0x00b)
|
||||
#define u_MidiControl (gus_base + 0x100)
|
||||
#define MIDI_RESET 0x03
|
||||
#define MIDI_ENABLE_XMIT 0x20
|
||||
#define MIDI_ENABLE_RCV 0x80
|
||||
#define u_MidiStatus u_MidiControl
|
||||
#define MIDI_RCV_FULL 0x01
|
||||
#define MIDI_XMIT_EMPTY 0x02
|
||||
#define MIDI_FRAME_ERR 0x10
|
||||
#define MIDI_OVERRUN 0x20
|
||||
#define MIDI_IRQ_PEND 0x80
|
||||
#define u_MidiData (gus_base + 0x101)
|
||||
#define u_Voice (gus_base + 0x102)
|
||||
#define u_Command (gus_base + 0x103)
|
||||
#define u_DataLo (gus_base + 0x104)
|
||||
#define u_DataHi (gus_base + 0x105)
|
||||
#define u_IrqStatus u_Status
|
||||
# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */
|
||||
# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */
|
||||
# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */
|
||||
# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */
|
||||
# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */
|
||||
# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */
|
||||
# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */
|
||||
#define u_DRAMIO (gus_base + 0x107)
|
257
sys/i386/isa/sound/gus_midi.c
Normal file
257
sys/i386/isa/sound/gus_midi.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/gus2_midi.c
|
||||
*
|
||||
* The low level driver for the GUS Midi Interface.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#include "gus_hw.h"
|
||||
|
||||
#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI)
|
||||
|
||||
static int midi_busy = 0, input_opened = 0;
|
||||
static int my_dev;
|
||||
static int output_used = 0;
|
||||
static volatile unsigned char gus_midi_control;
|
||||
|
||||
static unsigned char tmp_queue[256];
|
||||
static volatile int qlen;
|
||||
static volatile unsigned char qhead, qtail;
|
||||
extern int gus_base, gus_irq, gus_dma;
|
||||
|
||||
#define GUS_MIDI_STATUS() INB(u_MidiStatus)
|
||||
|
||||
static int
|
||||
gus_midi_open (int dev, int mode)
|
||||
{
|
||||
|
||||
if (midi_busy)
|
||||
{
|
||||
printk ("GUS: Midi busy\n");
|
||||
return RET_ERROR (EBUSY);
|
||||
}
|
||||
|
||||
OUTB (MIDI_RESET, u_MidiControl);
|
||||
gus_delay ();
|
||||
|
||||
gus_midi_control = 0;
|
||||
input_opened = 0;
|
||||
|
||||
if (mode == OPEN_READ || mode == OPEN_READWRITE)
|
||||
{
|
||||
gus_midi_control |= MIDI_ENABLE_RCV;
|
||||
input_opened = 1;
|
||||
}
|
||||
|
||||
if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
|
||||
{
|
||||
gus_midi_control |= MIDI_ENABLE_XMIT;
|
||||
}
|
||||
|
||||
OUTB (gus_midi_control, u_MidiControl); /* Enable */
|
||||
|
||||
midi_busy = 1;
|
||||
qlen = qhead = qtail = output_used = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dump_to_midi (unsigned char midi_byte)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ok = 0;
|
||||
|
||||
output_used = 1;
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY)
|
||||
{
|
||||
ok = 1;
|
||||
OUTB (midi_byte, u_MidiData);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Enable Midi xmit interrupts (again) */
|
||||
gus_midi_control |= MIDI_ENABLE_XMIT;
|
||||
OUTB (gus_midi_control, u_MidiControl);
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
gus_midi_close (int dev)
|
||||
{
|
||||
/* Reset FIFO pointers, disable intrs */
|
||||
|
||||
OUTB (MIDI_RESET, u_MidiControl);
|
||||
midi_busy = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gus_midi_out (int dev, unsigned char midi_byte)
|
||||
{
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Drain the local queue first
|
||||
*/
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
while (qlen && dump_to_midi (tmp_queue[qhead]))
|
||||
{
|
||||
qlen--;
|
||||
qhead++;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
/*
|
||||
* Output the byte if the local queue is empty.
|
||||
*/
|
||||
|
||||
if (!qlen)
|
||||
if (dump_to_midi (midi_byte))
|
||||
return 1; /* OK */
|
||||
|
||||
/*
|
||||
* Put to the local queue
|
||||
*/
|
||||
|
||||
if (qlen >= 256)
|
||||
return 0; /* Local queue full */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
tmp_queue[qtail] = midi_byte;
|
||||
qlen++;
|
||||
qtail++;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
gus_midi_start_read (int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gus_midi_end_read (int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gus_midi_ioctl (int dev, unsigned cmd, unsigned arg)
|
||||
{
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
static void
|
||||
gus_midi_kick (int dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
gus_midi_buffer_status (int dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!output_used)
|
||||
return 0;
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (qlen && dump_to_midi (tmp_queue[qhead]))
|
||||
{
|
||||
qlen--;
|
||||
qhead++;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
|
||||
}
|
||||
|
||||
static struct midi_operations gus_midi_operations =
|
||||
{
|
||||
{"Gravis UltraSound", 0},
|
||||
gus_midi_open,
|
||||
gus_midi_close,
|
||||
gus_midi_ioctl,
|
||||
gus_midi_out,
|
||||
gus_midi_start_read,
|
||||
gus_midi_end_read,
|
||||
gus_midi_kick,
|
||||
NULL, /* command */
|
||||
gus_midi_buffer_status
|
||||
};
|
||||
|
||||
long
|
||||
gus_midi_init (long mem_start)
|
||||
{
|
||||
OUTB (MIDI_RESET, u_MidiControl);
|
||||
|
||||
my_dev = num_midis;
|
||||
midi_devs[num_midis++] = &gus_midi_operations;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
void
|
||||
gus_midi_interrupt (int dummy)
|
||||
{
|
||||
unsigned char stat, data;
|
||||
unsigned long flags;
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
stat = GUS_MIDI_STATUS ();
|
||||
|
||||
if (stat & MIDI_RCV_FULL)
|
||||
{
|
||||
data = INB (u_MidiData);
|
||||
if (input_opened)
|
||||
sequencer_midi_input (my_dev, data);
|
||||
}
|
||||
|
||||
if (stat & MIDI_XMIT_EMPTY)
|
||||
{
|
||||
while (qlen && dump_to_midi (tmp_queue[qhead]))
|
||||
{
|
||||
qlen--;
|
||||
qhead++;
|
||||
}
|
||||
|
||||
if (!qlen)
|
||||
{
|
||||
/* Disable Midi output interrupts, since no data in the buffer */
|
||||
gus_midi_control &= ~MIDI_ENABLE_XMIT;
|
||||
OUTB (gus_midi_control, u_MidiControl);
|
||||
}
|
||||
}
|
||||
|
||||
if (stat & MIDI_FRAME_ERR)
|
||||
printk ("Midi framing error\n");
|
||||
if (stat & MIDI_OVERRUN && input_opened)
|
||||
printk ("GUS: Midi input overrun\n");
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
101
sys/i386/isa/sound/gus_vol.c
Normal file
101
sys/i386/isa/sound/gus_vol.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* gus_vol.c - Compute volume for GUS.
|
||||
*
|
||||
* Greg Lee 1993.
|
||||
*/
|
||||
#include "sound_config.h"
|
||||
#ifndef EXCLUDE_GUS
|
||||
|
||||
#define GUS_VOLUME gus_wave_volume
|
||||
|
||||
|
||||
extern int gus_wave_volume;
|
||||
|
||||
/*
|
||||
* Calculate gus volume from note velocity, main volume, expression, and
|
||||
* intrinsic patch volume given in patch library. Expression is multiplied
|
||||
* in, so it emphasizes differences in note velocity, while main volume is
|
||||
* added in -- I don't know whether this is right, but it seems reasonable to
|
||||
* me. (In the previous stage, main volume controller messages were changed
|
||||
* to expression controller messages, if they were found to be used for
|
||||
* dynamic volume adjustments, so here, main volume can be assumed to be
|
||||
* constant throughout a song.)
|
||||
*
|
||||
* Intrinsic patch volume is added in, but if over 64 is also multiplied in, so
|
||||
* we can give a big boost to very weak voices like nylon guitar and the
|
||||
* basses. The normal value is 64. Strings are assigned lower values.
|
||||
*/
|
||||
unsigned short
|
||||
gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
|
||||
{
|
||||
int i, m, n, x;
|
||||
|
||||
|
||||
/*
|
||||
* A voice volume of 64 is considered neutral, so adjust the main volume if
|
||||
* something other than this neutral value was assigned in the patch
|
||||
* library.
|
||||
*/
|
||||
x = 256 + 6 * (voicev - 64);
|
||||
|
||||
/* Boost expression by voice volume above neutral. */
|
||||
if (voicev > 65)
|
||||
xpn += voicev - 64;
|
||||
xpn += (voicev - 64) / 2;
|
||||
|
||||
/* Combine multiplicative and level components. */
|
||||
x = vel * xpn * 6 + (voicev / 4) * x;
|
||||
|
||||
#ifdef GUS_VOLUME
|
||||
/*
|
||||
* Further adjustment by installation-specific master volume control
|
||||
* (default 50).
|
||||
*/
|
||||
x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
|
||||
#endif
|
||||
|
||||
if (x < (1 << 11))
|
||||
return (11 << 8);
|
||||
else if (x >= 65535)
|
||||
return ((15 << 8) | 255);
|
||||
|
||||
/*
|
||||
* Convert to gus's logarithmic form with 4 bit exponent i and 8 bit
|
||||
* mantissa m.
|
||||
*/
|
||||
n = x;
|
||||
i = 7;
|
||||
if (n < 128)
|
||||
{
|
||||
while (i > 0 && n < (1 << i))
|
||||
i--;
|
||||
}
|
||||
else
|
||||
while (n > 255)
|
||||
{
|
||||
n >>= 1;
|
||||
i++;
|
||||
}
|
||||
/*
|
||||
* Mantissa is part of linear volume not expressed in exponent. (This is
|
||||
* not quite like real logs -- I wonder if it's right.)
|
||||
*/
|
||||
m = x - (1 << i);
|
||||
|
||||
/* Adjust mantissa to 8 bits. */
|
||||
if (m > 0)
|
||||
{
|
||||
if (i > 8)
|
||||
m >>= i - 8;
|
||||
else if (i < 8)
|
||||
m <<= 8 - i;
|
||||
}
|
||||
|
||||
/* low volumes give occasional sour notes */
|
||||
if (i < 11)
|
||||
return (11 << 8);
|
||||
|
||||
return ((i << 8) + m);
|
||||
}
|
||||
|
||||
#endif
|
2523
sys/i386/isa/sound/gus_wave.c
Normal file
2523
sys/i386/isa/sound/gus_wave.c
Normal file
File diff suppressed because it is too large
Load diff
16
sys/i386/isa/sound/gustest/Makefile
Normal file
16
sys/i386/isa/sound/gustest/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
all: gustest gusload gmod midithru
|
||||
|
||||
gustest: gustest.c
|
||||
$(CC) -o gustest gustest.c -lm
|
||||
|
||||
gusload: gusload.c
|
||||
$(CC) -o gusload gusload.c
|
||||
|
||||
gmod: gmod.c
|
||||
$(CC) -o gmod gmod.c
|
||||
|
||||
midithru: midithru.c
|
||||
$(CC) -o midithru midithru.c
|
||||
|
||||
clean:
|
||||
rm -f gusload gustest gmod midithru *.o
|
67
sys/i386/isa/sound/gustest/Readme
Normal file
67
sys/i386/isa/sound/gustest/Readme
Normal file
|
@ -0,0 +1,67 @@
|
|||
The programs in this directory are more or less incompletely implemented.
|
||||
I have used them for debugging purposes while developing the driver.
|
||||
|
||||
Files in this directory:
|
||||
|
||||
|
||||
../ultrasound.h (sys/ultrasound.h)
|
||||
This file contains some macros which are similar than
|
||||
the procedures provided by GUSUNIT.PAS. See gustest.c
|
||||
for more information.
|
||||
INSTALL THIS FILE TO YOUR /usr/include/sys !!!!!!!!!!!!
|
||||
|
||||
gusload.c This program can be used to load patches (samples) to
|
||||
the DRAM of GUS. It understands the format used in the
|
||||
.pat files shipped with GUS.
|
||||
|
||||
Usage: gusload pgm# patchfile.
|
||||
or gusload reset #Removes all patches from memory.
|
||||
|
||||
You should load just the patches you will need to play
|
||||
a Midi file, since the memory capacity of GUS is rather
|
||||
limited (256k-1M).
|
||||
|
||||
Example:
|
||||
|
||||
gusload 0 acpiano.pat
|
||||
gusload 1 britepno.pat
|
||||
gusload 19 church.pat
|
||||
|
||||
This program is not required if the adagio package is
|
||||
used. It can do the patch uploading itself.
|
||||
|
||||
gmod.c This is a simple module player which demonstrates
|
||||
programming with GUS. It doesn't try to interpret
|
||||
most of the effect commands. In fact this program
|
||||
may interpret the modules incorrectly since I am
|
||||
not a module player expert.
|
||||
This version plays .MOD, .STM and .669 modules.
|
||||
|
||||
midithru.c This program reads messages from the Midi interface
|
||||
and plays the notes with an internal synthesizer
|
||||
(FM or GUS). The program accepts one argument, the
|
||||
synthesizer device number. In addition to the note on
|
||||
and note off messages it interprets also program changes
|
||||
and channel pressure messages.
|
||||
If you need an example on programming the /dev/sequencer,
|
||||
this is a good one. The voice allocation algorithm is
|
||||
not good so don't look at it.
|
||||
|
||||
NOTE! This program is useful with gmod. Jus load
|
||||
a module with gmod. Wait until the module has
|
||||
finished or hit ^C. Now you can play the samples
|
||||
with the midithru program.
|
||||
|
||||
NOTE2! You need a Midi keyboard to use this program. In
|
||||
addition the Midi interface of GUS is not supported
|
||||
yet which means you need also PAS16 or MPU-401.
|
||||
|
||||
pmtest.c
|
||||
gpatinfo.c ******* For information only *******
|
||||
These programs demonstrate the patch manager interface
|
||||
which will be included to some later driver version.
|
||||
This interface is not complete in version 1.99.9.
|
||||
Using pmtest will hang your system sooner or later.
|
||||
|
||||
Hannu Savolainen
|
||||
hsavolai@cs.helsinki.fi
|
131
sys/i386/isa/sound/gustest/gmidi.h
Normal file
131
sys/i386/isa/sound/gustest/gmidi.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
char patch_names[][9] =
|
||||
{
|
||||
/* 0 */ "acpiano",
|
||||
/* 1 */ "britepno",
|
||||
/* 2 */ "synpiano",
|
||||
/* 3 */ "honktonk",
|
||||
/* 4 */ "epiano1",
|
||||
/* 5 */ "epiano2",
|
||||
/* 6 */ "hrpschrd",
|
||||
/* 7 */ "clavinet",
|
||||
/* 8 */ "celeste",
|
||||
/* 9 */ "glocken",
|
||||
/* 10 */ "musicbox",
|
||||
/* 11 */ "vibes",
|
||||
/* 12 */ "marimba",
|
||||
/* 13 */ "xylophon",
|
||||
/* 14 */ "tubebell",
|
||||
/* 15 */ "santur",
|
||||
/* 16 */ "homeorg",
|
||||
/* 17 */ "percorg",
|
||||
/* 18 */ "rockorg",
|
||||
/* 19 */ "church",
|
||||
/* 20 */ "reedorg",
|
||||
/* 21 */ "accordn",
|
||||
/* 22 */ "harmonca",
|
||||
/* 23 */ "concrtna",
|
||||
/* 24 */ "nyguitar",
|
||||
/* 25 */ "acguitar",
|
||||
/* 26 */ "jazzgtr",
|
||||
/* 27 */ "cleangtr",
|
||||
/* 28 */ "mutegtr",
|
||||
/* 29 */ "odguitar",
|
||||
/* 30 */ "distgtr",
|
||||
/* 31 */ "gtrharm",
|
||||
/* 32 */ "acbass",
|
||||
/* 33 */ "fngrbass",
|
||||
/* 34 */ "pickbass",
|
||||
/* 35 */ "fretless",
|
||||
/* 36 */ "slapbas1",
|
||||
/* 37 */ "slapbas2",
|
||||
/* 38 */ "synbass1",
|
||||
/* 39 */ "synbass2",
|
||||
/* 40 */ "violin",
|
||||
/* 41 */ "viola",
|
||||
/* 42 */ "cello",
|
||||
/* 43 */ "contraba",
|
||||
/* 44 */ "marcato",
|
||||
/* 45 */ "pizzcato",
|
||||
/* 46 */ "harp",
|
||||
/* 47 */ "timpani",
|
||||
/* 48 */ "marcato",
|
||||
/* 49 */ "slowstr",
|
||||
/* 50 */ "synstr1",
|
||||
/* 51 */ "synstr2",
|
||||
/* 52 */ "choir",
|
||||
/* 53 */ "doo",
|
||||
/* 54 */ "voices",
|
||||
/* 55 */ "orchhit",
|
||||
/* 56 */ "trumpet",
|
||||
/* 57 */ "trombone",
|
||||
/* 58 */ "tuba",
|
||||
/* 59 */ "mutetrum",
|
||||
/* 60 */ "frenchrn",
|
||||
/* 61 */ "hitbrass",
|
||||
/* 62 */ "synbras1",
|
||||
/* 63 */ "synbras2",
|
||||
/* 64 */ "sprnosax",
|
||||
/* 65 */ "altosax",
|
||||
/* 66 */ "tenorsax",
|
||||
/* 67 */ "barisax",
|
||||
/* 68 */ "oboe",
|
||||
/* 69 */ "englhorn",
|
||||
/* 70 */ "bassoon",
|
||||
/* 71 */ "clarinet",
|
||||
/* 72 */ "piccolo",
|
||||
/* 73 */ "flute",
|
||||
/* 74 */ "recorder",
|
||||
/* 75 */ "woodflut",
|
||||
/* 76 */ "bottle",
|
||||
/* 77 */ "shakazul",
|
||||
/* 78 */ "whistle",
|
||||
/* 79 */ "ocarina",
|
||||
/* 80 */ "sqrwave",
|
||||
/* 81 */ "sawwave",
|
||||
/* 82 */ "calliope",
|
||||
/* 83 */ "chiflead",
|
||||
/* 84 */ "voxlead",
|
||||
/* 85 */ "voxlead",
|
||||
/* 86 */ "lead5th",
|
||||
/* 87 */ "basslead",
|
||||
/* 88 */ "fantasia",
|
||||
/* 89 */ "warmpad",
|
||||
/* 90 */ "polysyn",
|
||||
/* 91 */ "ghostie",
|
||||
/* 92 */ "bowglass",
|
||||
/* 93 */ "metalpad",
|
||||
/* 94 */ "halopad",
|
||||
/* 95 */ "sweeper",
|
||||
/* 96 */ "aurora",
|
||||
/* 97 */ "soundtrk",
|
||||
/* 98 */ "crystal",
|
||||
/* 99 */ "atmosphr",
|
||||
/* 100 */ "freshair",
|
||||
/* 101 */ "unicorn",
|
||||
/* 102 */ "sweeper",
|
||||
/* 103 */ "startrak",
|
||||
/* 104 */ "sitar",
|
||||
/* 105 */ "banjo",
|
||||
/* 106 */ "shamisen",
|
||||
/* 107 */ "koto",
|
||||
/* 108 */ "kalimba",
|
||||
/* 109 */ "bagpipes",
|
||||
/* 110 */ "fiddle",
|
||||
/* 111 */ "Shannai",
|
||||
/* 112 */ "carillon",
|
||||
/* 113 */ "agogo",
|
||||
/* 114 */ "steeldrm",
|
||||
/* 115 */ "woodblk",
|
||||
/* 116 */ "taiko",
|
||||
/* 117 */ "toms",
|
||||
/* 118 */ "syntom",
|
||||
/* 119 */ "revcym",
|
||||
/* 120 */ "fx-fret",
|
||||
/* 121 */ "fx-blow",
|
||||
/* 122 */ "seashore",
|
||||
/* 123 */ "jungle",
|
||||
/* 124 */ "telephon",
|
||||
/* 125 */ "helicptr",
|
||||
/* 126 */ "applause",
|
||||
/* 127 */ "ringwhsl"
|
||||
};
|
1589
sys/i386/isa/sound/gustest/gmod.c
Normal file
1589
sys/i386/isa/sound/gustest/gmod.c
Normal file
File diff suppressed because it is too large
Load diff
176
sys/i386/isa/sound/gustest/gpatinfo.c
Normal file
176
sys/i386/isa/sound/gustest/gpatinfo.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* gpatinfo.c: This program demonstrates the patch management
|
||||
* interface of the GUS driver.
|
||||
*
|
||||
* NOTE! The patch manager interface is highly device dependent,
|
||||
* currently incompletely implemented prototype and
|
||||
* will change before final implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/ultrasound.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "gmidi.h"
|
||||
|
||||
#define GUS_DEV gus_dev
|
||||
|
||||
#define patch_access(cmd, rec) \
|
||||
rec.command = cmd;\
|
||||
rec.device = gus_dev;\
|
||||
if (ioctl(seqfd, SNDCTL_PMGR_IFACE, &rec)==-1)\
|
||||
{\
|
||||
perror("/dev/sequencer(SNDCTL_PMGR_IFACE/" #cmd ")");\
|
||||
exit(-1);\
|
||||
}
|
||||
|
||||
SEQ_DEFINEBUF (2048);
|
||||
|
||||
int seqfd;
|
||||
|
||||
int gus_dev = -1;
|
||||
|
||||
/*
|
||||
* The function seqbuf_dump() must always be provided
|
||||
*/
|
||||
|
||||
void
|
||||
seqbuf_dump ()
|
||||
{
|
||||
if (_seqbufptr)
|
||||
if (write (seqfd, _seqbuf, _seqbufptr) == -1)
|
||||
{
|
||||
perror ("write /dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
_seqbufptr = 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i, j, n;
|
||||
struct synth_info info;
|
||||
struct patch_info *patch;
|
||||
struct patmgr_info mgr, mgr2, mgr3;
|
||||
|
||||
if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
|
||||
{
|
||||
perror ("/dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
|
||||
{
|
||||
perror ("/dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* First locate the GUS device
|
||||
*/
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
info.device = i;
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
|
||||
{
|
||||
perror ("/dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (info.synth_type == SYNTH_TYPE_SAMPLE
|
||||
&& info.synth_subtype == SAMPLE_TYPE_GUS)
|
||||
gus_dev = i;
|
||||
}
|
||||
|
||||
if (gus_dev == -1)
|
||||
{
|
||||
fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
printf("Gravis UltraSound device = %d\n", gus_dev);
|
||||
|
||||
/*
|
||||
* Get type of the Patch Manager interface of the GUS device
|
||||
*/
|
||||
|
||||
patch_access(PM_GET_DEVTYPE, mgr);
|
||||
printf("Patch manager type: %d\n", mgr.parm1);
|
||||
|
||||
if (mgr.parm1 != PMTYPE_WAVE)
|
||||
{
|
||||
fprintf(stderr, "Hups, this program seems to be obsolete\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The GUS driver supports up to 256 different midi program numbers but
|
||||
* this limit can be changed before compiling the driver. The following
|
||||
* call returns the value compiled to the driver.
|
||||
*/
|
||||
|
||||
patch_access(PM_GET_PGMMAP, mgr);
|
||||
printf("Device supports %d midi programs.\n", mgr.parm1);
|
||||
|
||||
/*
|
||||
* Each program can be undefined or it may have one or more patches.
|
||||
* A patch consists of header and the waveform data. If there is more
|
||||
* than one patch in a program, the right one is selected by checking the
|
||||
* note number when the program is played.
|
||||
*
|
||||
* The following call reads an array indexed by program number. Each
|
||||
* element defines the number of patches defined for the corresponding
|
||||
* program.
|
||||
*/
|
||||
printf("Loaded programs:\n");
|
||||
|
||||
for (i=0;i<mgr.parm1;i++)
|
||||
if (mgr.data.data8[i])
|
||||
{
|
||||
printf("%03d: %2d patches\n", i, mgr.data.data8[i]);
|
||||
|
||||
/*
|
||||
* Next get the magic keys of the patches associated with this program.
|
||||
* This key can be used to access the patc data.
|
||||
*/
|
||||
mgr2.parm1=i;
|
||||
patch_access(PM_GET_PGM_PATCHES, mgr2);
|
||||
for (j = 0;j<mgr2.parm1;j++)
|
||||
{
|
||||
printf("\tPatch %d: %3d ", j, mgr2.data.data32[j]);
|
||||
|
||||
/*
|
||||
* The last step is to read the patch header (without wave data).
|
||||
* The header is returned in the mgr3.data. The field parm1 returns
|
||||
* address of the wave data in tge GUS DRAM. Parm2 returns
|
||||
* size of the struct patch_info in the kernel.
|
||||
*
|
||||
* There is also the PM_SET_PATCH call which allows modification of the
|
||||
* header data. The only limitation is that the sample len cannot be
|
||||
* increased.
|
||||
*/
|
||||
mgr3.parm1 = mgr2.data.data32[j];
|
||||
patch_access(PM_GET_PATCH, mgr3);
|
||||
patch = (struct patch_info *)&mgr3.data; /* Pointer to the patch hdr */
|
||||
|
||||
printf("DRAM ptr = %7d, sample len =%6d bytes.\n",
|
||||
mgr3.parm1, patch->len);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
i = gus_dev;
|
||||
|
||||
if (ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &i)==-1) exit(-1);
|
||||
printf("%d bytes of DRAM available for wave data\n", i);
|
||||
|
||||
|
||||
exit(0);
|
||||
}
|
350
sys/i386/isa/sound/gustest/gusload.c
Normal file
350
sys/i386/isa/sound/gustest/gusload.c
Normal file
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* patutil.c - A sample program which loads patches to the Gravis
|
||||
* Ultrasound
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PATCH_PATH
|
||||
#define PATCH_PATH "/D/ultrasnd/midi"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/ultrasound.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "gmidi.h"
|
||||
|
||||
struct pat_header
|
||||
{
|
||||
char magic[12];
|
||||
char version[10];
|
||||
char description[60];
|
||||
unsigned char instruments;
|
||||
char voices;
|
||||
char channels;
|
||||
unsigned short nr_waveforms;
|
||||
unsigned short master_volume;
|
||||
unsigned long data_size;
|
||||
};
|
||||
|
||||
struct sample_header
|
||||
{
|
||||
char name[7];
|
||||
unsigned char fractions;
|
||||
long len;
|
||||
long loop_start;
|
||||
long loop_end;
|
||||
unsigned short base_freq;
|
||||
long low_note;
|
||||
long high_note;
|
||||
long base_note;
|
||||
short detune;
|
||||
unsigned char panning;
|
||||
|
||||
unsigned char envelope_rate[6];
|
||||
unsigned char envelope_offset[6];
|
||||
|
||||
unsigned char tremolo_sweep;
|
||||
unsigned char tremolo_rate;
|
||||
unsigned char tremolo_depth;
|
||||
|
||||
unsigned char vibrato_sweep;
|
||||
unsigned char vibrato_rate;
|
||||
unsigned char vibrato_depth;
|
||||
|
||||
char modes;
|
||||
|
||||
short scale_frequency;
|
||||
unsigned short scale_factor;
|
||||
};
|
||||
|
||||
#define GUS_DEV gus_dev
|
||||
|
||||
SEQ_DEFINEBUF (2048);
|
||||
|
||||
int seqfd;
|
||||
|
||||
int gus_dev = -1;
|
||||
|
||||
struct patch_info *patch;
|
||||
|
||||
/*
|
||||
* The function seqbuf_dump() must always be provided
|
||||
*/
|
||||
|
||||
void
|
||||
seqbuf_dump ()
|
||||
{
|
||||
if (_seqbufptr)
|
||||
if (write (seqfd, _seqbuf, _seqbufptr) == -1)
|
||||
{
|
||||
perror ("write /dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
_seqbufptr = 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i, n, patfd, pgm, print_only = 0;
|
||||
struct synth_info info;
|
||||
struct pat_header header;
|
||||
struct sample_header sample;
|
||||
char buf[256];
|
||||
char name[256];
|
||||
long offset;
|
||||
|
||||
if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
|
||||
{
|
||||
perror ("/dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
|
||||
{
|
||||
perror ("/dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
info.device = i;
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
|
||||
{
|
||||
perror ("/dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (info.synth_type == SYNTH_TYPE_SAMPLE
|
||||
&& info.synth_subtype == SAMPLE_TYPE_GUS)
|
||||
gus_dev = i;
|
||||
}
|
||||
|
||||
if (gus_dev == -1)
|
||||
{
|
||||
fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
if (!strcmp (argv[1], "reset"))
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
|
||||
perror ("Sample reset");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf (stderr, "Usage: %s pgm# patchfile\n", argv[0]);
|
||||
fprintf (stderr, " or : %s pgm# GM\n", argv[0]);
|
||||
fprintf (stderr, " or : %s pgm# -l\n", argv[0]);
|
||||
fprintf (stderr, " or : %s reset\n", argv[0]);
|
||||
fprintf (stderr, " or : %s -l patchfile\n", argv[0]);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
pgm = atoi (argv[1]);
|
||||
strcpy (name, argv[2]);
|
||||
|
||||
if (strcmp (name, "GM") == 0 || strcmp(name, "-l")==0)
|
||||
{
|
||||
if (strcmp (name, "-l") == 0) print_only = 1;
|
||||
if (pgm < 0 || pgm > 127)
|
||||
{
|
||||
fprintf (stderr, "pgm# must be between 0 and 127\n");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]);
|
||||
|
||||
if (!print_only)
|
||||
fprintf (stderr, "Loading program %d from %s\n", pgm, name);
|
||||
}
|
||||
else if (strcmp (argv[1], "-l") == 0)
|
||||
print_only = 1;
|
||||
|
||||
if ((patfd = open (name, O_RDONLY, 0)) == -1)
|
||||
{
|
||||
perror (name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (read (patfd, buf, 0xef) != 0xef)
|
||||
{
|
||||
fprintf (stderr, "%s: Short file\n", name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
memcpy ((char *) &header, buf, sizeof (header));
|
||||
|
||||
if (strncmp (header.magic, "GF1PATCH110", 12))
|
||||
{
|
||||
fprintf (stderr, "%s: Not a patch file\n", name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (strncmp (header.version, "ID#000002", 10))
|
||||
{
|
||||
fprintf (stderr, "%s: Incompatible patch file version\n", name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
header.nr_waveforms = *(unsigned short *) &buf[85];
|
||||
header.master_volume = *(unsigned short *) &buf[87];
|
||||
|
||||
if (print_only)
|
||||
{
|
||||
printf ("Patch file: %s contains %d samples\n\n", name, header.nr_waveforms);
|
||||
printf ("Master volume: %d\n", header.master_volume);
|
||||
}
|
||||
|
||||
offset = 0xef;
|
||||
|
||||
for (i = 0; i < header.nr_waveforms; i++)
|
||||
{
|
||||
if (lseek (patfd, offset, 0) == -1)
|
||||
{
|
||||
perror (name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (read (patfd, &buf, sizeof (sample)) != sizeof (sample))
|
||||
{
|
||||
fprintf (stderr, "%s: Short file\n", name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
memcpy ((char *) &sample, buf, sizeof (sample));
|
||||
|
||||
/*
|
||||
* Since some fields of the patch record are not 32bit aligned, we must
|
||||
* handle them specially.
|
||||
*/
|
||||
sample.low_note = *(long *) &buf[22];
|
||||
sample.high_note = *(long *) &buf[26];
|
||||
sample.base_note = *(long *) &buf[30];
|
||||
sample.detune = *(short *) &buf[34];
|
||||
sample.panning = (unsigned char) buf[36];
|
||||
|
||||
memcpy (sample.envelope_rate, &buf[37], 6);
|
||||
memcpy (sample.envelope_offset, &buf[43], 6);
|
||||
|
||||
sample.tremolo_sweep = (unsigned char) buf[49];
|
||||
sample.tremolo_rate = (unsigned char) buf[50];
|
||||
sample.tremolo_depth = (unsigned char) buf[51];
|
||||
|
||||
sample.vibrato_sweep = (unsigned char) buf[52];
|
||||
sample.vibrato_rate = (unsigned char) buf[53];
|
||||
sample.vibrato_depth = (unsigned char) buf[54];
|
||||
sample.modes = (unsigned char) buf[55];
|
||||
sample.scale_frequency = *(short *) &buf[56];
|
||||
sample.scale_factor = *(unsigned short *) &buf[58];
|
||||
|
||||
if (print_only)
|
||||
{
|
||||
printf("\nSample: %03d / %s\n", i, sample.name);
|
||||
printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end);
|
||||
printf ("Flags: ");
|
||||
if (sample.modes & WAVE_16_BITS)
|
||||
printf ("16 bit ");
|
||||
if (sample.modes & WAVE_UNSIGNED)
|
||||
printf ("unsigned ");
|
||||
if (sample.modes & WAVE_LOOP_BACK)
|
||||
printf("reverse ");
|
||||
if (sample.modes & WAVE_BIDIR_LOOP)
|
||||
printf("bidir ");
|
||||
if (sample.modes & WAVE_LOOPING)
|
||||
printf ("looping "); else printf("one_shot" );
|
||||
if (sample.modes & WAVE_SUSTAIN_ON)
|
||||
printf ("sustain ");
|
||||
if (sample.modes & WAVE_ENVELOPES)
|
||||
printf ("enveloped ");
|
||||
printf ("\n");
|
||||
|
||||
if (sample.modes & WAVE_ENVELOPES)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf ("Envelope info: ");
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
printf ("%d/%d ", sample.envelope_rate[i],
|
||||
sample.envelope_offset[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
printf("Tremolo: sweep=%d, rate=%d, depth=%d\n",
|
||||
sample.tremolo_sweep,
|
||||
sample.tremolo_rate,
|
||||
sample.tremolo_depth);
|
||||
|
||||
printf("Vibrato: sweep=%d, rate=%d, depth=%d\n",
|
||||
sample.vibrato_sweep,
|
||||
sample.vibrato_rate,
|
||||
sample.vibrato_depth);
|
||||
}
|
||||
|
||||
offset = offset + 96;
|
||||
patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len);
|
||||
|
||||
patch->key = GUS_PATCH;
|
||||
patch->device_no = GUS_DEV;
|
||||
patch->instr_no = pgm;
|
||||
patch->mode = sample.modes | WAVE_TREMOLO |
|
||||
WAVE_VIBRATO | WAVE_SCALE;
|
||||
patch->len = sample.len;
|
||||
patch->loop_start = sample.loop_start;
|
||||
patch->loop_end = sample.loop_end;
|
||||
patch->base_note = sample.base_note;
|
||||
patch->high_note = sample.high_note;
|
||||
patch->low_note = sample.low_note;
|
||||
patch->base_freq = sample.base_freq;
|
||||
patch->detuning = sample.detune;
|
||||
patch->panning = (sample.panning - 7) * 16;
|
||||
|
||||
memcpy (patch->env_rate, sample.envelope_rate, 6);
|
||||
memcpy (patch->env_offset, sample.envelope_offset, 6);
|
||||
|
||||
patch->tremolo_sweep = sample.tremolo_sweep;
|
||||
patch->tremolo_rate = sample.tremolo_rate;
|
||||
patch->tremolo_depth = sample.tremolo_depth;
|
||||
|
||||
patch->vibrato_sweep = sample.vibrato_sweep;
|
||||
patch->vibrato_rate = sample.vibrato_rate;
|
||||
patch->vibrato_depth = sample.vibrato_depth;
|
||||
|
||||
patch->scale_frequency = sample.scale_frequency;
|
||||
patch->scale_factor = sample.scale_factor;
|
||||
|
||||
patch->volume = header.master_volume;
|
||||
|
||||
if (lseek (patfd, offset, 0) == -1)
|
||||
{
|
||||
perror (name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (!print_only)
|
||||
{
|
||||
if (read (patfd, patch->data, sample.len) != sample.len)
|
||||
{
|
||||
fprintf (stderr, "%s: Short file\n", name);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
SEQ_WRPATCH (patch, sizeof (*patch) + sample.len);
|
||||
}
|
||||
|
||||
offset = offset + sample.len;
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
325
sys/i386/isa/sound/gustest/midithru.c
Normal file
325
sys/i386/isa/sound/gustest/midithru.c
Normal file
|
@ -0,0 +1,325 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
SEQ_DEFINEBUF (2048);
|
||||
SEQ_PM_DEFINES;
|
||||
|
||||
int seqfd, dev = 0;
|
||||
unsigned char buf[100];
|
||||
int bufp;
|
||||
|
||||
/* LRU list for free operators */
|
||||
|
||||
unsigned char free_list[256];
|
||||
int fhead=0, ftail=0, flen=0;
|
||||
|
||||
/* LRU list for still playing notes */
|
||||
|
||||
unsigned char note_list[256];
|
||||
int nhead=0, ntail=0, nlen=0;
|
||||
unsigned char oper_note[32];
|
||||
|
||||
int pgm = 0;
|
||||
int num_voices;
|
||||
int bender = 0; /* Initially off */
|
||||
|
||||
void
|
||||
seqbuf_dump ()
|
||||
{
|
||||
if (_seqbufptr)
|
||||
if (write (seqfd, _seqbuf, _seqbufptr) == -1)
|
||||
{
|
||||
perror ("write /dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
_seqbufptr = 0;
|
||||
}
|
||||
|
||||
void
|
||||
stop_note(int note, int velocity)
|
||||
{
|
||||
int i, op;
|
||||
|
||||
op=255;
|
||||
|
||||
for (i=0;i<num_voices && op==255;i++)
|
||||
{
|
||||
if (oper_note[i]== note) op=i;
|
||||
}
|
||||
|
||||
if (op==255)
|
||||
{
|
||||
fprintf(stderr, "Note %d off, note not started\n", note);
|
||||
fprintf(stderr, "%d, %d\n", flen, nlen);
|
||||
return; /* Has already been killed ??? */
|
||||
}
|
||||
|
||||
SEQ_STOP_NOTE(dev, op, note, velocity);
|
||||
SEQ_DUMPBUF();
|
||||
|
||||
oper_note[op] = 255;
|
||||
|
||||
free_list[ftail]=op;
|
||||
flen++;
|
||||
ftail = (ftail+1) % num_voices;
|
||||
|
||||
for (i=0;i<16;i++)
|
||||
if (note_list[i] == op) note_list[i] = 255;
|
||||
|
||||
while (nlen && note_list[nhead] == 255)
|
||||
{
|
||||
nlen--;
|
||||
/* printf("Remove from note queue %d, len %d\n", nhead, nlen); */
|
||||
nhead = (nhead+1) % 256;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kill_one_note()
|
||||
{
|
||||
int oldest;
|
||||
|
||||
if (!nlen) {fprintf(stderr, "Free list empty but no notes playing\n");return;} /* No notes playing */
|
||||
|
||||
oldest = note_list[nhead];
|
||||
nlen--;
|
||||
nhead = (nhead+1) % 256;
|
||||
|
||||
fprintf(stderr, "Killing oper %d, note %d\n", oldest, oper_note[oldest]);
|
||||
|
||||
if (oldest== 255) return; /* Was already stopped. Why? */
|
||||
|
||||
stop_note(oper_note[oldest], 127);
|
||||
}
|
||||
|
||||
void
|
||||
start_note(int note, int velocity)
|
||||
{
|
||||
int free;
|
||||
|
||||
if (!flen) kill_one_note();
|
||||
|
||||
if (!flen) {printf("** no free voices\n");return;} /* Panic??? */
|
||||
|
||||
free = free_list[fhead];
|
||||
flen--;
|
||||
fhead = (fhead+1) % num_voices;
|
||||
|
||||
note_list[ntail] = free;
|
||||
|
||||
if (nlen>255)
|
||||
{
|
||||
#if 0
|
||||
fprintf(stderr, "Note list overflow %d, %d, %d\n",
|
||||
nlen, nhead, ntail);
|
||||
#endif
|
||||
nlen=0; /* Overflow -> hard reset */
|
||||
}
|
||||
nlen++;
|
||||
ntail = (ntail+1) % 256;
|
||||
|
||||
oper_note[free] = note;
|
||||
|
||||
SEQ_SET_PATCH(dev, free, pgm);
|
||||
SEQ_PITCHBEND(dev, free, bender);
|
||||
SEQ_START_NOTE(dev, free, note, velocity);
|
||||
SEQ_DUMPBUF();
|
||||
}
|
||||
|
||||
void
|
||||
channel_pressure(int ch, int pressure)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<num_voices;i++)
|
||||
{
|
||||
if (oper_note[i] != 255)
|
||||
{
|
||||
#if 1
|
||||
SEQ_CHN_PRESSURE(dev, i, pressure);
|
||||
#else
|
||||
SEQ_EXPRESSION(dev, i, pressure);
|
||||
#endif
|
||||
SEQ_DUMPBUF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pitch_bender(int ch, int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
value -= 8192;
|
||||
|
||||
bender = value;
|
||||
|
||||
for (i=0;i<num_voices;i++)
|
||||
{
|
||||
if (oper_note[i] != 255)
|
||||
{
|
||||
bender = value;
|
||||
SEQ_PITCHBEND(dev, i, value);
|
||||
SEQ_DUMPBUF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
do_buf()
|
||||
{
|
||||
int ch = buf[0] & 0x0f;
|
||||
int value;
|
||||
|
||||
switch (buf[0] & 0xf0)
|
||||
{
|
||||
case 0x90: /* Note on */
|
||||
if (bufp < 3) break;
|
||||
/* printf("Note on %d %d %d\n", ch, buf[1], buf[2]); */
|
||||
if (buf[2])
|
||||
start_note(buf[1], buf[2]);
|
||||
else
|
||||
stop_note(buf[1], buf[2]);
|
||||
bufp=1;
|
||||
break;
|
||||
|
||||
case 0xb0: /* Control change */
|
||||
if (bufp < 3) break;
|
||||
/* printf("Control change %d %d %d\n", ch, buf[1], buf[2]); */
|
||||
bufp=1;
|
||||
break;
|
||||
|
||||
case 0x80: /* Note off */
|
||||
if (bufp < 3) break;
|
||||
/* printf("Note off %d %d %d\n", ch, buf[1], buf[2]); */
|
||||
stop_note(buf[1], buf[2]);
|
||||
bufp=1;
|
||||
break;
|
||||
|
||||
case 0xe0: /* Pitch bender */
|
||||
if (bufp < 3) break;
|
||||
value = ((buf[2] & 0x7f) << 7) | (buf[1] & 0x7f);
|
||||
/* printf("Pitch bender %d %d\n", ch, value >> 7); */
|
||||
pitch_bender(ch, value);
|
||||
bufp=1;
|
||||
break;
|
||||
|
||||
case 0xc0: /* Pgm change */
|
||||
if (bufp < 2) break;
|
||||
/* printf("Pgm change %d %d\n", ch, buf[1]); */
|
||||
pgm = buf[1];
|
||||
if (PM_LOAD_PATCH(dev, 0, pgm) < 0)
|
||||
if (errno != ESRCH) /* No such process */
|
||||
perror("PM_LOAD_PATCH");
|
||||
bufp=0;
|
||||
break;
|
||||
|
||||
case 0xd0: /* Channel pressure */
|
||||
if (bufp < 2) break;
|
||||
/* printf("Channel pressure %d %d\n", ch, buf[1]); */
|
||||
channel_pressure(ch, buf[1]);
|
||||
bufp=1;
|
||||
break;
|
||||
|
||||
default:
|
||||
bufp=0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i, n, max_voice = 999;
|
||||
|
||||
struct synth_info info;
|
||||
|
||||
unsigned char ev[4], *p;
|
||||
|
||||
if (argc >= 2) dev = atoi(argv[1]);
|
||||
|
||||
for (i=0;i<16;i++) oper_note[i] = 255;
|
||||
|
||||
if ((seqfd = open ("/dev/sequencer", O_RDWR, 0)) == -1)
|
||||
{
|
||||
perror ("open /dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (argc >= 3)
|
||||
{
|
||||
int d = dev;
|
||||
ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &d);
|
||||
}
|
||||
|
||||
info.device = dev;
|
||||
|
||||
if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &info)==-1)
|
||||
{
|
||||
perror ("info /dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
num_voices = info.nr_voices;
|
||||
if (num_voices>max_voice)num_voices = max_voice;
|
||||
fprintf(stderr, "Output to synth device %d (%s)\n", dev, info.name);
|
||||
fprintf(stderr, "%d voices available\n", num_voices);
|
||||
|
||||
for (i=0;i<num_voices;i++)
|
||||
{
|
||||
flen++;
|
||||
free_list[fhead] = i;
|
||||
fhead = (fhead+1) % num_voices;
|
||||
}
|
||||
|
||||
bufp = 0;
|
||||
if (PM_LOAD_PATCH(dev, 0, 0) < 0) /* Load the default instrument */
|
||||
if (errno != ESRCH) /* No such process */
|
||||
perror("PM_LOAD_PATCH");
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((n = read (seqfd, ev, sizeof (ev))) == -1)
|
||||
{
|
||||
perror ("read /dev/sequencer");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
for (i = 0; i <= (n / 4); i++)
|
||||
{
|
||||
p = &ev[i * 4];
|
||||
|
||||
if (p[0] == SEQ_MIDIPUTC && p[2] == 0 /* Midi if# == 0 */)
|
||||
{
|
||||
/* printf("%02x ", p[1]);fflush(stdout); */
|
||||
if (p[1] & 0x80) /* Status */
|
||||
{
|
||||
if (bufp)
|
||||
do_buf ();
|
||||
buf[0] = p[1];
|
||||
bufp = 1;
|
||||
}
|
||||
else if (bufp)
|
||||
{
|
||||
buf[bufp++] = p[1];
|
||||
if ((buf[0] & 0xf0) == 0x90 || (buf[0] & 0xf0) == 0x80 || (buf[0] & 0xf0) == 0xb0 ||
|
||||
(buf[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
if (bufp == 3)
|
||||
do_buf ();
|
||||
}
|
||||
else
|
||||
if ((buf[0] & 0xf0) == 0xc0 || (buf[0] & 0xf0) == 0xd0)
|
||||
{
|
||||
if (bufp == 2) do_buf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
411
sys/i386/isa/sound/gustest/pmtest.c
Normal file
411
sys/i386/isa/sound/gustest/pmtest.c
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* CAUTION! This program is just an incompletely implemented version
|
||||
* of the patch manager daemon for GUS. Using this program
|
||||
* with the driver version 1.99.9 will hang your system
|
||||
* completely (sooner or later).
|
||||
*
|
||||
* This program is for information only. The final
|
||||
* implementation of the patch manager will not be
|
||||
* compatible with this one.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/ultrasound.h>
|
||||
#include <strings.h>
|
||||
#include <sys/errno.h>
|
||||
#include "gmidi.h"
|
||||
|
||||
#ifndef PATCH_PATH
|
||||
#define PATCH_PATH "/D/ultrasnd/midi"
|
||||
#endif
|
||||
|
||||
char loadmap[256] =
|
||||
{0}; /* 1 if the patch is already loaded */
|
||||
|
||||
struct pat_header
|
||||
{
|
||||
char magic[12];
|
||||
char version[10];
|
||||
char description[60];
|
||||
unsigned char instruments;
|
||||
char voices;
|
||||
char channels;
|
||||
unsigned short nr_waveforms;
|
||||
unsigned short master_volume;
|
||||
unsigned long data_size;
|
||||
};
|
||||
|
||||
struct sample_header
|
||||
{
|
||||
char name[7];
|
||||
unsigned char fractions;
|
||||
long len;
|
||||
long loop_start;
|
||||
long loop_end;
|
||||
unsigned short base_freq;
|
||||
long low_note;
|
||||
long high_note;
|
||||
long base_note;
|
||||
short detune;
|
||||
unsigned char panning;
|
||||
|
||||
unsigned char envelope_rate[6];
|
||||
unsigned char envelope_offset[6];
|
||||
|
||||
unsigned char tremolo_sweep;
|
||||
unsigned char tremolo_rate;
|
||||
unsigned char tremolo_depth;
|
||||
|
||||
unsigned char vibrato_sweep;
|
||||
unsigned char vibrato_rate;
|
||||
unsigned char vibrato_depth;
|
||||
|
||||
char modes;
|
||||
|
||||
short scale_frequency;
|
||||
unsigned short scale_factor;
|
||||
};
|
||||
int seqfd = 0, gus_dev = -1;
|
||||
|
||||
struct patch_info *patch;
|
||||
|
||||
int
|
||||
do_load_patch (struct patmgr_info *rec)
|
||||
{
|
||||
int i, patfd, pgm, print_only = 0;
|
||||
struct pat_header header;
|
||||
struct sample_header sample;
|
||||
char buf[256];
|
||||
char name[256];
|
||||
long offset;
|
||||
|
||||
pgm = rec->data.data8[0];
|
||||
|
||||
if (loadmap[pgm])
|
||||
return 0; /* Already loaded */
|
||||
|
||||
sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]);
|
||||
|
||||
if ((patfd = open (name, O_RDONLY, 0)) == -1)
|
||||
{
|
||||
perror (name);
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (read (patfd, buf, 0xef) != 0xef)
|
||||
{
|
||||
fprintf (stderr, "%s: Short file\n", name);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
memcpy ((char *) &header, buf, sizeof (header));
|
||||
|
||||
if (strncmp (header.magic, "GF1PATCH110", 12))
|
||||
{
|
||||
fprintf (stderr, "%s: Not a patch file\n", name);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp (header.version, "ID#000002", 10))
|
||||
{
|
||||
fprintf (stderr, "%s: Incompatible patch file version\n", name);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
header.nr_waveforms = *(unsigned short *) &buf[85];
|
||||
header.master_volume = *(unsigned short *) &buf[87];
|
||||
|
||||
printf ("GUS: Loading: %s\n", name);
|
||||
|
||||
offset = 0xef;
|
||||
|
||||
for (i = 0; i < header.nr_waveforms; i++)
|
||||
{
|
||||
if (lseek (patfd, offset, 0) == -1)
|
||||
{
|
||||
perror (name);
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (read (patfd, &buf, sizeof (sample)) != sizeof (sample))
|
||||
{
|
||||
fprintf (stderr, "%s: Short file\n", name);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
memcpy ((char *) &sample, buf, sizeof (sample));
|
||||
|
||||
/*
|
||||
* Since some fields of the patch record are not 32bit aligned, we must
|
||||
* handle them specially.
|
||||
*/
|
||||
sample.low_note = *(long *) &buf[22];
|
||||
sample.high_note = *(long *) &buf[26];
|
||||
sample.base_note = *(long *) &buf[30];
|
||||
sample.detune = *(short *) &buf[34];
|
||||
sample.panning = (unsigned char) buf[36];
|
||||
|
||||
memcpy (sample.envelope_rate, &buf[37], 6);
|
||||
memcpy (sample.envelope_offset, &buf[43], 6);
|
||||
|
||||
sample.tremolo_sweep = (unsigned char) buf[49];
|
||||
sample.tremolo_rate = (unsigned char) buf[50];
|
||||
sample.tremolo_depth = (unsigned char) buf[51];
|
||||
|
||||
sample.vibrato_sweep = (unsigned char) buf[52];
|
||||
sample.vibrato_rate = (unsigned char) buf[53];
|
||||
sample.vibrato_depth = (unsigned char) buf[54];
|
||||
sample.modes = (unsigned char) buf[55];
|
||||
sample.scale_frequency = *(short *) &buf[56];
|
||||
sample.scale_factor = *(unsigned short *) &buf[58];
|
||||
|
||||
if (print_only)
|
||||
{
|
||||
printf ("\nSample: %03d / %s\n", i, sample.name);
|
||||
printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end);
|
||||
printf ("Flags: ");
|
||||
if (sample.modes & WAVE_16_BITS)
|
||||
printf ("16 bit ");
|
||||
if (sample.modes & WAVE_UNSIGNED)
|
||||
printf ("unsigned ");
|
||||
if (sample.modes & WAVE_LOOP_BACK)
|
||||
printf ("reverse ");
|
||||
if (sample.modes & WAVE_BIDIR_LOOP)
|
||||
printf ("bidir ");
|
||||
if (sample.modes & WAVE_LOOPING)
|
||||
printf ("looping ");
|
||||
else
|
||||
printf ("one_shot");
|
||||
if (sample.modes & WAVE_SUSTAIN_ON)
|
||||
printf ("sustain ");
|
||||
if (sample.modes & WAVE_ENVELOPES)
|
||||
printf ("enveloped ");
|
||||
printf ("\n");
|
||||
|
||||
if (sample.modes & WAVE_ENVELOPES)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf ("Envelope info: ");
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
printf ("%d/%d ", sample.envelope_rate[i],
|
||||
sample.envelope_offset[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
printf ("Tremolo: sweep=%d, rate=%d, depth=%d\n",
|
||||
sample.tremolo_sweep,
|
||||
sample.tremolo_rate,
|
||||
sample.tremolo_depth);
|
||||
|
||||
printf ("Vibrato: sweep=%d, rate=%d, depth=%d\n",
|
||||
sample.vibrato_sweep,
|
||||
sample.vibrato_rate,
|
||||
sample.vibrato_depth);
|
||||
}
|
||||
|
||||
offset = offset + 96;
|
||||
patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len);
|
||||
|
||||
patch->key = GUS_PATCH;
|
||||
patch->device_no = gus_dev;
|
||||
patch->instr_no = pgm;
|
||||
patch->mode = sample.modes | WAVE_TREMOLO |
|
||||
WAVE_VIBRATO | WAVE_SCALE;
|
||||
patch->len = sample.len;
|
||||
patch->loop_start = sample.loop_start;
|
||||
patch->loop_end = sample.loop_end;
|
||||
patch->base_note = sample.base_note;
|
||||
patch->high_note = sample.high_note;
|
||||
patch->low_note = sample.low_note;
|
||||
patch->base_freq = sample.base_freq;
|
||||
patch->detuning = sample.detune;
|
||||
patch->panning = (sample.panning - 7) * 16;
|
||||
|
||||
memcpy (patch->env_rate, sample.envelope_rate, 6);
|
||||
memcpy (patch->env_offset, sample.envelope_offset, 6);
|
||||
|
||||
patch->tremolo_sweep = sample.tremolo_sweep;
|
||||
patch->tremolo_rate = sample.tremolo_rate;
|
||||
patch->tremolo_depth = sample.tremolo_depth;
|
||||
|
||||
patch->vibrato_sweep = sample.vibrato_sweep;
|
||||
patch->vibrato_rate = sample.vibrato_rate;
|
||||
patch->vibrato_depth = sample.vibrato_depth;
|
||||
|
||||
patch->scale_frequency = sample.scale_frequency;
|
||||
patch->scale_factor = sample.scale_factor;
|
||||
|
||||
patch->volume = header.master_volume;
|
||||
|
||||
if (lseek (patfd, offset, 0) == -1)
|
||||
{
|
||||
perror (name);
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (!print_only)
|
||||
{
|
||||
if (read (patfd, patch->data, sample.len) != sample.len)
|
||||
{
|
||||
fprintf (stderr, "%s: Short file\n", name);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
if (write (seqfd, patch, sizeof (*patch) + sample.len) == -1)
|
||||
{
|
||||
perror ("/dev/pmgr0");
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
offset = offset + sample.len;
|
||||
}
|
||||
|
||||
loadmap[pgm] = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
struct patmgr_info inf;
|
||||
int err, i, n;
|
||||
struct synth_info info;
|
||||
|
||||
if ((seqfd = open ("/dev/patmgr0", O_RDWR, 0)) == -1)
|
||||
{
|
||||
fprintf (stderr, "Cannot open\n");
|
||||
perror ("/dev/patmgr0");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
|
||||
{
|
||||
perror ("NRSYNTH: /dev/patmgr0");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
info.device = i;
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
|
||||
{
|
||||
perror ("SYNTH_INFO: /dev/patmgr0");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (info.synth_type == SYNTH_TYPE_SAMPLE
|
||||
&& info.synth_subtype == SAMPLE_TYPE_GUS)
|
||||
gus_dev = i;
|
||||
}
|
||||
|
||||
if (gus_dev == -1)
|
||||
{
|
||||
fprintf (stderr, "Error: Gravis Ultrasound not detected\n");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
|
||||
perror ("Sample reset");
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
loadmap[i] = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (read (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf))
|
||||
{
|
||||
perror ("Read");
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
if (inf.key == PM_K_EVENT)
|
||||
switch (inf.command)
|
||||
{
|
||||
case PM_E_OPENED:
|
||||
printf ("Opened\n");
|
||||
break;
|
||||
|
||||
case PM_E_CLOSED:
|
||||
printf ("Closed\n");
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
|
||||
perror ("Sample reset");
|
||||
for (i = 0; i < 256; i++)
|
||||
loadmap[i] = 0;
|
||||
break;
|
||||
|
||||
case PM_E_PATCH_RESET:
|
||||
printf ("Patch reset called\n");
|
||||
for (i = 0; i < 256; i++)
|
||||
loadmap[i] = 0;
|
||||
break;
|
||||
|
||||
case PM_E_PATCH_LOADED:
|
||||
printf ("Patch loaded by client\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("Unknown event %d\n", inf.command);
|
||||
inf.key = PM_ERROR;
|
||||
inf.parm1 = EINVAL;
|
||||
}
|
||||
else if (inf.key == PM_K_COMMAND)
|
||||
switch (inf.command)
|
||||
{
|
||||
case _PM_LOAD_PATCH:
|
||||
if ((err = do_load_patch (&inf)))
|
||||
if (err == ENOSPC)
|
||||
{
|
||||
if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1)
|
||||
{
|
||||
perror ("Sample reset");
|
||||
return errno;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
loadmap[i] = 0;
|
||||
err = do_load_patch (&inf);
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
inf.key = PM_ERROR;
|
||||
inf.parm1 = err;
|
||||
printf("Error = %d\n", err);
|
||||
}
|
||||
else
|
||||
{
|
||||
inf.key = PM_K_COMMAND;
|
||||
inf.parm1 = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("Unknown command %d\n", inf.command);
|
||||
inf.key = PM_ERROR;
|
||||
inf.parm1 = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Unknown event %d/%d\n", inf.key, inf.command);
|
||||
inf.key = PM_ERROR;
|
||||
inf.parm1 = EINVAL;
|
||||
}
|
||||
|
||||
if (write (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf))
|
||||
{
|
||||
perror ("write");
|
||||
exit (-1);
|
||||
}
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
17
sys/i386/isa/sound/local.h
Normal file
17
sys/i386/isa/sound/local.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* Generated by configure. Don't edit!!!! */
|
||||
|
||||
#define KERNEL_SOUNDCARD
|
||||
#undef EXCLUDE_PAS
|
||||
#undef EXCLUDE_SB
|
||||
#undef EXCLUDE_ADLIB
|
||||
#undef EXCLUDE_GUS
|
||||
#define EXCLUDE_MPU401 /* Not ready yet */
|
||||
#undef EXCLUDE_SBPRO
|
||||
#undef EXCLUDE_AUDIO
|
||||
#undef EXCLUDE_MIDI
|
||||
#undef EXCLUDE_YM3812
|
||||
#undef EXCLUDE_SEQUENCER
|
||||
#undef EXCLUDE_CHIP_MIDI
|
||||
|
||||
#define DSP_BUFFSIZE 32768
|
||||
#define SELECTED_SOUND_OPTIONS 0xffffffff
|
176
sys/i386/isa/sound/midi.c
Normal file
176
sys/i386/isa/sound/midi.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/* UWM - comments to soft-eng@cs.uwm.edu */
|
||||
#define _MIDI_TABLE_C_
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#ifndef EXCLUDE_CHIP_MIDI
|
||||
|
||||
|
||||
static int generic_midi_busy[MAX_MIDI_DEV];
|
||||
|
||||
long CMIDI_init (long mem_start)
|
||||
{
|
||||
|
||||
int i;
|
||||
int n = num_midi_drivers;
|
||||
/* int n = sizeof (midi_supported) / sizeof( struct generic_midi_info );
|
||||
*/
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if ( midi_supported[i].attach (mem_start) )
|
||||
{
|
||||
printk("MIDI: Successfully attached %s\n",midi_supported[i].name);
|
||||
}
|
||||
|
||||
}
|
||||
return (mem_start);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
CMIDI_open (int dev, struct fileinfo *file)
|
||||
{
|
||||
|
||||
int mode, err, retval;
|
||||
|
||||
dev = dev >> 4;
|
||||
|
||||
mode = file->mode & O_ACCMODE;
|
||||
|
||||
|
||||
if (generic_midi_busy[dev])
|
||||
return (RET_ERROR(EBUSY));
|
||||
|
||||
|
||||
if (dev >= num_generic_midis)
|
||||
{
|
||||
printk(" MIDI device %d not installed.\n", dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (!generic_midi_devs[dev])
|
||||
{
|
||||
printk(" MIDI device %d not initialized\n",dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* If all good and healthy, go ahead and issue call! */
|
||||
|
||||
|
||||
retval = generic_midi_devs[dev]->open (dev, mode) ;
|
||||
|
||||
/* If everything ok, set device as busy */
|
||||
|
||||
if ( retval >= 0 )
|
||||
generic_midi_busy[dev] = 1;
|
||||
|
||||
return ( retval );
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
|
||||
int retval;
|
||||
|
||||
dev = dev >> 4;
|
||||
|
||||
if (dev >= num_generic_midis)
|
||||
{
|
||||
printk(" MIDI device %d not installed.\n", dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Make double sure of healthiness -- doubt
|
||||
* Need we check this again??
|
||||
*
|
||||
*/
|
||||
|
||||
if (!generic_midi_devs[dev])
|
||||
{
|
||||
printk(" MIDI device %d not initialized\n",dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* If all good and healthy, go ahead and issue call! */
|
||||
|
||||
|
||||
retval = generic_midi_devs[dev]->write (dev, buf);
|
||||
|
||||
return ( retval );
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count)
|
||||
{
|
||||
int retval;
|
||||
|
||||
dev = dev >> 4;
|
||||
|
||||
if (dev >= num_generic_midis)
|
||||
{
|
||||
printk(" MIDI device %d not installed.\n", dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Make double sure of healthiness -- doubt
|
||||
* Need we check this again??
|
||||
*
|
||||
*/
|
||||
|
||||
if (!generic_midi_devs[dev])
|
||||
{
|
||||
printk(" MIDI device %d not initialized\n",dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* If all good and healthy, go ahead and issue call! */
|
||||
|
||||
|
||||
retval = generic_midi_devs[dev]->read(dev,buf);
|
||||
|
||||
return (retval);
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
CMIDI_close (int dev, struct fileinfo *file)
|
||||
{
|
||||
|
||||
int retval;
|
||||
dev = dev >> 4;
|
||||
|
||||
if (dev >= num_generic_midis)
|
||||
{
|
||||
printk(" MIDI device %d not installed.\n", dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Make double sure of healthiness -- doubt
|
||||
* Need we check this again??
|
||||
*
|
||||
*/
|
||||
|
||||
if (!generic_midi_devs[dev])
|
||||
{
|
||||
printk(" MIDI device %d not initialized\n",dev);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* If all good and healthy, go ahead and issue call! */
|
||||
|
||||
|
||||
generic_midi_devs[dev]->close(dev);
|
||||
|
||||
generic_midi_busy[dev] = 0; /* Free the device */
|
||||
|
||||
return (0) ;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
105
sys/i386/isa/sound/midibuf.c
Normal file
105
sys/i386/isa/sound/midibuf.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/midibuf.c
|
||||
*
|
||||
* Device file manager for /dev/midi
|
||||
*
|
||||
* NOTE! This part of the driver is currently just a stub.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*
|
||||
* Based on the Midi driver for bsd386 by Mike Durian.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MPU401)
|
||||
|
||||
#if 0
|
||||
#include "midiioctl.h"
|
||||
#include "midivar.h"
|
||||
#endif
|
||||
|
||||
static int midibuf_busy = 0;
|
||||
|
||||
int
|
||||
MIDIbuf_open (int dev, struct fileinfo *file)
|
||||
{
|
||||
int mode, err;
|
||||
|
||||
dev = dev >> 4;
|
||||
mode = file->mode & O_ACCMODE;
|
||||
|
||||
if (midibuf_busy)
|
||||
return RET_ERROR (EBUSY);
|
||||
|
||||
if (!mpu401_dev)
|
||||
{
|
||||
printk ("Midi: MPU-401 compatible Midi interface not present\n");
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
if ((err = midi_devs[mpu401_dev]->open (mpu401_dev, mode)) < 0)
|
||||
return err;
|
||||
|
||||
midibuf_busy = 1;
|
||||
|
||||
return RET_ERROR (ENXIO);
|
||||
}
|
||||
|
||||
void
|
||||
MIDIbuf_release (int dev, struct fileinfo *file)
|
||||
{
|
||||
int mode;
|
||||
|
||||
dev = dev >> 4;
|
||||
mode = file->mode & O_ACCMODE;
|
||||
|
||||
midi_devs[mpu401_dev]->close (mpu401_dev);
|
||||
midibuf_busy = 0;
|
||||
}
|
||||
|
||||
int
|
||||
MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
|
||||
dev = dev >> 4;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
dev = dev >> 4;
|
||||
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
int
|
||||
MIDIbuf_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg)
|
||||
{
|
||||
dev = dev >> 4;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
|
||||
default:
|
||||
return midi_devs[0]->ioctl (dev, cmd, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MIDIbuf_bytes_received (int dev, unsigned char *buf, int count)
|
||||
{
|
||||
}
|
||||
|
||||
long
|
||||
MIDIbuf_init (long mem_start)
|
||||
{
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
#endif
|
332
sys/i386/isa/sound/mpu401.c
Normal file
332
sys/i386/isa/sound/mpu401.c
Normal file
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/mpu401.c
|
||||
*
|
||||
* The low level driver for Roland MPU-401 compatible Midi cards.
|
||||
*
|
||||
* This version supports just the DUMB UART mode.
|
||||
*
|
||||
* (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
|
||||
|
||||
#define DATAPORT (mpu401_base)/* MPU-401 Data I/O Port on IBM */
|
||||
#define COMDPORT (mpu401_base+1) /* MPU-401 Command Port on IBM */
|
||||
#define STATPORT (mpu401_base+1) /* MPU-401 Status Port on IBM */
|
||||
|
||||
#define mpu401_status() INB(STATPORT)
|
||||
#define input_avail() (!(mpu401_status()&INPUT_AVAIL))
|
||||
#define output_ready() (!(mpu401_status()&OUTPUT_READY))
|
||||
#define mpu401_cmd(cmd) OUTB(cmd, COMDPORT)
|
||||
#define mpu401_read() INB(DATAPORT)
|
||||
#define mpu401_write(byte) OUTB(byte, DATAPORT)
|
||||
|
||||
#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */
|
||||
#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */
|
||||
#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */
|
||||
#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */
|
||||
#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */
|
||||
|
||||
static int mpu401_opened = 0;
|
||||
static int mpu401_base = 0x330;
|
||||
static int mpu401_irq;
|
||||
static int mpu401_detected = 0;
|
||||
static int my_dev;
|
||||
|
||||
static int reset_mpu401 (void);
|
||||
|
||||
static void
|
||||
mpu401_input_loop (void)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = 10;
|
||||
|
||||
while (count) /* Not timed out */
|
||||
if (input_avail ())
|
||||
{
|
||||
unsigned char c = mpu401_read ();
|
||||
|
||||
count = 100;
|
||||
|
||||
if (mpu401_opened & OPEN_READ)
|
||||
sequencer_midi_input (my_dev, c);
|
||||
}
|
||||
else
|
||||
while (!input_avail () && count)
|
||||
count--;
|
||||
}
|
||||
|
||||
void
|
||||
mpuintr (int unit)
|
||||
{
|
||||
unsigned char c;
|
||||
|
||||
if (input_avail ())
|
||||
mpu401_input_loop ();
|
||||
}
|
||||
|
||||
/*
|
||||
* It looks like there is no input interrupts in the UART mode. Let's try
|
||||
* polling.
|
||||
*/
|
||||
|
||||
static void
|
||||
poll_mpu401 (unsigned long dummy)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
static struct timer_list mpu401_timer =
|
||||
{NULL, 0, 0, poll_mpu401};
|
||||
|
||||
if (!(mpu401_opened & OPEN_READ))
|
||||
return; /* No longer required */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (input_avail ())
|
||||
mpu401_input_loop ();
|
||||
|
||||
mpu401_timer.expires = 1;
|
||||
add_timer (&mpu401_timer); /* Come back later */
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
static int
|
||||
set_mpu401_irq (int interrupt_level)
|
||||
{
|
||||
int retcode;
|
||||
|
||||
#ifdef linux
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = mpuintr;
|
||||
|
||||
#ifdef SND_SA_INTERRUPT
|
||||
sa.sa_flags = SA_INTERRUPT;
|
||||
#else
|
||||
sa.sa_flags = 0;
|
||||
#endif
|
||||
|
||||
sa.sa_mask = 0;
|
||||
sa.sa_restorer = NULL;
|
||||
|
||||
retcode = irqaction (interrupt_level, &sa);
|
||||
|
||||
if (retcode < 0)
|
||||
{
|
||||
printk ("MPU-401: IRQ%d already in use\n", interrupt_level);
|
||||
}
|
||||
|
||||
#else
|
||||
/* # error Unimplemented for this OS */
|
||||
#endif
|
||||
return retcode;
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_open (int dev, int mode)
|
||||
{
|
||||
if (mpu401_opened)
|
||||
{
|
||||
printk ("MPU-401: Midi busy\n");
|
||||
return RET_ERROR (EBUSY);
|
||||
}
|
||||
|
||||
mpu401_input_loop ();
|
||||
|
||||
mpu401_opened = mode;
|
||||
poll_mpu401 (0); /* Enable input polling */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mpu401_close (int dev)
|
||||
{
|
||||
mpu401_opened = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_out (int dev, unsigned char midi_byte)
|
||||
{
|
||||
int timeout;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Test for input since pending input seems to block the output.
|
||||
*/
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (input_avail ())
|
||||
mpu401_input_loop ();
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
/*
|
||||
* Sometimes it takes about 13000 loops before the output becomes ready
|
||||
* (After reset). Normally it takes just about 10 loops.
|
||||
*/
|
||||
|
||||
for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */
|
||||
|
||||
if (!output_ready ())
|
||||
{
|
||||
printk ("MPU-401: Timeout\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mpu401_write (midi_byte);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_command (int dev, unsigned char midi_byte)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_start_read (int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_end_read (int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_ioctl (int dev, unsigned cmd, unsigned arg)
|
||||
{
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
static void
|
||||
mpu401_kick (int dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_buffer_status (int dev)
|
||||
{
|
||||
return 0; /* No data in buffers */
|
||||
}
|
||||
|
||||
static struct midi_operations mpu401_operations =
|
||||
{
|
||||
{"MPU-401", 0},
|
||||
mpu401_open,
|
||||
mpu401_close,
|
||||
mpu401_ioctl,
|
||||
mpu401_out,
|
||||
mpu401_start_read,
|
||||
mpu401_end_read,
|
||||
mpu401_kick,
|
||||
mpu401_command,
|
||||
mpu401_buffer_status
|
||||
};
|
||||
|
||||
|
||||
long
|
||||
attach_mpu401 (long mem_start, struct address_info *hw_config)
|
||||
{
|
||||
int ok, timeout;
|
||||
unsigned long flags;
|
||||
|
||||
mpu401_base = hw_config->io_base;
|
||||
mpu401_irq = hw_config->irq;
|
||||
|
||||
if (!mpu401_detected)
|
||||
return RET_ERROR (EIO);
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
|
||||
mpu401_cmd (UART_MODE_ON);
|
||||
|
||||
ok = 0;
|
||||
for (timeout = 50000; timeout > 0 && !ok; timeout--)
|
||||
if (input_avail ())
|
||||
if (mpu401_read () == MPU_ACK)
|
||||
ok = 1;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
printk (" <Roland MPU-401>");
|
||||
|
||||
my_dev = num_midis;
|
||||
mpu401_dev = num_midis;
|
||||
midi_devs[num_midis++] = &mpu401_operations;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
static int
|
||||
reset_mpu401 (void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ok, timeout, n;
|
||||
|
||||
/*
|
||||
* Send the RESET command. Try twice if no success at the first time.
|
||||
*/
|
||||
|
||||
ok = 0;
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
for (n = 0; n < 2 && !ok; n++)
|
||||
{
|
||||
for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
|
||||
mpu401_cmd (MPU_RESET); /* Send MPU-401 RESET Command */
|
||||
|
||||
/*
|
||||
* Wait at least 25 msec. This method is not accurate so let's make the
|
||||
* loop bit longer. Cannot sleep since this is called during boot.
|
||||
*/
|
||||
|
||||
for (timeout = 50000; timeout > 0 && !ok; timeout--)
|
||||
if (input_avail ())
|
||||
if (mpu401_read () == MPU_ACK)
|
||||
ok = 1;
|
||||
|
||||
}
|
||||
|
||||
mpu401_opened = 0;
|
||||
if (ok)
|
||||
mpu401_input_loop (); /* Flush input before enabling interrupts */
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
probe_mpu401 (struct address_info *hw_config)
|
||||
{
|
||||
int ok = 0;
|
||||
|
||||
mpu401_base = hw_config->io_base;
|
||||
mpu401_irq = hw_config->irq;
|
||||
|
||||
if (set_mpu401_irq (mpu401_irq) < 0)
|
||||
return 0;
|
||||
|
||||
ok = reset_mpu401 ();
|
||||
|
||||
mpu401_detected = ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
913
sys/i386/isa/sound/opl3.c
Normal file
913
sys/i386/isa/sound/opl3.c
Normal file
|
@ -0,0 +1,913 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/opl3.c
|
||||
*
|
||||
* A low level driver for Yamaha YM3812 and OPL-3 -chips
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
/* Major improvements to the FM handling 30AUG92 by Rob Hooft, */
|
||||
/* hooft@chem.ruu.nl */
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
|
||||
|
||||
#include "opl3.h"
|
||||
|
||||
#define MAX_VOICE 18
|
||||
#define OFFS_4OP 11 /* Definitions for the operators OP3 and OP4
|
||||
* begin here */
|
||||
|
||||
static int opl3_enabled = 0;
|
||||
static int left_address = 0x388, right_address = 0x388, both_address = 0;
|
||||
|
||||
static int nr_voices = 9;
|
||||
static int logical_voices[MAX_VOICE] =
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
|
||||
|
||||
struct voice_info
|
||||
{
|
||||
unsigned char keyon_byte;
|
||||
long bender;
|
||||
long bender_range;
|
||||
unsigned long orig_freq;
|
||||
unsigned long current_freq;
|
||||
int mode;
|
||||
};
|
||||
|
||||
static struct voice_info voices[MAX_VOICE];
|
||||
|
||||
typedef struct sbi_instrument instr_array[SBFM_MAXINSTR];
|
||||
static instr_array instrmap;
|
||||
static struct sbi_instrument *active_instrument[MAX_VOICE] =
|
||||
{NULL};
|
||||
|
||||
static struct synth_info fm_info =
|
||||
{"AdLib", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0};
|
||||
|
||||
static int already_initialized = 0;
|
||||
|
||||
static int opl3_ok = 0;
|
||||
static int opl3_busy = 0;
|
||||
static int fm_model = 0; /* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2 */
|
||||
|
||||
static int store_instr (int instr_no, struct sbi_instrument *instr);
|
||||
static void freq_to_fnum (int freq, int *block, int *fnum);
|
||||
static void opl3_command (int io_addr, const unsigned char addr, const unsigned char val);
|
||||
static int opl3_kill_note (int dev, int voice, int velocity);
|
||||
static unsigned char connection_mask = 0x00;
|
||||
|
||||
void
|
||||
enable_opl3_mode (int left, int right, int both)
|
||||
{
|
||||
opl3_enabled = 1;
|
||||
left_address = left;
|
||||
right_address = right;
|
||||
both_address = both;
|
||||
fm_info.capabilities = SYNTH_CAP_OPL3;
|
||||
fm_info.synth_subtype = FM_TYPE_OPL3;
|
||||
}
|
||||
|
||||
static void
|
||||
enter_4op_mode (void)
|
||||
{
|
||||
int i;
|
||||
static int voices_4op[MAX_VOICE] =
|
||||
{0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};
|
||||
|
||||
connection_mask = 0x3f;
|
||||
opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f); /* Select all 4-OP
|
||||
* voices */
|
||||
for (i = 0; i < 3; i++)
|
||||
physical_voices[i].voice_mode = 4;
|
||||
for (i = 3; i < 6; i++)
|
||||
physical_voices[i].voice_mode = 0;
|
||||
|
||||
for (i = 9; i < 12; i++)
|
||||
physical_voices[i].voice_mode = 4;
|
||||
for (i = 12; i < 15; i++)
|
||||
physical_voices[i].voice_mode = 0;
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
logical_voices[i] = voices_4op[i];
|
||||
nr_voices = 6;
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_ioctl (int dev,
|
||||
unsigned int cmd, unsigned int arg)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
|
||||
case SNDCTL_FM_LOAD_INSTR:
|
||||
{
|
||||
struct sbi_instrument ins;
|
||||
|
||||
IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins));
|
||||
|
||||
if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
|
||||
{
|
||||
printk ("FM Error: Invalid instrument number %d\n", ins.channel);
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0);
|
||||
return store_instr (ins.channel, &ins);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDCTL_SYNTH_INFO:
|
||||
fm_info.nr_voices = nr_voices;
|
||||
|
||||
IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info));
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SNDCTL_SYNTH_MEMAVL:
|
||||
return 0x7fffffff;
|
||||
break;
|
||||
|
||||
case SNDCTL_FM_4OP_ENABLE:
|
||||
if (opl3_enabled)
|
||||
enter_4op_mode ();
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
opl3_detect (int ioaddr)
|
||||
{
|
||||
/*
|
||||
* This function returns 1 if the FM chicp is present at the given I/O port
|
||||
* The detection algorithm plays with the timer built in the FM chip and
|
||||
* looks for a change in the status register.
|
||||
*
|
||||
* Note! The timers of the FM chip are not connected to AdLib (and compatible)
|
||||
* boards.
|
||||
*
|
||||
* Note2! The chip is initialized if detected.
|
||||
*/
|
||||
|
||||
unsigned char stat1, stat2;
|
||||
int i;
|
||||
|
||||
if (already_initialized)
|
||||
{
|
||||
return 0; /* Do avoid duplicate initializations */
|
||||
}
|
||||
|
||||
opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */
|
||||
opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM
|
||||
* chicp */
|
||||
|
||||
stat1 = INB (ioaddr); /* Read status register */
|
||||
|
||||
if ((stat1 & 0xE0) != 0x00)
|
||||
{
|
||||
return 0; /* Should be 0x00 */
|
||||
}
|
||||
|
||||
opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer 1 to 0xff */
|
||||
opl3_command (ioaddr, TIMER_CONTROL_REGISTER,
|
||||
TIMER2_MASK | TIMER1_START); /* Unmask and start timer 1 */
|
||||
|
||||
/*
|
||||
* Now we have to delay at least 80 msec
|
||||
*/
|
||||
|
||||
for (i = 0; i < 50; i++)
|
||||
tenmicrosec (); /* To be sure */
|
||||
|
||||
stat2 = INB (ioaddr); /* Read status after timers have expired */
|
||||
|
||||
/* Stop the timers */
|
||||
|
||||
opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */
|
||||
opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM
|
||||
* chicp */
|
||||
|
||||
if ((stat2 & 0xE0) != 0xc0)
|
||||
{
|
||||
return 0; /* There is no YM3812 */
|
||||
}
|
||||
|
||||
/* There is a FM chicp in this address. Now set some default values. */
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
opl3_command (ioaddr, KEYON_BLOCK + i, 0); /* Note off */
|
||||
|
||||
opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
|
||||
opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /* Melodic mode. */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_kill_note (int dev, int voice, int velocity)
|
||||
{
|
||||
struct physical_voice_info *map;
|
||||
|
||||
if (voice < 0 || voice >= nr_voices)
|
||||
return 0;
|
||||
|
||||
map = &physical_voices[logical_voices[voice]];
|
||||
|
||||
DEB (printk ("Kill note %d\n", voice));
|
||||
|
||||
if (map->voice_mode == 0)
|
||||
return 0;
|
||||
|
||||
opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20);
|
||||
|
||||
voices[voice].keyon_byte = 0;
|
||||
voices[voice].bender = 0;
|
||||
voices[voice].bender_range = 200; /* 200 cents = 2 semitones */
|
||||
voices[voice].orig_freq = 0;
|
||||
voices[voice].current_freq = 0;
|
||||
voices[voice].mode = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HIHAT 0
|
||||
#define CYMBAL 1
|
||||
#define TOMTOM 2
|
||||
#define SNARE 3
|
||||
#define BDRUM 4
|
||||
#define UNDEFINED TOMTOM
|
||||
#define DEFAULT TOMTOM
|
||||
|
||||
static int
|
||||
store_instr (int instr_no, struct sbi_instrument *instr)
|
||||
{
|
||||
|
||||
if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled))
|
||||
printk ("FM warning: Invalid patch format field (key) 0x%04x\n", instr->key);
|
||||
memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_set_instr (int dev, int voice, int instr_no)
|
||||
{
|
||||
if (voice < 0 || voice >= nr_voices)
|
||||
return 0;
|
||||
|
||||
if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
|
||||
return 0;
|
||||
|
||||
active_instrument[voice] = &instrmap[instr_no];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The next table looks magical, but it certainly is not. Its values have
|
||||
* been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
|
||||
* for i=0. This log-table converts a linear volume-scaling (0..127) to a
|
||||
* logarithmic scaling as present in the FM-synthesizer chips. so : Volume
|
||||
* 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
|
||||
* volume -8 it was implemented as a table because it is only 128 bytes and
|
||||
* it saves a lot of log() calculations. (RH)
|
||||
*/
|
||||
char fm_volume_table[128] =
|
||||
{-64, -48, -40, -35, -32, -29, -27, -26, /* 0 - 7 */
|
||||
-24, -23, -21, -20, -19, -18, -18, -17, /* 8 - 15 */
|
||||
-16, -15, -15, -14, -13, -13, -12, -12, /* 16 - 23 */
|
||||
-11, -11, -10, -10, -10, -9, -9, -8, /* 24 - 31 */
|
||||
-8, -8, -7, -7, -7, -6, -6, -6,/* 32 - 39 */
|
||||
-5, -5, -5, -5, -4, -4, -4, -4,/* 40 - 47 */
|
||||
-3, -3, -3, -3, -2, -2, -2, -2,/* 48 - 55 */
|
||||
-2, -1, -1, -1, -1, 0, 0, 0, /* 56 - 63 */
|
||||
0, 0, 0, 1, 1, 1, 1, 1, /* 64 - 71 */
|
||||
1, 2, 2, 2, 2, 2, 2, 2, /* 72 - 79 */
|
||||
3, 3, 3, 3, 3, 3, 3, 4, /* 80 - 87 */
|
||||
4, 4, 4, 4, 4, 4, 4, 5, /* 88 - 95 */
|
||||
5, 5, 5, 5, 5, 5, 5, 5, /* 96 - 103 */
|
||||
6, 6, 6, 6, 6, 6, 6, 6, /* 104 - 111 */
|
||||
6, 7, 7, 7, 7, 7, 7, 7, /* 112 - 119 */
|
||||
7, 7, 7, 8, 8, 8, 8, 8}; /* 120 - 127 */
|
||||
|
||||
static void
|
||||
calc_vol (unsigned char *regbyte, int volume)
|
||||
{
|
||||
int level = (~*regbyte & 0x3f);
|
||||
|
||||
if (level)
|
||||
level += fm_volume_table[volume];
|
||||
|
||||
if (level > 0x3f)
|
||||
level = 0x3f;
|
||||
if (level < 0)
|
||||
level = 0;
|
||||
|
||||
*regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
|
||||
}
|
||||
|
||||
static void
|
||||
set_voice_volume (int voice, int volume)
|
||||
{
|
||||
unsigned char vol1, vol2, vol3, vol4;
|
||||
struct sbi_instrument *instr;
|
||||
struct physical_voice_info *map;
|
||||
|
||||
if (voice < 0 || voice >= nr_voices)
|
||||
return;
|
||||
|
||||
map = &physical_voices[logical_voices[voice]];
|
||||
|
||||
instr = active_instrument[voice];
|
||||
|
||||
if (!instr)
|
||||
instr = &instrmap[0];
|
||||
|
||||
if (instr->channel < 0)
|
||||
return;
|
||||
|
||||
if (voices[voice].mode == 0)
|
||||
return;
|
||||
|
||||
if (voices[voice].mode == 2)
|
||||
{ /* 2 OP voice */
|
||||
|
||||
vol1 = instr->operators[2];
|
||||
vol2 = instr->operators[3];
|
||||
|
||||
if ((instr->operators[10] & 0x01))
|
||||
{ /* Additive synthesis */
|
||||
calc_vol (&vol1, volume);
|
||||
calc_vol (&vol2, volume);
|
||||
}
|
||||
else
|
||||
{ /* FM synthesis */
|
||||
calc_vol (&vol2, volume);
|
||||
}
|
||||
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /* Modulator volume */
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /* Carrier volume */
|
||||
}
|
||||
else
|
||||
{ /* 4 OP voice */
|
||||
int connection;
|
||||
|
||||
vol1 = instr->operators[2];
|
||||
vol2 = instr->operators[3];
|
||||
vol3 = instr->operators[OFFS_4OP + 2];
|
||||
vol4 = instr->operators[OFFS_4OP + 3];
|
||||
|
||||
/*
|
||||
* The connection method for 4 OP voices is defined by the rightmost
|
||||
* bits at the offsets 10 and 10+OFFS_4OP
|
||||
*/
|
||||
|
||||
connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
|
||||
|
||||
switch (connection)
|
||||
{
|
||||
case 0:
|
||||
calc_vol (&vol4, volume); /* Just the OP 4 is carrier */
|
||||
break;
|
||||
|
||||
case 1:
|
||||
calc_vol (&vol2, volume);
|
||||
calc_vol (&vol4, volume);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
calc_vol (&vol1, volume);
|
||||
calc_vol (&vol4, volume);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
calc_vol (&vol1, volume);
|
||||
calc_vol (&vol3, volume);
|
||||
calc_vol (&vol4, volume);
|
||||
break;
|
||||
|
||||
default:/* Why ?? */ ;
|
||||
}
|
||||
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_start_note (int dev, int voice, int note, int volume)
|
||||
{
|
||||
unsigned char data;
|
||||
int block, fnum, freq, voice_mode;
|
||||
struct sbi_instrument *instr;
|
||||
struct physical_voice_info *map;
|
||||
|
||||
if (voice < 0 || voice >= nr_voices)
|
||||
return 0;
|
||||
|
||||
map = &physical_voices[logical_voices[voice]];
|
||||
|
||||
if (map->voice_mode == 0)
|
||||
return 0;
|
||||
|
||||
if (note == 255) /* Just change the volume */
|
||||
{
|
||||
set_voice_volume (voice, volume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Kill previous note before playing */
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* Carrier volume to min */
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* Modulator volume to */
|
||||
|
||||
if (map->voice_mode == 4)
|
||||
{
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
|
||||
opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
|
||||
}
|
||||
|
||||
opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* Note off */
|
||||
|
||||
instr = active_instrument[voice];
|
||||
|
||||
if (!instr)
|
||||
instr = &instrmap[0];
|
||||
|
||||
if (instr->channel < 0)
|
||||
{
|
||||
printk (
|
||||
"OPL3: Initializing voice %d with undefined instrument\n",
|
||||
voice);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
|
||||
return 0; /* Cannot play */
|
||||
|
||||
voice_mode = map->voice_mode;
|
||||
|
||||
if (voice_mode == 4)
|
||||
{
|
||||
int voice_shift;
|
||||
|
||||
voice_shift = (map->ioaddr == left_address) ? 0 : 3;
|
||||
voice_shift += map->voice_num;
|
||||
|
||||
if (instr->key != OPL3_PATCH) /* Just 2 OP patch */
|
||||
{
|
||||
voice_mode = 2;
|
||||
connection_mask &= ~(1 << voice_shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection_mask |= (1 << voice_shift);
|
||||
}
|
||||
|
||||
opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
|
||||
}
|
||||
|
||||
/* Set Sound Characteristics */
|
||||
opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
|
||||
opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
|
||||
|
||||
/* Set Attack/Decay */
|
||||
opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
|
||||
opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
|
||||
|
||||
/* Set Sustain/Release */
|
||||
opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
|
||||
opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
|
||||
|
||||
/* Set Wave Select */
|
||||
opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
|
||||
opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
|
||||
|
||||
/* Set Feedback/Connection */
|
||||
/* Connect the voice to both stereo channels */
|
||||
opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, instr->operators[10] | 0x30);
|
||||
|
||||
/*
|
||||
* If the voice is a 4 OP one, initialize the operators 3 and 4 also
|
||||
*/
|
||||
|
||||
if (voice_mode == 4)
|
||||
{
|
||||
|
||||
/* Set Sound Characteristics */
|
||||
opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
|
||||
opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
|
||||
|
||||
/* Set Attack/Decay */
|
||||
opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
|
||||
opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
|
||||
|
||||
/* Set Sustain/Release */
|
||||
opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
|
||||
opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
|
||||
|
||||
/* Set Wave Select */
|
||||
opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
|
||||
opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
|
||||
|
||||
/* Set Feedback/Connection */
|
||||
/* Connect the voice to both stereo channels */
|
||||
opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, instr->operators[OFFS_4OP + 10] | 0x30);
|
||||
}
|
||||
|
||||
voices[voice].mode = voice_mode;
|
||||
|
||||
set_voice_volume (voice, volume);
|
||||
|
||||
freq = voices[voice].orig_freq = note_to_freq (note) / 1000;
|
||||
|
||||
/*
|
||||
* Since the pitch bender may have been set before playing the note, we
|
||||
* have to calculate the bending now.
|
||||
*/
|
||||
|
||||
freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
|
||||
voices[voice].current_freq = freq;
|
||||
|
||||
freq_to_fnum (freq, &block, &fnum);
|
||||
|
||||
/* Play note */
|
||||
|
||||
data = fnum & 0xff; /* Least significant bits of fnumber */
|
||||
opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
|
||||
|
||||
data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
|
||||
voices[voice].keyon_byte = data;
|
||||
opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
|
||||
if (voice_mode == 4)
|
||||
opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
freq_to_fnum (int freq, int *block, int *fnum)
|
||||
{
|
||||
int f, octave;
|
||||
|
||||
/* Converts the note frequency to block and fnum values for the FM chip */
|
||||
/* First try to compute the block -value (octave) where the note belongs */
|
||||
|
||||
f = freq;
|
||||
|
||||
octave = 5;
|
||||
|
||||
if (f == 0)
|
||||
octave = 0;
|
||||
else if (f < 261)
|
||||
{
|
||||
while (f < 261)
|
||||
{
|
||||
octave--;
|
||||
f <<= 1;
|
||||
}
|
||||
}
|
||||
else if (f > 493)
|
||||
{
|
||||
while (f > 493)
|
||||
{
|
||||
octave++;
|
||||
f >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (octave > 7)
|
||||
octave = 7;
|
||||
|
||||
*fnum = freq * (1 << (20 - octave)) / 49716;
|
||||
*block = octave;
|
||||
}
|
||||
|
||||
static void
|
||||
opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The original 2-OP synth requires a quite long delay after writing to a
|
||||
* register. The OPL-3 survives with just two INBs
|
||||
*/
|
||||
|
||||
OUTB (addr, io_addr); /* Select register */
|
||||
|
||||
if (!opl3_enabled)
|
||||
tenmicrosec ();
|
||||
else
|
||||
for (i = 0; i < 2; i++)
|
||||
INB (io_addr);
|
||||
|
||||
OUTB (val, io_addr + 1); /* Write to register */
|
||||
|
||||
if (!opl3_enabled)
|
||||
{
|
||||
tenmicrosec ();
|
||||
tenmicrosec ();
|
||||
tenmicrosec ();
|
||||
}
|
||||
else
|
||||
for (i = 0; i < 2; i++)
|
||||
INB (io_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
opl3_reset (int dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_voices; i++)
|
||||
{
|
||||
opl3_command (physical_voices[logical_voices[i]].ioaddr,
|
||||
KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff); /* OP1 volume to min */
|
||||
|
||||
opl3_command (physical_voices[logical_voices[i]].ioaddr,
|
||||
KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); /* OP2 volume to min */
|
||||
|
||||
if (physical_voices[logical_voices[i]].voice_mode == 4) /* 4 OP voice */
|
||||
{
|
||||
opl3_command (physical_voices[logical_voices[i]].ioaddr,
|
||||
KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); /* OP3 volume to min */
|
||||
|
||||
opl3_command (physical_voices[logical_voices[i]].ioaddr,
|
||||
KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); /* OP4 volume to min */
|
||||
}
|
||||
|
||||
opl3_kill_note (dev, i, 64);
|
||||
}
|
||||
|
||||
if (opl3_enabled)
|
||||
{
|
||||
nr_voices = 18;
|
||||
|
||||
for (i = 0; i < 18; i++)
|
||||
logical_voices[i] = i;
|
||||
|
||||
for (i = 0; i < 18; i++)
|
||||
physical_voices[i].voice_mode = 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_open (int dev, int mode)
|
||||
{
|
||||
if (!opl3_ok)
|
||||
return RET_ERROR (ENXIO);
|
||||
if (opl3_busy)
|
||||
return RET_ERROR (EBUSY);
|
||||
opl3_busy = 1;
|
||||
|
||||
connection_mask = 0x00; /* Just 2 OP voices */
|
||||
if (opl3_enabled)
|
||||
opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
opl3_close (int dev)
|
||||
{
|
||||
opl3_busy = 0;
|
||||
nr_voices = opl3_enabled ? 18 : 9;
|
||||
fm_info.nr_drums = 0;
|
||||
fm_info.perc_mode = 0;
|
||||
|
||||
opl3_reset (dev);
|
||||
}
|
||||
|
||||
static void
|
||||
opl3_hw_control (int dev, unsigned char *event)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_load_patch (int dev, int format, snd_rw_buf * addr,
|
||||
int offs, int count, int pmgr_flag)
|
||||
{
|
||||
struct sbi_instrument ins;
|
||||
|
||||
if (count < sizeof (ins))
|
||||
{
|
||||
printk ("FM Error: Patch record too short\n");
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs);
|
||||
|
||||
if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
|
||||
{
|
||||
printk ("FM Error: Invalid instrument number %d\n", ins.channel);
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
ins.key = format;
|
||||
|
||||
return store_instr (ins.channel, &ins);
|
||||
}
|
||||
|
||||
static void
|
||||
opl3_panning (int dev, int voice, int pressure)
|
||||
{
|
||||
}
|
||||
|
||||
#define SET_VIBRATO(cell) { \
|
||||
tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
|
||||
if (pressure > 110) \
|
||||
tmp |= 0x40; /* Vibrato on */ \
|
||||
opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
|
||||
|
||||
static void
|
||||
opl3_aftertouch (int dev, int voice, int pressure)
|
||||
{
|
||||
int tmp;
|
||||
struct sbi_instrument *instr;
|
||||
struct physical_voice_info *map;
|
||||
|
||||
if (voice < 0 || voice >= nr_voices)
|
||||
return;
|
||||
|
||||
map = &physical_voices[logical_voices[voice]];
|
||||
|
||||
DEB (printk ("Aftertouch %d\n", voice));
|
||||
|
||||
if (map->voice_mode == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Adjust the amount of vibrato depending the pressure
|
||||
*/
|
||||
|
||||
instr = active_instrument[voice];
|
||||
|
||||
if (!instr)
|
||||
instr = &instrmap[0];
|
||||
|
||||
if (voices[voice].mode == 4)
|
||||
{
|
||||
int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
|
||||
|
||||
switch (connection)
|
||||
{
|
||||
case 0:
|
||||
SET_VIBRATO (4);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SET_VIBRATO (2);
|
||||
SET_VIBRATO (4);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
SET_VIBRATO (1);
|
||||
SET_VIBRATO (4);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
SET_VIBRATO (1);
|
||||
SET_VIBRATO (3);
|
||||
SET_VIBRATO (4);
|
||||
break;
|
||||
|
||||
}
|
||||
/* Not implemented yet */
|
||||
}
|
||||
else
|
||||
{
|
||||
SET_VIBRATO (1);
|
||||
|
||||
if ((instr->operators[10] & 0x01)) /* Additive synthesis */
|
||||
SET_VIBRATO (2);
|
||||
}
|
||||
}
|
||||
|
||||
#undef SET_VIBRATO
|
||||
|
||||
static void
|
||||
opl3_controller (int dev, int voice, int ctrl_num, int value)
|
||||
{
|
||||
unsigned char data;
|
||||
int block, fnum, freq;
|
||||
struct physical_voice_info *map;
|
||||
|
||||
if (voice < 0 || voice >= nr_voices)
|
||||
return;
|
||||
|
||||
map = &physical_voices[logical_voices[voice]];
|
||||
|
||||
if (map->voice_mode == 0)
|
||||
return;
|
||||
|
||||
switch (ctrl_num)
|
||||
{
|
||||
case CTRL_PITCH_BENDER:
|
||||
voices[voice].bender = value;
|
||||
if (!value)
|
||||
return;
|
||||
if (!(voices[voice].keyon_byte & 0x20))
|
||||
return; /* Not keyed on */
|
||||
|
||||
freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
|
||||
voices[voice].current_freq = freq;
|
||||
|
||||
freq_to_fnum (freq, &block, &fnum);
|
||||
|
||||
data = fnum & 0xff; /* Least significant bits of fnumber */
|
||||
opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
|
||||
|
||||
data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* KEYON|OCTAVE|MS bits
|
||||
* of f-num */
|
||||
voices[voice].keyon_byte = data;
|
||||
opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
|
||||
break;
|
||||
|
||||
case CTRL_PITCH_BENDER_RANGE:
|
||||
voices[voice].bender_range = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
opl3_patchmgr (int dev, struct patmgr_info *rec)
|
||||
{
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
static struct synth_operations opl3_operations =
|
||||
{
|
||||
&fm_info,
|
||||
SYNTH_TYPE_FM,
|
||||
FM_TYPE_ADLIB,
|
||||
opl3_open,
|
||||
opl3_close,
|
||||
opl3_ioctl,
|
||||
opl3_kill_note,
|
||||
opl3_start_note,
|
||||
opl3_set_instr,
|
||||
opl3_reset,
|
||||
opl3_hw_control,
|
||||
opl3_load_patch,
|
||||
opl3_aftertouch,
|
||||
opl3_controller,
|
||||
opl3_panning,
|
||||
opl3_patchmgr
|
||||
};
|
||||
|
||||
long
|
||||
opl3_init (long mem_start)
|
||||
{
|
||||
int i;
|
||||
|
||||
synth_devs[num_synths++] = &opl3_operations;
|
||||
fm_model = 0;
|
||||
opl3_ok = 1;
|
||||
if (opl3_enabled)
|
||||
{
|
||||
printk (" <Yamaha OPL-3 FM>");
|
||||
fm_model = 2;
|
||||
nr_voices = 18;
|
||||
fm_info.nr_drums = 0;
|
||||
fm_info.capabilities |= SYNTH_CAP_OPL3;
|
||||
strcpy (fm_info.name, "Yamaha OPL-3");
|
||||
|
||||
for (i = 0; i < 18; i++)
|
||||
if (physical_voices[i].ioaddr == USE_LEFT)
|
||||
physical_voices[i].ioaddr = left_address;
|
||||
else
|
||||
physical_voices[i].ioaddr = right_address;
|
||||
|
||||
|
||||
opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /* Enable OPL-3 mode */
|
||||
opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /* Select all 2-OP
|
||||
* voices */
|
||||
}
|
||||
else
|
||||
{
|
||||
printk (" <Yamaha 2-OP FM>");
|
||||
fm_model = 1;
|
||||
nr_voices = 9;
|
||||
fm_info.nr_drums = 0;
|
||||
|
||||
for (i = 0; i < 18; i++)
|
||||
physical_voices[i].ioaddr = left_address;
|
||||
};
|
||||
|
||||
already_initialized = 1;
|
||||
for (i = 0; i < SBFM_MAXINSTR; i++)
|
||||
instrmap[i].channel = -1;
|
||||
|
||||
printk("\n");
|
||||
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
#endif
|
263
sys/i386/isa/sound/opl3.h
Normal file
263
sys/i386/isa/sound/opl3.h
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* opl3.h - Definitions of the OPL-3 registers
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright by Hannu Savolainen 1993
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The OPL-3 mode is switched on by writing 0x01, to the offset 5
|
||||
* of the right side.
|
||||
*
|
||||
* Another special register at the right side is at offset 4. It contains
|
||||
* a bit mask defining which voices are used as 4 OP voices.
|
||||
*
|
||||
* The percussive mode is implemented in the left side only.
|
||||
*
|
||||
* With the above exeptions the both sides can be operated independently.
|
||||
*
|
||||
* A 4 OP voice can be created by setting the corresponding
|
||||
* bit at offset 4 of the right side.
|
||||
*
|
||||
* For example setting the rightmost bit (0x01) changes the
|
||||
* first voice on the right side to the 4 OP mode. The fourth
|
||||
* voice is made inaccessible.
|
||||
*
|
||||
* If a voice is set to the 2 OP mode, it works like 2 OP modes
|
||||
* of the original YM3812 (AdLib). In addition the voice can
|
||||
* be connected the left, right or both stereo channels. It can
|
||||
* even be left unconnected. This works with 4 OP voices also.
|
||||
*
|
||||
* The stereo connection bits are located in the FEEDBACK_CONNECTION
|
||||
* register of the voice (0xC0-0xC8). In 4 OP voices these bits are
|
||||
* in the second half of the voice.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Register numbers for the global registers
|
||||
*/
|
||||
|
||||
#define TEST_REGISTER 0x01
|
||||
#define ENABLE_WAVE_SELECT 0x20
|
||||
|
||||
#define TIMER1_REGISTER 0x02
|
||||
#define TIMER2_REGISTER 0x03
|
||||
#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
|
||||
#define IRQ_RESET 0x80
|
||||
#define TIMER1_MASK 0x40
|
||||
#define TIMER2_MASK 0x20
|
||||
#define TIMER1_START 0x01
|
||||
#define TIMER2_START 0x02
|
||||
|
||||
#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
|
||||
#define RIGHT_4OP_0 0x01
|
||||
#define RIGHT_4OP_1 0x02
|
||||
#define RIGHT_4OP_2 0x04
|
||||
#define LEFT_4OP_0 0x08
|
||||
#define LEFT_4OP_1 0x10
|
||||
#define LEFT_4OP_2 0x20
|
||||
|
||||
#define OPL3_MODE_REGISTER 0x05 /* Right side */
|
||||
#define OPL3_ENABLE 0x01
|
||||
|
||||
#define KBD_SPLIT_REGISTER 0x08 /* Left side */
|
||||
#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
|
||||
#define KEYBOARD_SPLIT 0x40
|
||||
|
||||
#define PERCUSSION_REGISTER 0xbd /* Left side only */
|
||||
#define TREMOLO_DEPTH 0x80
|
||||
#define VIBRATO_DEPTH 0x40
|
||||
#define PERCUSSION_ENABLE 0x20
|
||||
#define BASSDRUM_ON 0x10
|
||||
#define SNAREDRUM_ON 0x08
|
||||
#define TOMTOM_ON 0x04
|
||||
#define CYMBAL_ON 0x02
|
||||
#define HIHAT_ON 0x01
|
||||
|
||||
/*
|
||||
* Offsets to the register banks for operators. To get the
|
||||
* register number just add the operator offset to the bank offset
|
||||
*
|
||||
* AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
|
||||
*/
|
||||
#define AM_VIB 0x20
|
||||
#define TREMOLO_ON 0x80
|
||||
#define VIBRATO_ON 0x40
|
||||
#define SUSTAIN_ON 0x20
|
||||
#define KSR 0x10 /* Key scaling rate */
|
||||
#define MULTIPLE_MASK 0x0f /* Frequency multiplier */
|
||||
|
||||
/*
|
||||
* KSL/Total level (0x40 to 0x55)
|
||||
*/
|
||||
#define KSL_LEVEL 0x40
|
||||
#define KSL_MASK 0xc0 /* Envelope scaling bits */
|
||||
#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
|
||||
|
||||
/*
|
||||
* Attack / Decay rate (0x60 to 0x75)
|
||||
*/
|
||||
#define ATTACK_DECAY 0x60
|
||||
#define ATTACK_MASK 0xf0
|
||||
#define DECAY_MASK 0x0f
|
||||
|
||||
/*
|
||||
* Sustain level / Release rate (0x80 to 0x95)
|
||||
*/
|
||||
#define SUSTAIN_RELEASE 0x80
|
||||
#define SUSTAIN_MASK 0xf0
|
||||
#define RELEASE_MASK 0x0f
|
||||
|
||||
/*
|
||||
* Wave select (0xE0 to 0xF5)
|
||||
*/
|
||||
#define WAVE_SELECT 0xe0
|
||||
|
||||
/*
|
||||
* Offsets to the register banks for voices. Just add to the
|
||||
* voice number to get the register number.
|
||||
*
|
||||
* F-Number low bits (0xA0 to 0xA8).
|
||||
*/
|
||||
#define FNUM_LOW 0xa0
|
||||
|
||||
/*
|
||||
* F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
|
||||
*/
|
||||
#define KEYON_BLOCK 0xb0
|
||||
#define KEYON_BIT 0x20
|
||||
#define BLOCKNUM_MASK 0x1c
|
||||
#define FNUM_HIGH_MASK 0x03
|
||||
|
||||
/*
|
||||
* Feedback / Connection (0xc0 to 0xc8)
|
||||
*
|
||||
* These registers have two new bits when the OPL-3 mode
|
||||
* is selected. These bits controls connecting the voice
|
||||
* to the stereo channels. For 4 OP voices this bit is
|
||||
* defined in the second half of the voice (add 3 to the
|
||||
* register offset).
|
||||
*
|
||||
* For 4 OP voices the connection bit is used in the
|
||||
* both halfs (gives 4 ways to connect the operators).
|
||||
*/
|
||||
#define FEEDBACK_CONNECTION 0xc0
|
||||
#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
|
||||
#define CONNECTION_BIT 0x01
|
||||
/*
|
||||
* In the 4 OP mode there is four possible configurations how the
|
||||
* operators can be connected together (in 2 OP modes there is just
|
||||
* AM or FM). The 4 OP connection mode is defined by the rightmost
|
||||
* bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs.
|
||||
*
|
||||
* First half Second half Mode
|
||||
*
|
||||
* +---+
|
||||
* v |
|
||||
* 0 0 >+-1-+--2--3--4-->
|
||||
*
|
||||
*
|
||||
*
|
||||
* +---+
|
||||
* | |
|
||||
* 0 1 >+-1-+--2-+
|
||||
* |->
|
||||
* >--3----4-+
|
||||
*
|
||||
* +---+
|
||||
* | |
|
||||
* 1 0 >+-1-+-----+
|
||||
* |->
|
||||
* >--2--3--4-+
|
||||
*
|
||||
* +---+
|
||||
* | |
|
||||
* 1 1 >+-1-+--+
|
||||
* |
|
||||
* >--2--3-+->
|
||||
* |
|
||||
* >--4----+
|
||||
*/
|
||||
#define STEREO_BITS 0x30 /* OPL-3 only */
|
||||
#define VOICE_TO_LEFT 0x10
|
||||
#define VOICE_TO_RIGHT 0x20
|
||||
|
||||
/*
|
||||
* Definition table for the physical voices
|
||||
*/
|
||||
|
||||
struct physical_voice_info {
|
||||
unsigned char voice_num;
|
||||
unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
|
||||
unsigned short ioaddr; /* I/O port (left or right side) */
|
||||
unsigned char op[4]; /* Operator offsets */
|
||||
};
|
||||
|
||||
/*
|
||||
* There is 18 possible 2 OP voices
|
||||
* (9 in the left and 9 in the right).
|
||||
* The first OP is the modulator and 2nd is the carrier.
|
||||
*
|
||||
* The first three voices in the both sides may be connected
|
||||
* with another voice to a 4 OP voice. For example voice 0
|
||||
* can be connected with voice 3. The operators of voice 3 are
|
||||
* used as operators 3 and 4 of the new 4 OP voice.
|
||||
* In this case the 2 OP voice number 0 is the 'first half' and
|
||||
* voice 3 is the second.
|
||||
*/
|
||||
|
||||
#define USE_LEFT 0
|
||||
#define USE_RIGHT 1
|
||||
|
||||
static struct physical_voice_info physical_voices[18] =
|
||||
{
|
||||
/* No Mode Side OP1 OP2 OP3 OP4 */
|
||||
/* --------------------------------------------------- */
|
||||
{ 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
|
||||
{ 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
|
||||
{ 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
|
||||
|
||||
{ 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
|
||||
{ 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
|
||||
{ 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
|
||||
|
||||
{ 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
|
||||
{ 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
|
||||
{ 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
|
||||
|
||||
{ 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
|
||||
{ 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
|
||||
{ 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
|
||||
|
||||
{ 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
|
||||
{ 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
|
||||
{ 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
|
||||
|
||||
{ 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
|
||||
{ 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
|
||||
{ 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
|
||||
};
|
273
sys/i386/isa/sound/os.h
Normal file
273
sys/i386/isa/sound/os.h
Normal file
|
@ -0,0 +1,273 @@
|
|||
#ifndef _OS_H_
|
||||
#define _OS_H_
|
||||
/*
|
||||
* OS specific settings for FreeBSD
|
||||
*
|
||||
* This chould be used as an example when porting the driver to a new
|
||||
* operating systems.
|
||||
*
|
||||
* What you should do is to rewrite the soundcard.c and os.h (this file).
|
||||
* You should create a new subdirectory and put these two files there.
|
||||
* In addition you have to do a makefile.<OS>.
|
||||
*
|
||||
* If you have to make changes to other than these two files, please contact me
|
||||
* before making the changes. It's possible that I have already made the
|
||||
* change.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Insert here the includes required by your kernel.
|
||||
*/
|
||||
|
||||
#include "param.h"
|
||||
#include "systm.h"
|
||||
#include "ioctl.h"
|
||||
#include "tty.h"
|
||||
#include "proc.h"
|
||||
#include "user.h"
|
||||
#include "conf.h"
|
||||
#include "file.h"
|
||||
#include "uio.h"
|
||||
/* #include "kernel.h" */
|
||||
#include "syslog.h"
|
||||
#include "errno.h"
|
||||
#include "malloc.h"
|
||||
#include "buf.h"
|
||||
#include "i386/isa/isa_device.h"
|
||||
|
||||
/* These few lines are used by FreeBSD (only??). */
|
||||
|
||||
#if NSND > 0
|
||||
#define KERNEL_SOUNDCARD
|
||||
#else
|
||||
#undef KERNEL_SOUNDCARD
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Rest of the file is compiled only if the driver is really required.
|
||||
*/
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
/* lbolt is required by the FreeBSD version (only???) */
|
||||
extern int __timeout_val;
|
||||
extern int __process_aborting;
|
||||
|
||||
/*
|
||||
* select() is currently implemented in Linux specific way. Don't enable.
|
||||
* I don't remember what the SHORT_BANNERS means so forget it.
|
||||
*/
|
||||
|
||||
#undef ALLOW_SELECT
|
||||
#define SHORT_BANNERS
|
||||
|
||||
/* The soundcard.h could be in a nonstandard place so inclyde it here. */
|
||||
#include "soundcard.h"
|
||||
|
||||
/*
|
||||
* Here is the first portability problem. Every OS has it's own way to
|
||||
* pass a pointer to the buffer in read() and write() calls. In Linux it's
|
||||
* just a char*. In BSD it's struct uio. This parameter is passed to
|
||||
* all functions called from read() or write(). Since nothing can be
|
||||
* assumed about this structure, the driver uses set of macros for
|
||||
* accessing the user buffer.
|
||||
*
|
||||
* The driver reads/writes bytes in the user buffer sequentially which
|
||||
* means that calls like uiomove() can be used.
|
||||
*
|
||||
* snd_rw_buf is the type which is passed to the device file specific
|
||||
* read() and write() calls.
|
||||
*
|
||||
* The following macros are used to move date to and from the
|
||||
* user buffer. These macros should be used only when the
|
||||
* target or source parameter has snd_rw_buf type.
|
||||
* The offs parameter is a offset relative to the beginning of
|
||||
* the user buffer. In Linux the offset is required but for example
|
||||
* BSD passes the offset info in the uio structure. It could be usefull
|
||||
* if these macros verify that the offs parameter and the value in
|
||||
* the snd_rw_buf structure are equal.
|
||||
*/
|
||||
typedef struct uio snd_rw_buf;
|
||||
|
||||
/*
|
||||
* Move bytes from the buffer which the application given in a
|
||||
* write() call.
|
||||
* offs is position relative to the beginning of the buffer in
|
||||
* user space. The count is number of bytes to be moved.
|
||||
*/
|
||||
#define COPY_FROM_USER(target, source, offs, count) \
|
||||
if (uiomove(target, count, source)) { \
|
||||
printf ("sb: Bad copyin()!\n"); \
|
||||
} else
|
||||
/* Like COPY_FOM_USER but for writes. */
|
||||
#define COPY_TO_USER(target, offs, source, count) \
|
||||
if (uiomove(source, count, target)) { \
|
||||
printf ("sb: Bad copyout()!\n"); \
|
||||
} else
|
||||
/*
|
||||
* The following macros are like COPY_*_USER but work just with one byte (8bit),
|
||||
* short (16 bit) or long (32 bit) at a time.
|
||||
* The same restrictions apply than for COPY_*_USER
|
||||
*/
|
||||
#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, addr);}
|
||||
#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, addr);}
|
||||
#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, addr);}
|
||||
#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, addr);}
|
||||
|
||||
/*
|
||||
* The way how the ioctl arguments are passed is another nonportable thing.
|
||||
* In Linux the argument is just a pointer directly to the user segment. On
|
||||
* 386bsd the data is already moved to the kernel space. The following
|
||||
* macros should handle the difference.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IOCTL_FROM_USER is used to copy a record pointed by the argument to
|
||||
* a buffer in the kernel space. On 386bsd it can be done just by calling
|
||||
* memcpy. With Linux a memcpy_from_fs should be called instead.
|
||||
* Parameters of the following macros are like in the COPY_*_USER macros.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When the ioctl argument points to a record or array (longer than 32 bits),
|
||||
* the macros IOCTL_*_USER are used. It's assumed that the source and target
|
||||
* parameters are direct memory addresses.
|
||||
*/
|
||||
#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);}
|
||||
#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);}
|
||||
/* The following macros are used if the ioctl argument points to 32 bit int */
|
||||
#define IOCTL_IN(arg) (*(int*)arg)
|
||||
#define IOCTL_OUT(arg, ret) *(int*)arg = ret
|
||||
|
||||
/*
|
||||
* When the driver displays something to the console, printk() will be called.
|
||||
* The name can be changed here.
|
||||
*/
|
||||
#define printk printf
|
||||
|
||||
/*
|
||||
* The following macros define an interface to the process management.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define
|
||||
* a structure which can be passed as a parameter to a sleep(). The second
|
||||
* parameter is name of a flag variable (must be defined as int).
|
||||
*/
|
||||
#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; static int flag = 0
|
||||
/* Like the above but defines an array of wait queues and flags */
|
||||
#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; static int flag = {0}
|
||||
/*
|
||||
* This driver handles interrupts little bit nonstandard way. The following
|
||||
* macro is used to test if the current process has received a signal which
|
||||
* is aborts the process. This macro is called from close() to see if the
|
||||
* buffers should be discarded. If this kind info is not available, a constant
|
||||
* 1 or 0 could be returned (1 should be better than 0).
|
||||
* I'm not sure if the following is correct for FreeBSD.
|
||||
*/
|
||||
#define PROCESS_ABORTING (__process_aborting | curproc->p_sig)
|
||||
/*
|
||||
* REQUEST_TIMEOUT is called before sleep. It shoud ensure that the
|
||||
* process is woken up after given number of ticks (1/HZ secs.).
|
||||
* The wqueue gives the wait queue.
|
||||
*/
|
||||
#define REQUEST_TIMEOUT(nticks, wqueue) __timeout_val = nticks;
|
||||
|
||||
/*
|
||||
* The following macro calls sleep. It should be implemented such that
|
||||
* the process is resumed if it receives a signal. The following is propably
|
||||
* not the way how it should be done on 386bsd.
|
||||
* The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE()
|
||||
* The second parameter is a flag. It must be initialized to 1 before sleep
|
||||
* and to zero after proces continues.
|
||||
*/
|
||||
#define INTERRUPTIBLE_SLEEP_ON(on_what, flag) \
|
||||
{ \
|
||||
flag = 1; \
|
||||
flag=tsleep(&(on_what), (PRIBIO-5)|PCATCH, "sndint", __timeout_val); \
|
||||
if(flag == ERESTART) __process_aborting = 1;\
|
||||
else __process_aborting = 0;\
|
||||
__timeout_val = 0; \
|
||||
flag = 0; \
|
||||
}
|
||||
|
||||
/* An the following wakes up a process */
|
||||
#define WAKE_UP(who) wakeup(&(who))
|
||||
|
||||
/*
|
||||
* Timing macros. This driver assumes that there is a timer running in the
|
||||
* kernel. The timer should return a value which is increased once at every
|
||||
* timer tick. The macro HZ should return the number of such ticks/sec.
|
||||
*/
|
||||
|
||||
#ifndef HZ
|
||||
extern int hz;
|
||||
#define HZ hz
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GET_TIME() returns current value of the counter incremented at timer
|
||||
* ticks. This can overflow, so the timeout might be real big...
|
||||
*
|
||||
*/
|
||||
#define GET_TIME() get_time()
|
||||
/*#define GET_TIME() (lbolt) /* Returns current time (1/HZ secs since boot) */
|
||||
|
||||
/*
|
||||
* The following three macros are called before and after atomic
|
||||
* code sequences. The flags parameter has always type of unsigned long.
|
||||
* The macro DISABLE_INTR() should ensure that all interrupts which
|
||||
* may invoke any part of the driver (timer, soundcard interrupts) are
|
||||
* disabled.
|
||||
* RESTORE_INTR() should return the interrupt status back to the
|
||||
* state when DISABLE_INTR() was called. The flags parameter is
|
||||
* a variable which can carry 32 bits of state information between
|
||||
* DISABLE_INTR() and RESTORE_INTR() calls.
|
||||
*/
|
||||
#define DISABLE_INTR(flags) flags = splhigh()
|
||||
#define RESTORE_INTR(flags) splx(flags)
|
||||
|
||||
/*
|
||||
* INB() and OUTB() should be obvious. NOTE! The order of
|
||||
* paratemeters of OUTB() is different than on some other
|
||||
* operating systems.
|
||||
*/
|
||||
|
||||
#define INB inb
|
||||
#define OUTB(addr, data) outb(data, addr)
|
||||
|
||||
/* memcpy() was not defined og 386bsd. Lets define it here */
|
||||
#define memcpy(d, s, c) bcopy(s, d, c)
|
||||
|
||||
/*
|
||||
* When a error (such as EINVAL) is returned by a function,
|
||||
* the following macro is used. The driver assumes that a
|
||||
* error is signalled by returning a negative value.
|
||||
*/
|
||||
|
||||
#define RET_ERROR(err) -(err)
|
||||
|
||||
/*
|
||||
KERNEL_MALLOC() allocates requested number of memory and
|
||||
KERNEL_FREE is used to free it.
|
||||
These macros are never called from interrupt, in addition the
|
||||
nbytes will never be more than 4096 bytes. Generally the driver
|
||||
will allocate memory in blocks of 4k. If the kernel has just a
|
||||
page level memory allocation, 4K can be safely used as the size
|
||||
(the nbytes parameter can be ignored).
|
||||
*/
|
||||
#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK)
|
||||
#define KERNEL_FREE(addr) free(addr, M_TEMP)
|
||||
|
||||
/*
|
||||
* The rest of this file is not complete yet. The functions using these
|
||||
* macros will not work
|
||||
*/
|
||||
#define ALLOC_DMA_CHN(chn) (0)
|
||||
#define RELEASE_DMA_CHN(chn) (0)
|
||||
#define DMA_MODE_READ 0
|
||||
#define DMA_MODE_WRITE 1
|
||||
#define RELEASE_IRQ(irq_no)
|
||||
|
||||
#endif
|
||||
#endif
|
249
sys/i386/isa/sound/pas.h
Normal file
249
sys/i386/isa/sound/pas.h
Normal file
|
@ -0,0 +1,249 @@
|
|||
/* */
|
||||
/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */
|
||||
/* */
|
||||
/* Feel free to use this header file in any application you create that has support for the Media Vision */
|
||||
/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */
|
||||
/* */
|
||||
/* - cmetz@thor.tjhsst.edu */
|
||||
/* */
|
||||
/* Notes: */
|
||||
/* */
|
||||
/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */
|
||||
/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */
|
||||
/* they don't actually have a direct connection. */
|
||||
/* */
|
||||
/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */
|
||||
/* PAS cards are pretty defunct now, so no attempt is made here to support them. */
|
||||
/* */
|
||||
/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */
|
||||
/* incompatibilities, there still are differences that need to be accounted for. */
|
||||
/* */
|
||||
/* Card CD-ROM interface PCM chip Mixer chip FM chip */
|
||||
/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
|
||||
/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */
|
||||
/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */
|
||||
/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */
|
||||
/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
|
||||
/* */
|
||||
#define PAS_DEFAULT_BASE 0x388
|
||||
|
||||
/* Symbolic Name Value R W Subsystem Description */
|
||||
#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */
|
||||
#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */
|
||||
#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */
|
||||
#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */
|
||||
#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */
|
||||
#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */
|
||||
|
||||
#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */
|
||||
#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */
|
||||
#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */
|
||||
#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */
|
||||
#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */
|
||||
#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */
|
||||
|
||||
#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */
|
||||
#define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */
|
||||
#define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */
|
||||
#define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */
|
||||
#define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */
|
||||
#define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */
|
||||
#define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */
|
||||
#define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */
|
||||
#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */
|
||||
#define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */
|
||||
#define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */
|
||||
#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */
|
||||
#define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */
|
||||
#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */
|
||||
|
||||
#define IO_CONFIGURATION_1 0xF388 /* R W Control */
|
||||
#define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */
|
||||
#define IO_CONFIGURATION_2 0xF389 /* R W Control */
|
||||
#define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */
|
||||
#define IO_CONFIGURATION_3 0xF38A /* R W Control */
|
||||
#define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */
|
||||
|
||||
#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */
|
||||
#define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */
|
||||
#define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */
|
||||
#define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */
|
||||
#define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */
|
||||
#define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */
|
||||
#define EMULATION_ADDRESS 0xF789 /* R W Control */
|
||||
#define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */
|
||||
#define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */
|
||||
#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */
|
||||
#define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */
|
||||
#define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */
|
||||
#define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */
|
||||
|
||||
#define OPERATION_MODE_1 0xEF8B /* R Control */
|
||||
#define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */
|
||||
#define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */
|
||||
#define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */
|
||||
#define OPERATION_MODE_2 0xFF8B /* R Control */
|
||||
#define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */
|
||||
#define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */
|
||||
#define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */
|
||||
|
||||
#define INTERRUPT_MASK 0x0B8B /* R W Control */
|
||||
#define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */
|
||||
#define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */
|
||||
#define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */
|
||||
#define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */
|
||||
#define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */
|
||||
#define I_M_BOARD_REV 0xE0 /* R Control Board revision */
|
||||
|
||||
#define INTERRUPT_STATUS 0x0B89 /* R W Control */
|
||||
#define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */
|
||||
#define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */
|
||||
#define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */
|
||||
#define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */
|
||||
#define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */
|
||||
#define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */
|
||||
#define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */
|
||||
#define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */
|
||||
|
||||
#define FILTER_FREQUENCY 0x0B8A /* R W Control */
|
||||
#define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */
|
||||
#if 0
|
||||
struct { /* R W Mixer Filter translation */
|
||||
unsigned int freq:24;
|
||||
unsigned int value:8;
|
||||
} F_F_FILTER_translate[] =
|
||||
{ { 73500, 0x01 }, /* 73500Hz - divide by 16 */
|
||||
{ 65333, 0x02 }, /* 65333Hz - divide by 18 */
|
||||
{ 49000, 0x09 }, /* 49000Hz - divide by 24 */
|
||||
{ 36750, 0x11 }, /* 36750Hz - divide by 32 */
|
||||
{ 24500, 0x19 }, /* 24500Hz - divide by 48 */
|
||||
{ 18375, 0x07 }, /* 18375Hz - divide by 64 */
|
||||
{ 12783, 0x0f }, /* 12783Hz - divide by 92 */
|
||||
{ 12250, 0x04 }, /* 12250Hz - divide by 96 */
|
||||
{ 9188, 0x17 }, /* 9188Hz - divide by 128 */
|
||||
{ 6125, 0x1f }, /* 6125Hz - divide by 192 */
|
||||
};
|
||||
#endif
|
||||
#define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */
|
||||
#define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */
|
||||
#define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */
|
||||
|
||||
#define PAS_NONE 0
|
||||
#define PAS_PLUS 1
|
||||
#define PAS_CDPC 2
|
||||
#define PAS_16 3
|
||||
|
||||
#ifdef DEFINE_TRANSLATIONS
|
||||
char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
|
||||
{ 4, 1, 2, 3, 0, 5, 6, 7 };
|
||||
char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
|
||||
{ 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
|
||||
char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
|
||||
{ 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x05, 0x06, 0x07 };
|
||||
char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
|
||||
{ 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x28, 0x30, 0x38 };
|
||||
char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
|
||||
{ 0x00, 0x40, 0x80, 0xC0 };
|
||||
char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */
|
||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 2, 3 };
|
||||
#else
|
||||
extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */
|
||||
extern char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */
|
||||
extern char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */
|
||||
extern char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */
|
||||
extern char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */
|
||||
extern char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */
|
||||
#endif
|
||||
|
||||
#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */
|
||||
#define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */
|
||||
#define P_M_MV508_DATA 0x00
|
||||
#define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */
|
||||
#define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */
|
||||
#define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */
|
||||
#define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */
|
||||
#define P_M_MV508_VOLUME 0x00
|
||||
|
||||
#define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */
|
||||
#define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */
|
||||
|
||||
#define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */
|
||||
#define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */
|
||||
#define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */
|
||||
#define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */
|
||||
#define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */
|
||||
|
||||
#define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */
|
||||
#define P_M_MV508_ENHANCE_BITS 0x03
|
||||
#define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */
|
||||
#define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */
|
||||
#define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */
|
||||
#define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */
|
||||
|
||||
#define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */
|
||||
#define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */
|
||||
#define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */
|
||||
#define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */
|
||||
#define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */
|
||||
#define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */
|
||||
#define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */
|
||||
#define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */
|
||||
|
||||
#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */
|
||||
#define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */
|
||||
#define S_M_FM_RESET 0x02 /* R W FM FM chip reset */
|
||||
#define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */
|
||||
#define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */
|
||||
#define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */
|
||||
#define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */
|
||||
|
||||
#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */
|
||||
#define P_C_MIXER_CROSS_FIELD 0x0f
|
||||
#define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */
|
||||
#define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */
|
||||
#define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */
|
||||
#define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */
|
||||
#define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */
|
||||
#define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */
|
||||
#define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */
|
||||
#define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */
|
||||
#define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */
|
||||
#define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */
|
||||
|
||||
#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */
|
||||
#define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */
|
||||
#define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */
|
||||
#define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */
|
||||
|
||||
/* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */
|
||||
#define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */
|
||||
#define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */
|
||||
|
||||
#define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */
|
||||
|
||||
#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */
|
||||
#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */
|
||||
|
||||
#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */
|
||||
#define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */
|
||||
#define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */
|
||||
#define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */
|
||||
#define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */
|
||||
#define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */
|
||||
#define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */
|
||||
#define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */
|
||||
#define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */
|
||||
|
||||
#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */
|
||||
#define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */
|
||||
#define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */
|
||||
#define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */
|
||||
#define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */
|
||||
#define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */
|
||||
#define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */
|
||||
#define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */
|
||||
#define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */
|
||||
|
||||
#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */
|
||||
#define MIDI_DATA 0x178A /* R W MIDI Midi data register */
|
||||
#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */
|
343
sys/i386/isa/sound/pas2_card.c
Normal file
343
sys/i386/isa/sound/pas2_card.c
Normal file
|
@ -0,0 +1,343 @@
|
|||
#define _PAS2_CARD_C_
|
||||
#define SND_SA_INTERRUPT
|
||||
/*
|
||||
* linux/kernel/chr_drv/sound/pas2_card.c
|
||||
*
|
||||
* Detection routine for the Pro Audio Spectrum cards.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz
|
||||
* (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be
|
||||
* distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
|
||||
|
||||
#define DEFINE_TRANSLATIONS
|
||||
#include "pas.h"
|
||||
|
||||
/*
|
||||
* The Address Translation code is used to convert I/O register addresses to
|
||||
* be relative to the given base -register
|
||||
*/
|
||||
|
||||
int translat_code;
|
||||
static int pas_intr_mask = 0;
|
||||
static int pas_irq = 0;
|
||||
|
||||
static char pas_model;
|
||||
static char *pas_model_names[] =
|
||||
{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16"};
|
||||
|
||||
/* pas_read() and pas_write() are equivalents of INB() and OUTB() */
|
||||
/* These routines perform the I/O address translation required */
|
||||
/* to support other than the default base address */
|
||||
|
||||
unsigned char
|
||||
pas_read (int ioaddr)
|
||||
{
|
||||
return INB (ioaddr ^ translat_code);
|
||||
}
|
||||
|
||||
void
|
||||
pas_write (unsigned char data, int ioaddr)
|
||||
{
|
||||
OUTB (data, ioaddr ^ translat_code);
|
||||
}
|
||||
|
||||
void
|
||||
pas2_msg (char *foo)
|
||||
{
|
||||
printk (" PAS2: %s.\n", foo);
|
||||
}
|
||||
|
||||
/******************* Begin of the Interrupt Handler ********************/
|
||||
|
||||
void
|
||||
pasintr (int unused)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = pas_read (INTERRUPT_STATUS);
|
||||
pas_write (status, INTERRUPT_STATUS); /* Clear interrupt */
|
||||
|
||||
if (status & I_S_PCM_SAMPLE_BUFFER_IRQ)
|
||||
{
|
||||
#ifndef EXCLUDE_AUDIO
|
||||
pas_pcm_interrupt (status, 1);
|
||||
#endif
|
||||
status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ;
|
||||
}
|
||||
if (status & I_S_MIDI_IRQ)
|
||||
{
|
||||
#ifndef EXCLUDE_MIDI
|
||||
#ifdef EXCLUDE_PRO_MIDI
|
||||
pas_midi_interrupt ();
|
||||
#endif
|
||||
#endif
|
||||
status &= ~I_S_MIDI_IRQ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
set_pas_irq (int interrupt_level)
|
||||
{
|
||||
#ifdef linux
|
||||
int retcode;
|
||||
struct sigaction sa;
|
||||
|
||||
pas_write (0xff, INTERRUPT_STATUS); /* Reset pending interrupts */
|
||||
|
||||
sa.sa_handler = pasintr;
|
||||
|
||||
#ifdef SND_SA_INTERRUPT
|
||||
sa.sa_flags = SA_INTERRUPT;
|
||||
#else
|
||||
sa.sa_flags = 0;
|
||||
#endif
|
||||
|
||||
sa.sa_mask = 0;
|
||||
sa.sa_restorer = NULL;
|
||||
|
||||
retcode = irqaction (interrupt_level, &sa);
|
||||
|
||||
if (retcode < 0)
|
||||
{
|
||||
printk ("ProAudioSpectrum: IRQ%d already in use\n", interrupt_level);
|
||||
}
|
||||
return retcode;
|
||||
#else
|
||||
/* # error This routine does not work with this OS */
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
pas_set_intr (int mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!mask)
|
||||
return 0;
|
||||
|
||||
if (!pas_intr_mask)
|
||||
{
|
||||
if ((err = set_pas_irq (pas_irq)) < 0)
|
||||
return err;
|
||||
}
|
||||
pas_intr_mask |= mask;
|
||||
|
||||
pas_write (pas_intr_mask, INTERRUPT_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pas_remove_intr (int mask)
|
||||
{
|
||||
if (!mask)
|
||||
return 0;
|
||||
|
||||
pas_intr_mask &= ~mask;
|
||||
pas_write (pas_intr_mask, INTERRUPT_MASK);
|
||||
|
||||
if (!pas_intr_mask)
|
||||
{
|
||||
RELEASE_IRQ (pas_irq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************* End of the Interrupt handler **********************/
|
||||
|
||||
/******************* Begin of the Initialization Code ******************/
|
||||
|
||||
int
|
||||
config_pas_hw (struct address_info *hw_config)
|
||||
{
|
||||
char ok = 1;
|
||||
|
||||
pas_irq = hw_config->irq;
|
||||
|
||||
pas_write (0x00, INTERRUPT_MASK);
|
||||
|
||||
pas_write (0x36, SAMPLE_COUNTER_CONTROL); /* Local timer control
|
||||
* register */
|
||||
|
||||
pas_write (0x36, SAMPLE_RATE_TIMER); /* Sample rate timer (16 bit) */
|
||||
pas_write (0, SAMPLE_RATE_TIMER);
|
||||
|
||||
pas_write (0x74, SAMPLE_COUNTER_CONTROL); /* Local timer control
|
||||
* register */
|
||||
|
||||
pas_write (0x74, SAMPLE_BUFFER_COUNTER); /* Sample count register (16
|
||||
* bit) */
|
||||
pas_write (0, SAMPLE_BUFFER_COUNTER);
|
||||
|
||||
pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY);
|
||||
pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL);
|
||||
pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* | S_M_OPL3_DUAL_MONO */ , SERIAL_MIXER);
|
||||
|
||||
pas_write (I_C_1_BOOT_RESET_ENABLE, IO_CONFIGURATION_1);
|
||||
|
||||
if (pas_irq < 0 || pas_irq > 15)
|
||||
{
|
||||
printk ("PAS2: Invalid IRQ %d", pas_irq);
|
||||
ok = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pas_write (I_C_3_PCM_IRQ_translate[pas_irq], IO_CONFIGURATION_3);
|
||||
if (!I_C_3_PCM_IRQ_translate[pas_irq])
|
||||
{
|
||||
printk ("PAS2: Invalid IRQ %d", pas_irq);
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (hw_config->dma < 0 || hw_config->dma > 7)
|
||||
{
|
||||
printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
|
||||
ok = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2);
|
||||
if (!I_C_2_PCM_DMA_translate[hw_config->dma])
|
||||
{
|
||||
printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BROKEN_BUS_CLOCK
|
||||
pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1);
|
||||
#else
|
||||
/* pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); */
|
||||
pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1);
|
||||
#endif
|
||||
/* pas_write(S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); Don't do this */
|
||||
pas_write (0x18, SYSTEM_CONFIGURATION_3); /* ??? */
|
||||
|
||||
pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* Sets mute off and
|
||||
* selects filter rate
|
||||
* of 17.897 kHz */
|
||||
|
||||
if (pas_model == PAS_16)
|
||||
pas_write (8, PRESCALE_DIVIDER);
|
||||
else
|
||||
pas_write (0, PRESCALE_DIVIDER);
|
||||
|
||||
pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
|
||||
pas_write (5, PARALLEL_MIXER);
|
||||
|
||||
#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
|
||||
|
||||
/* Turn on Sound Blaster compatibility */
|
||||
/* bit 1 = SB emulation */
|
||||
/* bit 0 = MPU401 emulation (CDPC only :-( ) */
|
||||
pas_write (0x02, COMPATIBILITY_ENABLE);
|
||||
|
||||
/* "Emulation address" */
|
||||
pas_write ((SBC_BASE >> 4) & 0x0f, EMULATION_ADDRESS);
|
||||
#endif
|
||||
|
||||
if (!ok)
|
||||
pas2_msg ("Driver not enabled");
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
detect_pas_hw (struct address_info *hw_config)
|
||||
{
|
||||
unsigned char board_id, foo;
|
||||
|
||||
/*
|
||||
* WARNING: Setting an option like W:1 or so that disables warm boot reset
|
||||
* of the card will screw up this detect code something fierce. Adding code
|
||||
* to handle this means possibly interfering with other cards on the bus if
|
||||
* you have something on base port 0x388. SO be forewarned.
|
||||
*/
|
||||
|
||||
OUTB (0xBC, MASTER_DECODE); /* Talk to first board */
|
||||
OUTB (hw_config->io_base >> 2, MASTER_DECODE); /* Set base address */
|
||||
translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base;
|
||||
pas_write (1, WAIT_STATE); /* One wait-state */
|
||||
|
||||
board_id = pas_read (INTERRUPT_MASK);
|
||||
|
||||
if (board_id == 0xff)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We probably have a PAS-series board, now check for a PAS2-series board
|
||||
* by trying to change the board revision bits. PAS2-series hardware won't
|
||||
* let you do this - the bits are read-only.
|
||||
*/
|
||||
|
||||
foo = board_id ^ 0xe0;
|
||||
|
||||
pas_write (foo, INTERRUPT_MASK);
|
||||
foo = INB (INTERRUPT_MASK);
|
||||
pas_write (board_id, INTERRUPT_MASK);
|
||||
|
||||
if (board_id != foo) /* Not a PAS2 */
|
||||
return 0;
|
||||
|
||||
if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]));
|
||||
|
||||
return pas_model;
|
||||
}
|
||||
|
||||
long
|
||||
attach_pas_card (long mem_start, struct address_info *hw_config)
|
||||
{
|
||||
pas_irq = hw_config->irq;
|
||||
|
||||
if (detect_pas_hw (hw_config))
|
||||
{
|
||||
|
||||
if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]))
|
||||
{
|
||||
printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
|
||||
}
|
||||
|
||||
if (config_pas_hw (hw_config))
|
||||
{
|
||||
|
||||
#ifndef EXCLUDE_AUDIO
|
||||
mem_start = pas_pcm_init (mem_start, hw_config);
|
||||
#endif
|
||||
|
||||
# if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
|
||||
|
||||
sb_dsp_disable_midi (); /* The SB emulation don't support
|
||||
* midi */
|
||||
# endif
|
||||
|
||||
#ifndef EXCLUDE_YM3812
|
||||
enable_opl3_mode (0x388, 0x38a, 0);
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_MIDI
|
||||
#ifdef EXCLUDE_PRO_MIDI
|
||||
mem_start = pas_midi_init (mem_start);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
pas_init_mixer ();
|
||||
}
|
||||
}
|
||||
|
||||
printk("\n");
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
int
|
||||
probe_pas (struct address_info *hw_config)
|
||||
{
|
||||
return detect_pas_hw (hw_config);
|
||||
}
|
||||
|
||||
#endif
|
269
sys/i386/isa/sound/pas2_midi.c
Normal file
269
sys/i386/isa/sound/pas2_midi.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/pas2_midi.c
|
||||
*
|
||||
* The low level driver for the PAS Midi Interface.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#include "pas.h"
|
||||
|
||||
#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI)
|
||||
|
||||
static int midi_busy = 0, input_opened = 0;
|
||||
static int my_dev;
|
||||
static volatile int ofifo_bytes = 0;
|
||||
|
||||
static unsigned char tmp_queue[256];
|
||||
static volatile int qlen;
|
||||
static volatile unsigned char qhead, qtail;
|
||||
|
||||
static int
|
||||
pas_midi_open (int dev, int mode)
|
||||
{
|
||||
int err;
|
||||
unsigned long flags;
|
||||
unsigned char ctrl;
|
||||
|
||||
|
||||
if (midi_busy)
|
||||
{
|
||||
printk ("PAS2: Midi busy\n");
|
||||
return RET_ERROR (EBUSY);
|
||||
}
|
||||
|
||||
/* Reset input and output FIFO pointers */
|
||||
pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO,
|
||||
MIDI_CONTROL);
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0)
|
||||
return err;
|
||||
|
||||
/* Enable input available and output FIFO empty interrupts */
|
||||
|
||||
ctrl = 0;
|
||||
input_opened = 0;
|
||||
|
||||
if (mode == OPEN_READ || mode == OPEN_READWRITE)
|
||||
{
|
||||
ctrl |= M_C_ENA_INPUT_IRQ;/* Enable input */
|
||||
input_opened = 1;
|
||||
}
|
||||
|
||||
if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
|
||||
{
|
||||
ctrl |= M_C_ENA_OUTPUT_IRQ | /* Enable output */
|
||||
M_C_ENA_OUTPUT_HALF_IRQ;
|
||||
}
|
||||
|
||||
pas_write (ctrl,
|
||||
MIDI_CONTROL);
|
||||
|
||||
/* Acknowledge any pending interrupts */
|
||||
|
||||
pas_write (0xff, MIDI_STATUS);
|
||||
ofifo_bytes = 0;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
midi_busy = 1;
|
||||
qlen = qhead = qtail = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pas_midi_close (int dev)
|
||||
{
|
||||
|
||||
/* Reset FIFO pointers, disable intrs */
|
||||
pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL);
|
||||
|
||||
pas_remove_intr (I_M_MIDI_IRQ_ENABLE);
|
||||
midi_busy = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dump_to_midi (unsigned char midi_byte)
|
||||
{
|
||||
int fifo_space, x;
|
||||
|
||||
fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f;
|
||||
|
||||
if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /* Fifo full */
|
||||
{
|
||||
return 0; /* Upper layer will call again */
|
||||
}
|
||||
|
||||
ofifo_bytes++;
|
||||
|
||||
pas_write (midi_byte, MIDI_DATA);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_midi_out (int dev, unsigned char midi_byte)
|
||||
{
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Drain the local queue first
|
||||
*/
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
while (qlen && dump_to_midi (tmp_queue[qhead]))
|
||||
{
|
||||
qlen--;
|
||||
qhead++;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
/*
|
||||
* Output the byte if the local queue is empty.
|
||||
*/
|
||||
|
||||
if (!qlen)
|
||||
if (dump_to_midi (midi_byte))
|
||||
return 1; /* OK */
|
||||
|
||||
/*
|
||||
* Put to the local queue
|
||||
*/
|
||||
|
||||
if (qlen >= 256)
|
||||
return 0; /* Local queue full */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
tmp_queue[qtail] = midi_byte;
|
||||
qlen++;
|
||||
qtail++;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_midi_start_read (int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_midi_end_read (int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_midi_ioctl (int dev, unsigned cmd, unsigned arg)
|
||||
{
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
static void
|
||||
pas_midi_kick (int dev)
|
||||
{
|
||||
ofifo_bytes = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_buffer_status (int dev)
|
||||
{
|
||||
return !qlen;
|
||||
}
|
||||
|
||||
static struct midi_operations pas_midi_operations =
|
||||
{
|
||||
{"Pro Audio Spectrum", 0},
|
||||
pas_midi_open,
|
||||
pas_midi_close,
|
||||
pas_midi_ioctl,
|
||||
pas_midi_out,
|
||||
pas_midi_start_read,
|
||||
pas_midi_end_read,
|
||||
pas_midi_kick,
|
||||
NULL, /* command */
|
||||
pas_buffer_status
|
||||
};
|
||||
|
||||
long
|
||||
pas_midi_init (long mem_start)
|
||||
{
|
||||
my_dev = num_midis;
|
||||
midi_devs[num_midis++] = &pas_midi_operations;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
void
|
||||
pas_midi_interrupt (void)
|
||||
{
|
||||
unsigned char stat;
|
||||
int i, incount;
|
||||
unsigned long flags;
|
||||
|
||||
stat = pas_read (MIDI_STATUS);
|
||||
|
||||
if (stat & M_S_INPUT_AVAIL) /* Input byte available */
|
||||
{
|
||||
incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /* Input FIFO count */
|
||||
if (!incount)
|
||||
incount = 16;
|
||||
|
||||
for (i = 0; i < incount; i++)
|
||||
if (input_opened)
|
||||
{
|
||||
sequencer_midi_input (my_dev, pas_read (MIDI_DATA));
|
||||
}
|
||||
else
|
||||
pas_read (MIDI_DATA); /* Flush */
|
||||
}
|
||||
|
||||
if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY))
|
||||
{
|
||||
if (!(stat & M_S_OUTPUT_EMPTY))
|
||||
{
|
||||
ofifo_bytes = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
ofifo_bytes = 0;
|
||||
}
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
while (qlen && dump_to_midi (tmp_queue[qhead]))
|
||||
{
|
||||
qlen--;
|
||||
qhead++;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
if (stat & M_S_FRAMING_ERROR)
|
||||
printk ("MIDI framing error\n");
|
||||
|
||||
if (stat & M_S_OUTPUT_OVERRUN)
|
||||
{
|
||||
printk ("MIDI output overrun %02x,%02x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
|
||||
ofifo_bytes = 100;
|
||||
}
|
||||
|
||||
pas_write (stat, MIDI_STATUS);/* Acknowledge interrupts */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
481
sys/i386/isa/sound/pas2_mixer.c
Normal file
481
sys/i386/isa/sound/pas2_mixer.c
Normal file
|
@ -0,0 +1,481 @@
|
|||
#define _PAS2_MIXER_C_
|
||||
|
||||
/*
|
||||
* linux/kernel/chr_drv/sound/pas2_mixer.c
|
||||
*
|
||||
* Mixer routines for the Pro Audio Spectrum cards.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz
|
||||
* (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be
|
||||
* distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
|
||||
|
||||
#include "pas.h"
|
||||
|
||||
#define TRACE(what) /* (what) */
|
||||
|
||||
extern int translat_code;
|
||||
|
||||
static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */
|
||||
static int mode_control = 0;
|
||||
|
||||
#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
|
||||
SOUND_MASK_CD | SOUND_MASK_ALTPCM)
|
||||
|
||||
#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
|
||||
SOUND_MASK_CD /*|SOUND_MASK_ALTPCM*/ | SOUND_MASK_IMIX | \
|
||||
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
|
||||
SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
|
||||
|
||||
static unsigned short levels[SOUND_MIXER_NRDEVICES] =
|
||||
{
|
||||
0x3232, /* Master Volume */
|
||||
0x3232, /* Bass */
|
||||
0x3232, /* Treble */
|
||||
0x5050, /* FM */
|
||||
0x4b4b, /* PCM */
|
||||
0x3232, /* PC Speaker */
|
||||
0x4b4b, /* Ext Line */
|
||||
0x4b4b, /* Mic */
|
||||
0x4b4b, /* CD */
|
||||
0x6464, /* Recording monitor */
|
||||
0x4b4b, /* SB PCM */
|
||||
0x6464}; /* Recording level */
|
||||
|
||||
static int
|
||||
mixer_output (int right_vol, int left_vol, int div, int bits,
|
||||
int mixer /* Input or output mixer */ )
|
||||
{
|
||||
int left = left_vol * div / 100;
|
||||
int right = right_vol * div / 100;
|
||||
|
||||
/*
|
||||
* The Revision D cards have a problem with their MVA508 interface. The
|
||||
* kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
|
||||
* MSBs out of the output byte and to do a 16-bit out to the mixer port -
|
||||
* 1. We don't need to do this because the call to pas_write more than
|
||||
* compensates for the timing problems.
|
||||
*/
|
||||
|
||||
if (bits & P_M_MV508_MIXER)
|
||||
{ /* Select input or output mixer */
|
||||
left |= mixer;
|
||||
right |= mixer;
|
||||
}
|
||||
|
||||
if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
|
||||
{ /* Bass and trebble are mono devices */
|
||||
pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
|
||||
pas_write (left, PARALLEL_MIXER);
|
||||
right_vol = left_vol;
|
||||
}
|
||||
else
|
||||
{
|
||||
pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
|
||||
pas_write (left, PARALLEL_MIXER);
|
||||
pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
|
||||
pas_write (right, PARALLEL_MIXER);
|
||||
}
|
||||
|
||||
return (left_vol | (right_vol << 8));
|
||||
}
|
||||
|
||||
void
|
||||
set_mode (int new_mode)
|
||||
{
|
||||
pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
|
||||
pas_write (new_mode, PARALLEL_MIXER);
|
||||
|
||||
mode_control = new_mode;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_mixer_set (int whichDev, unsigned int level)
|
||||
{
|
||||
int left, right, devmask, changed, i, mixer = 0;
|
||||
|
||||
TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
|
||||
|
||||
left = level & 0x7f;
|
||||
right = (level & 0x7f00) >> 8;
|
||||
|
||||
if (whichDev < SOUND_MIXER_NRDEVICES)
|
||||
if ((1 << whichDev) & rec_devices)
|
||||
mixer = P_M_MV508_INPUTMIX;
|
||||
else
|
||||
mixer = P_M_MV508_OUTPUTMIX;
|
||||
|
||||
switch (whichDev)
|
||||
{
|
||||
case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
|
||||
levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Note! Bass and Treble are mono devices. Will use just the left
|
||||
* channel.
|
||||
*/
|
||||
case SOUND_MIXER_BASS: /* Bass (0-12) */
|
||||
levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0);
|
||||
break;
|
||||
case SOUND_MIXER_TREBLE: /* Treble (0-12) */
|
||||
levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0);
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_LINE: /* External line (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_CD: /* CD (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_MIC: /* External microphone (0-31) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
|
||||
break;
|
||||
case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Only available
|
||||
* on the Output Mixer) */
|
||||
levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
|
||||
P_M_MV508_OUTPUTMIX);
|
||||
break;
|
||||
case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
|
||||
levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0);
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_MUTE:
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_ENHANCE:
|
||||
i = 0;
|
||||
level &= 0x7f;
|
||||
if (level)
|
||||
i = (level / 20) - 1;
|
||||
|
||||
mode_control &= ~P_M_MV508_ENHANCE_BITS;
|
||||
mode_control |= P_M_MV508_ENHANCE_BITS;
|
||||
set_mode (mode_control);
|
||||
|
||||
if (i)
|
||||
i = (i + 1) * 20;
|
||||
return i;
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_LOUD:
|
||||
mode_control &= ~P_M_MV508_LOUDNESS;
|
||||
if (level)
|
||||
mode_control |= P_M_MV508_LOUDNESS;
|
||||
set_mode (mode_control);
|
||||
return !!level; /* 0 or 1 */
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_RECSRC:
|
||||
devmask = level & POSSIBLE_RECORDING_DEVICES;
|
||||
|
||||
changed = devmask ^ rec_devices;
|
||||
rec_devices = devmask;
|
||||
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
||||
if (changed & (1 << i))
|
||||
{
|
||||
pas_mixer_set (i, levels[i]);
|
||||
}
|
||||
return rec_devices;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
return (levels[whichDev]);
|
||||
}
|
||||
|
||||
/*****/
|
||||
|
||||
static int
|
||||
mixer_set_levels (struct sb_mixer_levels *user_l)
|
||||
{
|
||||
#define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15))
|
||||
|
||||
struct sb_mixer_levels l;
|
||||
|
||||
IOCTL_FROM_USER ((char *) &l, (char *) user_l, 0, sizeof (l));
|
||||
|
||||
if (l.master.l & ~0xF || l.master.r & ~0xF
|
||||
|| l.line.l & ~0xF || l.line.r & ~0xF
|
||||
|| l.voc.l & ~0xF || l.voc.r & ~0xF
|
||||
|| l.fm.l & ~0xF || l.fm.r & ~0xF
|
||||
|| l.cd.l & ~0xF || l.cd.r & ~0xF
|
||||
|| l.mic & ~0x7)
|
||||
return (RET_ERROR (EINVAL));
|
||||
|
||||
pas_mixer_set (SOUND_MIXER_VOLUME, cmix (l.master));
|
||||
pas_mixer_set (SOUND_MIXER_LINE, cmix (l.line));
|
||||
pas_mixer_set (SOUND_MIXER_PCM, cmix (l.voc));
|
||||
pas_mixer_set (SOUND_MIXER_ALTPCM, cmix (l.voc));
|
||||
pas_mixer_set (SOUND_MIXER_SYNTH, cmix (l.fm));
|
||||
pas_mixer_set (SOUND_MIXER_CD, cmix (l.cd));
|
||||
pas_mixer_set (SOUND_MIXER_MIC, ((l.mic * 100 + 3) / 7) | (((l.mic * 100 + 3) / 7) << 8));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets aspects of the Mixer that are not volume levels. (Recording
|
||||
* source, filter level, I/O filtering, and stereo.)
|
||||
*/
|
||||
static int
|
||||
mixer_set_params (struct sb_mixer_params *user_p)
|
||||
{
|
||||
struct sb_mixer_params p;
|
||||
S_BYTE val;
|
||||
int src;
|
||||
unsigned long flags;
|
||||
|
||||
IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
|
||||
|
||||
if (p.record_source != SRC_MIC
|
||||
&& p.record_source != SRC_CD
|
||||
&& p.record_source != SRC_LINE)
|
||||
return (RET_ERROR (EINVAL));
|
||||
|
||||
/*
|
||||
* I'm not sure if this is The Right Thing. Should stereo be entirely
|
||||
* under control of DSP? I like being able to toggle it while a sound is
|
||||
* playing, so I do this... because I can.
|
||||
*/
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
val = (pas_read (PCM_CONTROL) & ~P_C_MIXER_CROSS_FIELD) | P_C_MIXER_CROSS_R_TO_R | P_C_MIXER_CROSS_L_TO_L;
|
||||
if (!p.dsp_stereo)
|
||||
val |= (P_C_MIXER_CROSS_R_TO_L | P_C_MIXER_CROSS_L_TO_R); /* Mono */
|
||||
pas_write (val, PCM_CONTROL);
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
switch (p.record_source)
|
||||
{
|
||||
case SRC_CD:
|
||||
src = SOUND_MASK_CD;
|
||||
break;
|
||||
|
||||
case SRC_LINE:
|
||||
src = SOUND_MASK_LINE;
|
||||
break;
|
||||
|
||||
default:
|
||||
src = SOUND_MASK_MIC;
|
||||
break;
|
||||
}
|
||||
|
||||
pas_mixer_set (SOUND_MIXER_RECSRC, src);
|
||||
|
||||
/*
|
||||
* setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) |
|
||||
* (p.filter_output ? FILT_ON : FILT_OFF)));
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
getmixer (int dev, int chn)
|
||||
{
|
||||
if (chn == P_M_MV508_RIGHT)
|
||||
{
|
||||
return (levels[dev] >> 8) & 0x7f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return levels[dev] & 0x7f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the current mixer level settings into the user's struct. */
|
||||
static int
|
||||
mixer_get_levels (struct sb_mixer_levels *user_l)
|
||||
{
|
||||
|
||||
struct sb_mixer_levels l;
|
||||
|
||||
l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100; /* Master */
|
||||
l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100; /* Master */
|
||||
|
||||
l.line.r = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_RIGHT) * 15) + 50) / 100; /* Line */
|
||||
l.line.l = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_LEFT) * 15) + 50) / 100;
|
||||
|
||||
l.voc.r = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_RIGHT) * 15) + 50) / 100; /* DAC */
|
||||
l.voc.l = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_LEFT) * 15) + 50) / 100;
|
||||
|
||||
l.fm.r = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_RIGHT) * 15) + 50) / 100; /* FM */
|
||||
l.fm.l = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_LEFT) * 15) + 50) / 100;
|
||||
|
||||
l.cd.r = ((getmixer (SOUND_MIXER_CD, P_M_MV508_RIGHT) * 15) + 50) / 100; /* CD */
|
||||
l.cd.l = ((getmixer (SOUND_MIXER_CD, P_M_MV508_LEFT) * 15) + 50) / 100;
|
||||
|
||||
l.mic = ((getmixer (SOUND_MIXER_MIC, P_M_MV508_LEFT) * 7) + 50) / 100; /* Microphone */
|
||||
|
||||
IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Read the current mixer parameters into the user's struct. */
|
||||
static int
|
||||
mixer_get_params (struct sb_mixer_params *user_params)
|
||||
{
|
||||
S_BYTE val;
|
||||
struct sb_mixer_params params;
|
||||
|
||||
switch (rec_devices)
|
||||
{
|
||||
case SOUND_MASK_CD:
|
||||
params.record_source = SRC_CD;
|
||||
break;
|
||||
|
||||
case SOUND_MASK_LINE:
|
||||
params.record_source = SRC_LINE;
|
||||
break;
|
||||
|
||||
case SOUND_MASK_MIC:
|
||||
params.record_source = SRC_MIC;
|
||||
break;
|
||||
|
||||
default:
|
||||
params.record_source = SRC_MIC;
|
||||
pas_mixer_set (SOUND_MIXER_RECSRC, SOUND_MASK_MIC); /* Adjust */
|
||||
}
|
||||
|
||||
params.hifreq_filter = OFF;
|
||||
params.filter_input = OFF;
|
||||
params.filter_output = OFF;
|
||||
|
||||
val = INB (PCM_CONTROL);
|
||||
params.dsp_stereo = ((val & P_C_MIXER_CROSS_FIELD) == (P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R));
|
||||
|
||||
IOCTL_TO_USER ((char *) user_params, 0, (char *) ¶ms, sizeof (params));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*****/
|
||||
|
||||
static void
|
||||
pas_mixer_reset (void)
|
||||
{
|
||||
int foo;
|
||||
|
||||
TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
|
||||
|
||||
for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
|
||||
pas_mixer_set (foo, levels[foo]);
|
||||
|
||||
set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
|
||||
}
|
||||
|
||||
int
|
||||
pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
|
||||
{
|
||||
TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
|
||||
|
||||
if (((cmd >> 8) & 0xff) == 'M')
|
||||
{
|
||||
if (cmd & IOC_IN)
|
||||
return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
|
||||
else
|
||||
{ /* Read parameters */
|
||||
|
||||
switch (cmd & 0xff)
|
||||
{
|
||||
|
||||
case SOUND_MIXER_RECSRC:
|
||||
return IOCTL_OUT (arg, rec_devices);
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_STEREODEVS:
|
||||
return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_DEVMASK:
|
||||
return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_RECMASK:
|
||||
return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_CAPS:
|
||||
return IOCTL_OUT (arg, 0); /* No special capabilities */
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_MUTE:
|
||||
return IOCTL_OUT (arg, 0); /* No mute yet */
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_ENHANCE:
|
||||
if (!(mode_control & P_M_MV508_ENHANCE_BITS))
|
||||
return IOCTL_OUT (arg, 0);
|
||||
return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20);
|
||||
break;
|
||||
|
||||
case SOUND_MIXER_LOUD:
|
||||
if (mode_control & P_M_MV508_LOUDNESS)
|
||||
return IOCTL_OUT (arg, 1);
|
||||
return IOCTL_OUT (arg, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return IOCTL_OUT (arg, levels[cmd & 0xff]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case MIXER_IOCTL_SET_LEVELS:
|
||||
mixer_set_levels ((struct sb_mixer_levels *) arg);
|
||||
return mixer_get_levels ((struct sb_mixer_levels *) arg);
|
||||
case MIXER_IOCTL_SET_PARAMS:
|
||||
mixer_set_params ((struct sb_mixer_params *) arg);
|
||||
return mixer_get_params ((struct sb_mixer_params *) arg);
|
||||
case MIXER_IOCTL_READ_LEVELS:
|
||||
return mixer_get_levels ((struct sb_mixer_levels *) arg);
|
||||
case MIXER_IOCTL_READ_PARAMS:
|
||||
return mixer_get_params ((struct sb_mixer_params *) arg);
|
||||
case MIXER_IOCTL_RESET:
|
||||
pas_mixer_reset ();
|
||||
return (0);
|
||||
default:
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
}
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
static struct mixer_operations pas_mixer_operations =
|
||||
{
|
||||
pas_mixer_ioctl
|
||||
};
|
||||
|
||||
int
|
||||
pas_init_mixer (void)
|
||||
{
|
||||
pas_mixer_reset ();
|
||||
|
||||
mixer_devs[num_mixers++] = &pas_mixer_operations;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
412
sys/i386/isa/sound/pas2_pcm.c
Normal file
412
sys/i386/isa/sound/pas2_pcm.c
Normal file
|
@ -0,0 +1,412 @@
|
|||
#define _PAS2_PCM_C_
|
||||
/*
|
||||
* linux/kernel/chr_drv/sound/pas2_pcm.c
|
||||
*
|
||||
* The low level driver for the Pro Audio Spectrum ADC/DAC.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz
|
||||
* (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be
|
||||
* distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#include "pas.h"
|
||||
|
||||
#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO)
|
||||
|
||||
#define TRACE(WHAT) /* (WHAT) */
|
||||
|
||||
#define PAS_PCM_INTRBITS (0x08)
|
||||
/* Sample buffer timer interrupt enable */
|
||||
|
||||
#define PCM_NON 0
|
||||
#define PCM_DAC 1
|
||||
#define PCM_ADC 2
|
||||
|
||||
static unsigned long pcm_speed = 0; /* sampling rate */
|
||||
static unsigned char pcm_channels = 1; /* channels/sample (1 or 2) */
|
||||
static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
|
||||
static unsigned char pcm_filter = 0; /* filter FLAG */
|
||||
static unsigned char pcm_mode = PCM_NON;
|
||||
static unsigned long pcm_count = 0;
|
||||
static unsigned short pcm_bitsok = 8; /* mask of OK bits */
|
||||
static int my_devnum = 0;
|
||||
|
||||
int
|
||||
pcm_set_speed (int arg)
|
||||
{
|
||||
int foo, tmp;
|
||||
unsigned long flags;
|
||||
|
||||
if (arg > 44100)
|
||||
arg = 44100;
|
||||
if (arg < 5000)
|
||||
arg = 5000;
|
||||
|
||||
foo = 1193180 / arg;
|
||||
arg = 1193180 / foo;
|
||||
|
||||
if (pcm_channels & 2)
|
||||
foo = foo >> 1;
|
||||
|
||||
pcm_speed = arg;
|
||||
|
||||
tmp = pas_read (FILTER_FREQUENCY);
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
|
||||
pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
|
||||
pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
|
||||
pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
|
||||
pas_write (tmp, FILTER_FREQUENCY);
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return pcm_speed;
|
||||
}
|
||||
|
||||
int
|
||||
pcm_set_channels (int arg)
|
||||
{
|
||||
|
||||
if ((arg != 1) && (arg != 2))
|
||||
return pcm_channels;
|
||||
|
||||
if (arg != pcm_channels)
|
||||
{
|
||||
pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
|
||||
|
||||
pcm_channels = arg;
|
||||
pcm_set_speed (pcm_speed);/* The speed must be reinitialized */
|
||||
}
|
||||
|
||||
return pcm_channels;
|
||||
}
|
||||
|
||||
int
|
||||
pcm_set_bits (int arg)
|
||||
{
|
||||
if ((arg & pcm_bitsok) != arg)
|
||||
return pcm_bits;
|
||||
|
||||
if (arg != pcm_bits)
|
||||
{
|
||||
pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
|
||||
|
||||
pcm_bits = arg;
|
||||
}
|
||||
|
||||
return pcm_bits;
|
||||
}
|
||||
|
||||
static int
|
||||
pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
|
||||
{
|
||||
TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case SOUND_PCM_WRITE_RATE:
|
||||
if (local)
|
||||
return pcm_set_speed (arg);
|
||||
return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg)));
|
||||
break;
|
||||
|
||||
case SOUND_PCM_READ_RATE:
|
||||
if (local)
|
||||
return pcm_speed;
|
||||
return IOCTL_OUT (arg, pcm_speed);
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_STEREO:
|
||||
if (local)
|
||||
return pcm_set_channels (arg + 1) - 1;
|
||||
return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1);
|
||||
break;
|
||||
|
||||
case SOUND_PCM_WRITE_CHANNELS:
|
||||
return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
|
||||
break;
|
||||
|
||||
case SOUND_PCM_READ_CHANNELS:
|
||||
if (local)
|
||||
return pcm_channels;
|
||||
return IOCTL_OUT (arg, pcm_channels);
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_SAMPLESIZE:
|
||||
if (local)
|
||||
return pcm_set_bits (arg);
|
||||
return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg)));
|
||||
break;
|
||||
|
||||
case SOUND_PCM_READ_BITS:
|
||||
if (local)
|
||||
return pcm_bits;
|
||||
return IOCTL_OUT (arg, pcm_bits);
|
||||
|
||||
case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */
|
||||
if (IOCTL_IN (arg) > 1)
|
||||
return IOCTL_OUT (arg, RET_ERROR (EINVAL));
|
||||
break;
|
||||
|
||||
pcm_filter = IOCTL_IN (arg);
|
||||
case SOUND_PCM_READ_FILTER:
|
||||
return IOCTL_OUT (arg, pcm_filter);
|
||||
break;
|
||||
|
||||
default:
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
static void
|
||||
pas_pcm_reset (int dev)
|
||||
{
|
||||
TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
|
||||
|
||||
pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
|
||||
}
|
||||
|
||||
static int
|
||||
pas_pcm_open (int dev, int mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
|
||||
|
||||
if (mode != OPEN_READ && mode != OPEN_WRITE)
|
||||
{
|
||||
printk ("PAS2: Attempt to open PCM device for simultaneous read and write");
|
||||
return RET_ERROR (EINVAL);
|
||||
}
|
||||
|
||||
if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
|
||||
return err;
|
||||
|
||||
if (!DMAbuf_open_dma (dev))
|
||||
{
|
||||
pas_remove_intr (PAS_PCM_INTRBITS);
|
||||
return RET_ERROR (EBUSY);
|
||||
}
|
||||
|
||||
pcm_count = 0;
|
||||
|
||||
pcm_set_bits (8);
|
||||
pcm_set_channels (1);
|
||||
pcm_set_speed (DSP_DEFAULT_SPEED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pas_pcm_close (int dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
pas_pcm_reset (dev);
|
||||
DMAbuf_close_dma (dev);
|
||||
pas_remove_intr (PAS_PCM_INTRBITS);
|
||||
pcm_mode = PCM_NON;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
static void
|
||||
pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag)
|
||||
{
|
||||
unsigned long flags, cnt;
|
||||
|
||||
TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
|
||||
|
||||
cnt = count;
|
||||
if (sound_dsp_dmachan[dev] > 3)
|
||||
cnt >>= 1;
|
||||
cnt--;
|
||||
|
||||
if (sound_dma_automode[dev] &&
|
||||
intrflag &&
|
||||
cnt == pcm_count)
|
||||
return; /* Auto mode on. No need to react */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
|
||||
PCM_CONTROL);
|
||||
|
||||
DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
|
||||
|
||||
if (sound_dsp_dmachan[dev] > 3)
|
||||
count >>= 1;
|
||||
count--;
|
||||
|
||||
if (count != pcm_count)
|
||||
{
|
||||
pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
|
||||
pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
|
||||
pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
|
||||
pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
|
||||
pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
|
||||
|
||||
pcm_count = count;
|
||||
}
|
||||
pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
|
||||
pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
|
||||
|
||||
pcm_mode = PCM_DAC;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
static void
|
||||
pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag)
|
||||
{
|
||||
unsigned long flags;
|
||||
int cnt;
|
||||
|
||||
TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
|
||||
|
||||
cnt = count;
|
||||
if (sound_dsp_dmachan[dev] > 3)
|
||||
cnt >>= 1;
|
||||
cnt--;
|
||||
|
||||
if (sound_dma_automode[my_devnum] &&
|
||||
intrflag &&
|
||||
cnt == pcm_count)
|
||||
return; /* Auto mode on. No need to react */
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
|
||||
|
||||
if (sound_dsp_dmachan[dev] > 3)
|
||||
count >>= 1;
|
||||
|
||||
count--;
|
||||
|
||||
if (count != pcm_count)
|
||||
{
|
||||
pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
|
||||
pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
|
||||
pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
|
||||
pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
|
||||
pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
|
||||
|
||||
pcm_count = count;
|
||||
}
|
||||
pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
|
||||
pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
|
||||
|
||||
pcm_mode = PCM_ADC;
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
}
|
||||
|
||||
static int
|
||||
pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct audio_operations pas_pcm_operations =
|
||||
{
|
||||
"Pro Audio Spectrum",
|
||||
pas_pcm_open, /* */
|
||||
pas_pcm_close, /* */
|
||||
pas_pcm_output_block, /* */
|
||||
pas_pcm_start_input, /* */
|
||||
pas_pcm_ioctl, /* */
|
||||
pas_pcm_prepare_for_input, /* */
|
||||
pas_pcm_prepare_for_output, /* */
|
||||
pas_pcm_reset, /* */
|
||||
pas_pcm_reset, /* halt_xfer */
|
||||
NULL, /* has_output_drained */
|
||||
NULL /* copy_from_user */
|
||||
};
|
||||
|
||||
long
|
||||
pas_pcm_init (long mem_start, struct address_info *hw_config)
|
||||
{
|
||||
TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
|
||||
|
||||
pcm_bitsok = 8;
|
||||
if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
|
||||
pcm_bitsok |= 16;
|
||||
|
||||
pcm_set_speed (DSP_DEFAULT_SPEED);
|
||||
|
||||
if (num_dspdevs < MAX_DSP_DEV)
|
||||
{
|
||||
dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations;
|
||||
sound_dsp_dmachan[my_devnum] = hw_config->dma;
|
||||
if (hw_config->dma > 3)
|
||||
{
|
||||
sound_buffcounts[my_devnum] = 1;
|
||||
sound_buffsizes[my_devnum] = 2 * 65536;
|
||||
sound_dma_automode[my_devnum] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sound_buffcounts[my_devnum] = 1;
|
||||
sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
|
||||
sound_dma_automode[my_devnum] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
printk ("PAS2: Too many PCM devices available\n");
|
||||
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
void
|
||||
pas_pcm_interrupt (unsigned char status, int cause)
|
||||
{
|
||||
if (cause == 1) /* PCM buffer done */
|
||||
{
|
||||
/*
|
||||
* Halt the PCM first. Otherwise we don't have time to start a new
|
||||
* block before the PCM chip proceeds to the next sample
|
||||
*/
|
||||
|
||||
if (!sound_dma_automode[my_devnum])
|
||||
{
|
||||
pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
|
||||
PCM_CONTROL);
|
||||
}
|
||||
|
||||
switch (pcm_mode)
|
||||
{
|
||||
|
||||
case PCM_DAC:
|
||||
DMAbuf_outputintr (my_devnum);
|
||||
break;
|
||||
|
||||
case PCM_ADC:
|
||||
DMAbuf_inputintr (my_devnum);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk ("PAS: Unexpected PCM interrupt\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
239
sys/i386/isa/sound/patmgr.c
Normal file
239
sys/i386/isa/sound/patmgr.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* linux/kernel/chr_drv/sound/patmgr.c
|
||||
*
|
||||
* The patch maneger interface for the /dev/sequencer
|
||||
*
|
||||
* (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#define PATMGR_C
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER)
|
||||
|
||||
DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV],
|
||||
server_wait_flag[MAX_SYNTH_DEV]);
|
||||
|
||||
static struct patmgr_info *mbox[MAX_SYNTH_DEV] =
|
||||
{NULL};
|
||||
static volatile int msg_direction[MAX_SYNTH_DEV] =
|
||||
{0};
|
||||
|
||||
static int pmgr_opened[MAX_SYNTH_DEV] =
|
||||
{0};
|
||||
|
||||
#define A_TO_S 1
|
||||
#define S_TO_A 2
|
||||
|
||||
DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag);
|
||||
|
||||
int
|
||||
pmgr_open (int dev)
|
||||
{
|
||||
if (dev < 0 || dev >= num_synths)
|
||||
return RET_ERROR (ENXIO);
|
||||
|
||||
if (pmgr_opened[dev])
|
||||
return RET_ERROR (EBUSY);
|
||||
pmgr_opened[dev] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pmgr_release (int dev)
|
||||
{
|
||||
|
||||
if (mbox[dev]) /* Killed in action. Inform the client */
|
||||
{
|
||||
|
||||
mbox[dev]->key = PM_ERROR;
|
||||
mbox[dev]->parm1 = RET_ERROR (EIO);
|
||||
|
||||
if (appl_wait_flag)
|
||||
WAKE_UP (appl_proc);
|
||||
}
|
||||
|
||||
pmgr_opened[dev] = 0;
|
||||
}
|
||||
|
||||
int
|
||||
pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ok = 0;
|
||||
|
||||
if (count != sizeof (struct patmgr_info))
|
||||
{
|
||||
printk ("PATMGR%d: Invalid read count\n", dev);
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
while (!ok && !PROCESS_ABORTING)
|
||||
{
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && !PROCESS_ABORTING)
|
||||
{
|
||||
INTERRUPTIBLE_SLEEP_ON (server_procs[dev], server_wait_flag[dev]);
|
||||
}
|
||||
|
||||
if (mbox[dev] && msg_direction[dev] == A_TO_S)
|
||||
{
|
||||
COPY_TO_USER (buf, 0, (char *) mbox[dev], count);
|
||||
msg_direction[dev] = 0;
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
return RET_ERROR (EINTR);
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (count < 4)
|
||||
{
|
||||
printk ("PATMGR%d: Write count < 4\n", dev);
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
COPY_FROM_USER (mbox[dev], buf, 0, 4);
|
||||
|
||||
if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
|
||||
{
|
||||
int tmp_dev;
|
||||
|
||||
tmp_dev = ((unsigned short *) mbox[dev])[2];
|
||||
if (tmp_dev != dev)
|
||||
return RET_ERROR (ENXIO);
|
||||
|
||||
return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev],
|
||||
buf, 4, count, 1);
|
||||
}
|
||||
|
||||
if (count != sizeof (struct patmgr_info))
|
||||
{
|
||||
printk ("PATMGR%d: Invalid write count\n", dev);
|
||||
return RET_ERROR (EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
* If everything went OK, there should be a preallocated buffer in the
|
||||
* mailbox and a client waiting.
|
||||
*/
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (mbox[dev] && !msg_direction[dev])
|
||||
{
|
||||
COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
|
||||
msg_direction[dev] = S_TO_A;
|
||||
|
||||
if (appl_wait_flag)
|
||||
{
|
||||
WAKE_UP (appl_proc);
|
||||
}
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
pmgr_access (int dev, struct patmgr_info *rec)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (mbox[dev])
|
||||
printk (" PATMGR: Server %d mbox full. Why?\n", dev);
|
||||
else
|
||||
{
|
||||
rec->key = PM_K_COMMAND;
|
||||
mbox[dev] = rec;
|
||||
msg_direction[dev] = A_TO_S;
|
||||
|
||||
if (server_wait_flag[dev])
|
||||
{
|
||||
WAKE_UP (server_procs[dev]);
|
||||
}
|
||||
|
||||
INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag);
|
||||
|
||||
if (msg_direction[dev] != S_TO_A)
|
||||
{
|
||||
rec->key = PM_ERROR;
|
||||
rec->parm1 = RET_ERROR (EIO);
|
||||
}
|
||||
else if (rec->key == PM_ERROR)
|
||||
{
|
||||
err = rec->parm1;
|
||||
if (err > 0)
|
||||
err = -err;
|
||||
}
|
||||
|
||||
mbox[dev] = NULL;
|
||||
msg_direction[dev] = 0;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
|
||||
unsigned long p3, unsigned long p4)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
if (!pmgr_opened[dev])
|
||||
return 0;
|
||||
|
||||
DISABLE_INTR (flags);
|
||||
|
||||
if (mbox[dev])
|
||||
printk (" PATMGR: Server %d mbox full. Why?\n", dev);
|
||||
else
|
||||
{
|
||||
mbox[dev] =
|
||||
(struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info));
|
||||
|
||||
mbox[dev]->key = PM_K_EVENT;
|
||||
mbox[dev]->command = event;
|
||||
mbox[dev]->parm1 = p1;
|
||||
mbox[dev]->parm2 = p2;
|
||||
mbox[dev]->parm3 = p3;
|
||||
msg_direction[dev] = A_TO_S;
|
||||
|
||||
if (server_wait_flag[dev])
|
||||
{
|
||||
WAKE_UP (server_procs[dev]);
|
||||
}
|
||||
|
||||
INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag);
|
||||
if (mbox[dev])
|
||||
KERNEL_FREE (mbox[dev]);
|
||||
mbox[dev] = NULL;
|
||||
msg_direction[dev] = 0;
|
||||
}
|
||||
|
||||
RESTORE_INTR (flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
155
sys/i386/isa/sound/pro_midi.c
Normal file
155
sys/i386/isa/sound/pro_midi.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/* UWM -- comments to soft-eng@cs.uwm.edu */
|
||||
#define ALL_EXTERNAL_TO_ME
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#include "pas.h"
|
||||
#define ESUCCESS 0
|
||||
|
||||
#if !defined(EXCLUDE_PRO_MIDI) && !defined(EXCLUDE_CHIP_MIDI)
|
||||
|
||||
|
||||
/** Structure for handling operations **/
|
||||
|
||||
|
||||
static struct generic_midi_operations pro_midi_operations = {
|
||||
|
||||
{"Pro_Audio_Spectrum 16 MV101", 0},
|
||||
pro_midi_open,
|
||||
pro_midi_close,
|
||||
pro_midi_write,
|
||||
pro_midi_read
|
||||
};
|
||||
|
||||
/*
|
||||
* Note! Note! Note!
|
||||
* Follow the same model for any other attach function you
|
||||
* may write
|
||||
*/
|
||||
|
||||
long pro_midi_attach( long mem_start)
|
||||
{
|
||||
pro_midi_dev = num_generic_midis;
|
||||
generic_midi_devs[num_generic_midis++] = &pro_midi_operations;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
int pro_midi_open(int dev, int mode)
|
||||
{
|
||||
|
||||
int intr_mask, s;
|
||||
|
||||
|
||||
s = splhigh();
|
||||
|
||||
|
||||
/* Reset the input and output FIFO pointers */
|
||||
|
||||
|
||||
outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
|
||||
|
||||
/* Get the interrupt status */
|
||||
|
||||
intr_mask = inb(INTERRUPT_MASK);
|
||||
|
||||
|
||||
/* Enable MIDI IRQ */
|
||||
|
||||
intr_mask |= I_M_MIDI_IRQ_ENABLE;
|
||||
outb(INTERRUPT_MASK, intr_mask);
|
||||
|
||||
|
||||
/* Enable READ/WRITE on MIDI port. This part is quite unsure though */
|
||||
|
||||
outb(MIDI_CONTROL,M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ);
|
||||
|
||||
/* Acknowledge pending interrupts */
|
||||
|
||||
outb(MIDI_STATUS,0xff);
|
||||
|
||||
|
||||
splx(s);
|
||||
|
||||
return(ESUCCESS);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void pro_midi_close(int dev)
|
||||
{
|
||||
|
||||
int intr_mask;
|
||||
|
||||
/* Clean up */
|
||||
|
||||
outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
|
||||
intr_mask = inb(INTERRUPT_MASK);
|
||||
intr_mask &= ~I_M_MIDI_IRQ_ENABLE;
|
||||
outb(INTERRUPT_MASK,intr_mask);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int pro_midi_write(int dev, struct uio *uio)
|
||||
{
|
||||
|
||||
int s;
|
||||
unsigned char data;
|
||||
|
||||
/* printf("midi: Going to do write routine..\n"); */
|
||||
while(uio->uio_resid) {
|
||||
|
||||
if ( uiomove(&data,1,uio) ) return(ENOTTY);
|
||||
|
||||
s = splhigh();
|
||||
|
||||
DELAY(30);
|
||||
outb(MIDI_DATA,data);
|
||||
DELAY(70); /* Ze best pause.. find a better one if
|
||||
* you can :)
|
||||
*/
|
||||
splx(s);
|
||||
}
|
||||
|
||||
return(ESUCCESS);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int pro_midi_read(int dev, struct uio *uio)
|
||||
{
|
||||
|
||||
int s;
|
||||
unsigned char data;
|
||||
|
||||
s = splhigh();
|
||||
|
||||
/* For each uio_iov[] entry .... */
|
||||
|
||||
while (uio->uio_resid) {
|
||||
|
||||
if((( inb(MIDI_STATUS) & M_S_INPUT_AVAIL) == 0 ) &&
|
||||
((inb(MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0 ) )
|
||||
|
||||
data = 0xfe;
|
||||
else
|
||||
data = inb(MIDI_DATA);
|
||||
|
||||
if ( uiomove(&data, 1 , uio)) {
|
||||
|
||||
printf("midi: Bad copyout()!\n");
|
||||
return(ENOTTY);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
splx(s);
|
||||
return(ESUCCESS);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
33
sys/i386/isa/sound/sb_card.c
Normal file
33
sys/i386/isa/sound/sb_card.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
/*
|
||||
* linux/kernel/chr_drv/sound/sb_card.c
|
||||
*
|
||||
* Detection routine for the SoundBlaster cards.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further
|
||||
* details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
|
||||
|
||||
long
|
||||
attach_sb_card (long mem_start, struct address_info *hw_config)
|
||||
{
|
||||
#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI)
|
||||
if (!sb_dsp_detect (hw_config))
|
||||
return mem_start;
|
||||
mem_start = sb_dsp_init (mem_start, hw_config);
|
||||
#endif
|
||||
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
int
|
||||
probe_sb (struct address_info *hw_config)
|
||||
{
|
||||
return sb_dsp_detect (hw_config);
|
||||
}
|
||||
|
||||
#endif
|
1303
sys/i386/isa/sound/sb_dsp.c
Normal file
1303
sys/i386/isa/sound/sb_dsp.c
Normal file
File diff suppressed because it is too large
Load diff
1137
sys/i386/isa/sound/sequencer.c
Normal file
1137
sys/i386/isa/sound/sequencer.c
Normal file
File diff suppressed because it is too large
Load diff
183
sys/i386/isa/sound/sound_calls.h
Normal file
183
sys/i386/isa/sound/sound_calls.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* DMA buffer calls
|
||||
*/
|
||||
|
||||
int DMAbuf_open(int dev, int mode);
|
||||
int DMAbuf_release(int dev, int mode);
|
||||
int DMAbuf_read (int dev, snd_rw_buf *user_buf, int count);
|
||||
int DMAbuf_getwrbuffer(int dev, char **buf, int *size);
|
||||
int DMAbuf_getrdbuffer(int dev, char **buf, int *len);
|
||||
int DMAbuf_rmchars(int dev, int buff_no, int c);
|
||||
int DMAbuf_start_output(int dev, int buff_no, int l);
|
||||
int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local);
|
||||
long DMAbuf_init(long mem_start);
|
||||
int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
|
||||
int DMAbuf_open_dma (int chan);
|
||||
void DMAbuf_close_dma (int chan);
|
||||
void DMAbuf_reset_dma (int chan);
|
||||
void DMAbuf_inputintr(int dev);
|
||||
void DMAbuf_outputintr(int dev);
|
||||
|
||||
/*
|
||||
* System calls for the /dev/dsp
|
||||
*/
|
||||
|
||||
int dsp_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int dsp_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int dsp_open (int dev, struct fileinfo *file, int bits);
|
||||
void dsp_release (int dev, struct fileinfo *file);
|
||||
int dsp_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg);
|
||||
int dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
|
||||
long dsp_init (long mem_start);
|
||||
|
||||
/*
|
||||
* System calls for the /dev/audio
|
||||
*/
|
||||
|
||||
int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int audio_open (int dev, struct fileinfo *file);
|
||||
void audio_release (int dev, struct fileinfo *file);
|
||||
int audio_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg);
|
||||
int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
|
||||
long audio_init (long mem_start);
|
||||
|
||||
/*
|
||||
* System calls for the /dev/sequencer
|
||||
*/
|
||||
|
||||
int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int sequencer_open (int dev, struct fileinfo *file);
|
||||
void sequencer_release (int dev, struct fileinfo *file);
|
||||
int sequencer_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg);
|
||||
int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
|
||||
long sequencer_init (long mem_start);
|
||||
void sequencer_midi_input(int dev, unsigned char data);
|
||||
void sequencer_midi_output(int dev);
|
||||
void sequencer_timer(void);
|
||||
int note_to_freq(int note_num);
|
||||
unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
|
||||
|
||||
#ifdef ALLOW_SELECT
|
||||
int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* System calls for the /dev/midi
|
||||
*/
|
||||
|
||||
int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int MIDIbuf_open (int dev, struct fileinfo *file);
|
||||
void MIDIbuf_release (int dev, struct fileinfo *file);
|
||||
int MIDIbuf_ioctl (int dev, struct fileinfo *file,
|
||||
unsigned int cmd, unsigned int arg);
|
||||
int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
|
||||
void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
|
||||
long MIDIbuf_init(long mem_start);
|
||||
|
||||
/*
|
||||
* System calls for the generic midi interface.
|
||||
*
|
||||
*/
|
||||
|
||||
long CMIDI_init (long mem_start);
|
||||
int CMIDI_open (int dev, struct fileinfo *file);
|
||||
int CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
|
||||
int CMIDI_close (int dev, struct fileinfo *file);
|
||||
|
||||
/*
|
||||
*
|
||||
* Misc calls from various sources
|
||||
*/
|
||||
|
||||
/* From pro_midi.c */
|
||||
|
||||
long pro_midi_attach(long mem_start);
|
||||
int pro_midi_open(int dev, int mode);
|
||||
void pro_midi_close(int dev);
|
||||
int pro_midi_write(int dev, struct uio *uio);
|
||||
int pro_midi_read(int dev, struct uio *uio);
|
||||
|
||||
/* From soundcard.c */
|
||||
long soundcard_init(long mem_start);
|
||||
void tenmicrosec(void);
|
||||
void request_sound_timer (int count);
|
||||
void sound_stop_timer(void);
|
||||
int snd_ioctl_return(int *addr, int value);
|
||||
|
||||
/* From sb_dsp.c */
|
||||
int sb_dsp_detect (struct address_info *hw_config);
|
||||
long sb_dsp_init (long mem_start, struct address_info *hw_config);
|
||||
void sb_dsp_disable_midi(void);
|
||||
|
||||
/* From opl3.c */
|
||||
int opl3_detect (int ioaddr);
|
||||
long opl3_init(long mem_start);
|
||||
|
||||
/* From sb_card.c */
|
||||
long attach_sb_card(long mem_start, struct address_info *hw_config);
|
||||
int probe_sb(struct address_info *hw_config);
|
||||
|
||||
/* From adlib_card.c */
|
||||
long attach_adlib_card(long mem_start, struct address_info *hw_config);
|
||||
int probe_adlib(struct address_info *hw_config);
|
||||
|
||||
/* From pas_card.c */
|
||||
long attach_pas_card(long mem_start, struct address_info *hw_config);
|
||||
int probe_pas(struct address_info *hw_config);
|
||||
int pas_set_intr(int mask);
|
||||
int pas_remove_intr(int mask);
|
||||
unsigned char pas_read(int ioaddr);
|
||||
void pas_write(unsigned char data, int ioaddr);
|
||||
|
||||
/* From pas_audio.c */
|
||||
void pas_pcm_interrupt(unsigned char status, int cause);
|
||||
long pas_pcm_init(long mem_start, struct address_info *hw_config);
|
||||
|
||||
/* From pas_mixer.c */
|
||||
int pas_init_mixer(void);
|
||||
|
||||
/* From pas_midi.c */
|
||||
long pas_midi_init(long mem_start);
|
||||
void pas_midi_interrupt(void);
|
||||
|
||||
/* From gus_card.c */
|
||||
long attach_gus_card(long mem_start, struct address_info * hw_config);
|
||||
int probe_gus(struct address_info *hw_config);
|
||||
int gus_set_midi_irq(int num);
|
||||
void gusintr(int);
|
||||
|
||||
/* From gus_wave.c */
|
||||
int gus_wave_detect(int baseaddr);
|
||||
long gus_wave_init(long mem_start, int irq, int dma);
|
||||
void gus_voice_irq(void);
|
||||
unsigned char gus_read8 (int reg);
|
||||
void gus_write8(int reg, unsigned char data);
|
||||
void guswave_dma_irq(void);
|
||||
void gus_delay(void);
|
||||
|
||||
/* From gus_midi.c */
|
||||
long gus_midi_init(long mem_start);
|
||||
void gus_midi_interrupt(int dummy);
|
||||
|
||||
/* From mpu401.c */
|
||||
long attach_mpu401(long mem_start, struct address_info * hw_config);
|
||||
int probe_mpu401(struct address_info *hw_config);
|
||||
|
||||
/* From opl3.c */
|
||||
void enable_opl3_mode(int left, int right, int both);
|
||||
|
||||
/* From patmgr.c */
|
||||
int pmgr_open(int dev);
|
||||
void pmgr_release(int dev);
|
||||
int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
|
||||
int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
|
||||
int pmgr_access(int dev, struct patmgr_info *rec);
|
||||
int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2,
|
||||
unsigned long parm3, unsigned long parm4);
|
182
sys/i386/isa/sound/sound_config.h
Normal file
182
sys/i386/isa/sound/sound_config.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* linux/kernel/chr_drv/sound/sound-config.h
|
||||
|
||||
A driver for Soundcards, misc configuration parameters.
|
||||
|
||||
(C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) */
|
||||
|
||||
#include "local.h"
|
||||
|
||||
|
||||
#undef CONFIGURE_SOUNDCARD
|
||||
#undef DYNAMIC_BUFFER
|
||||
|
||||
#ifdef KERNEL_SOUNDCARD
|
||||
#define CONFIGURE_SOUNDCARD
|
||||
#define DYNAMIC_BUFFER
|
||||
#undef LOADABLE_SOUNDCARD
|
||||
#endif
|
||||
|
||||
#ifdef EXCLUDE_SEQUENCER
|
||||
#define EXCLUDE_MIDI
|
||||
#define EXCLUDE_YM3812
|
||||
#define EXCLUDE_OPL3
|
||||
#endif
|
||||
|
||||
/** UWM - new MIDI stuff **/
|
||||
|
||||
#ifdef EXCLUDE_CHIP_MIDI
|
||||
#define EXCLUDE_PRO_MIDI
|
||||
#endif
|
||||
|
||||
/** UWM - stuff **/
|
||||
|
||||
#if defined(EXCLUDE_SEQUENCER) && defined(EXCLUDE_AUDIO)
|
||||
#undef CONFIGURE_SOUNDCARD
|
||||
#endif
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
/* ****** IO-address, DMA and IRQ settings ****
|
||||
|
||||
If your card has nonstandard I/O address or IRQ number, change defines
|
||||
for the following settings in your kernel Makefile */
|
||||
|
||||
#ifndef SBC_BASE
|
||||
#define SBC_BASE 0x220 /* 0x220 is the factory default. */
|
||||
#endif
|
||||
|
||||
#ifndef SBC_IRQ
|
||||
#define SBC_IRQ 7 /* IQR7 is the factory default. */
|
||||
#endif
|
||||
|
||||
#ifndef SBC_DMA
|
||||
#define SBC_DMA 1
|
||||
#endif
|
||||
|
||||
#ifndef PAS_BASE
|
||||
#define PAS_BASE 0x388
|
||||
#endif
|
||||
|
||||
#ifndef PAS_IRQ
|
||||
#define PAS_IRQ 5
|
||||
#endif
|
||||
|
||||
#ifndef PAS_DMA
|
||||
#define PAS_DMA 3
|
||||
#endif
|
||||
|
||||
#ifndef GUS_BASE
|
||||
#define GUS_BASE 0x220
|
||||
#endif
|
||||
|
||||
#ifndef GUS_IRQ
|
||||
#define GUS_IRQ 15
|
||||
#endif
|
||||
|
||||
#ifndef GUS_MIDI_IRQ
|
||||
#define GUS_MIDI_IRQ GUS_IRQ
|
||||
#endif
|
||||
|
||||
#ifndef GUS_DMA
|
||||
#define GUS_DMA 6
|
||||
#endif
|
||||
|
||||
#ifndef MPU_BASE
|
||||
#define MPU_BASE 0x330
|
||||
#endif
|
||||
|
||||
#ifndef MPU_IRQ
|
||||
#define MPU_IRQ 6
|
||||
#endif
|
||||
|
||||
/************* PCM DMA buffer sizes *******************/
|
||||
|
||||
/* If you are using high playback or recording speeds, the default buffersize
|
||||
is too small. DSP_BUFFSIZE must be 64k or less.
|
||||
|
||||
A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and
|
||||
4k for SB.
|
||||
|
||||
If you change the DSP_BUFFSIZE, don't modify this file.
|
||||
Use the make config command instead. */
|
||||
|
||||
#ifndef DSP_BUFFSIZE
|
||||
#define DSP_BUFFSIZE (4096)
|
||||
#endif
|
||||
|
||||
#ifndef DSP_BUFFCOUNT
|
||||
#define DSP_BUFFCOUNT 2 /* 2 is recommended. */
|
||||
#endif
|
||||
|
||||
#define DMA_AUTOINIT 0x10
|
||||
|
||||
#define SND_MAJOR 14
|
||||
|
||||
#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
|
||||
|
||||
/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
|
||||
driver. (There is no need to alter this) */
|
||||
#define SEQ_MAX_QUEUE 512
|
||||
|
||||
#define SBFM_MAXINSTR (256) /* Size of the FM Instrument
|
||||
bank */
|
||||
/* 128 instruments for general MIDI setup and 16 unassigned */
|
||||
|
||||
#define SND_NDEVS 50 /* Number of supported devices */
|
||||
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
|
||||
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
|
||||
synthesizer and MIDI output) */
|
||||
#define SND_DEV_MIDIN 2 /* MIDI input /dev/midin (not implemented
|
||||
yet) */
|
||||
#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
|
||||
#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
|
||||
#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
|
||||
#define SND_DEV_STATUS 6 /* /dev/sndstatus */
|
||||
|
||||
/* UWM ... note add new MIDI devices here..
|
||||
* Also do not forget to add table midi_supported[]
|
||||
* Minor numbers for on-chip midi devices start from 15.. and
|
||||
* should be contiguous.. viz. 15,16,17....
|
||||
* Also note the max # of midi devices as MAX_MIDI_DEV
|
||||
*/
|
||||
|
||||
#define CMIDI_DEV_PRO 15 /* Chip midi device == /dev/pro_midi */
|
||||
|
||||
/*
|
||||
* Add other midis here...
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
*/
|
||||
|
||||
#define DSP_DEFAULT_SPEED 8000
|
||||
|
||||
#define ON 1
|
||||
#define OFF 0
|
||||
|
||||
#define MAX_DSP_DEV 3
|
||||
#define MAX_MIXER_DEV 1
|
||||
#define MAX_SYNTH_DEV 3
|
||||
#define MAX_MIDI_DEV 3
|
||||
|
||||
struct fileinfo {
|
||||
int mode; /* Open mode */
|
||||
};
|
||||
|
||||
struct address_info {
|
||||
int io_base;
|
||||
int irq;
|
||||
int dma;
|
||||
};
|
||||
|
||||
#define OPEN_READ 1
|
||||
#define OPEN_WRITE 2
|
||||
#define OPEN_READWRITE 3
|
||||
|
||||
#include "os.h"
|
||||
#include "sound_calls.h"
|
||||
#include "dev_table.h"
|
||||
#include "debug.h"
|
||||
|
||||
#endif
|
593
sys/i386/isa/sound/soundcard.c
Normal file
593
sys/i386/isa/sound/soundcard.c
Normal file
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
* sound/386bsd/soundcard.c
|
||||
*
|
||||
* Soundcard driver for 386BSD.
|
||||
*
|
||||
* (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi)
|
||||
* See COPYING for further details. Should be distributed with this file.
|
||||
*/
|
||||
|
||||
#include "sound_config.h"
|
||||
|
||||
#ifdef CONFIGURE_SOUNDCARD
|
||||
|
||||
#include "dev_table.h"
|
||||
|
||||
int __timeout_val = 0;
|
||||
int __process_aborting = 0;
|
||||
|
||||
u_int snd1mask;
|
||||
u_int snd2mask;
|
||||
u_int snd3mask;
|
||||
u_int snd4mask;
|
||||
u_int snd5mask;
|
||||
|
||||
struct sbc_device
|
||||
{
|
||||
int usecount;
|
||||
};
|
||||
|
||||
#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
|
||||
|
||||
static struct sbc_device sbc_devices[SND_NDEVS];
|
||||
static int timer_running = 0;
|
||||
|
||||
static int in_use = 0; /* Total # of open device files (excluding
|
||||
* minor 0) */
|
||||
|
||||
static int soundcards_installed = 0; /* Number of installed
|
||||
* soundcards */
|
||||
static int soundcard_configured = 0;
|
||||
extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
|
||||
extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
|
||||
extern int snd_raw_count[MAX_DSP_DEV];
|
||||
|
||||
static struct fileinfo files[SND_NDEVS];
|
||||
|
||||
int sndprobe (struct isa_device *dev);
|
||||
int sndattach (struct isa_device *dev);
|
||||
int sndopen (dev_t dev, int flags);
|
||||
int sndclose (dev_t dev, int flags);
|
||||
int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode);
|
||||
int sndread (int dev, struct uio *uio);
|
||||
int sndwrite (int dev, struct uio *uio);
|
||||
int sndselect (int dev, int rw);
|
||||
static void sound_mem_init(void);
|
||||
|
||||
int
|
||||
get_time()
|
||||
{
|
||||
extern struct timeval time;
|
||||
|
||||
return(time.tv_usec + (time.tv_sec*1000000));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sndread (int dev, struct uio *buf)
|
||||
{
|
||||
int count = buf->uio_resid;
|
||||
|
||||
dev = minor (dev);
|
||||
|
||||
DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count));
|
||||
|
||||
switch (dev & 0xff) /* Changed to 0xff from 0x0f */
|
||||
{
|
||||
case SND_DEV_AUDIO:
|
||||
FIX_RETURN (audio_read (dev, &files[dev], buf, count));
|
||||
break;
|
||||
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
FIX_RETURN (dsp_read (dev, &files[dev], buf, count));
|
||||
break;
|
||||
|
||||
case SND_DEV_SEQ:
|
||||
FIX_RETURN (sequencer_read (dev, &files[dev], buf, count));
|
||||
break;
|
||||
|
||||
#ifndef EXCLUDE_CHIP_MIDI
|
||||
case CMIDI_DEV_PRO:
|
||||
FIX_RETURN (CMIDI_read (dev, &files[dev], buf, count));
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef EXCLUDE_MPU401
|
||||
case SND_DEV_MIDIN:
|
||||
FIX_RETURN (MIDIbuf_read (dev, &files[dev], buf, count));
|
||||
#endif
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
FIX_RETURN (-EPERM);
|
||||
}
|
||||
|
||||
int
|
||||
sndwrite (int dev, struct uio *buf)
|
||||
{
|
||||
int count = buf->uio_resid;
|
||||
|
||||
DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count));
|
||||
|
||||
dev = minor (dev);
|
||||
|
||||
switch (dev & 0xff) /* Changed to 0xff from 0x0f */
|
||||
{
|
||||
|
||||
case SND_DEV_SEQ:
|
||||
FIX_RETURN (sequencer_write (dev, &files[dev], buf, count));
|
||||
break;
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
FIX_RETURN (audio_write (dev, &files[dev], buf, count));
|
||||
break;
|
||||
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
FIX_RETURN (dsp_write (dev, &files[dev], buf, count));
|
||||
break;
|
||||
|
||||
#ifndef EXCLUDE_CHIP_MIDI
|
||||
case CMIDI_DEV_PRO:
|
||||
FIX_RETURN (CMIDI_write (dev, &files[dev], buf, count));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
FIX_RETURN (-EPERM);
|
||||
}
|
||||
|
||||
FIX_RETURN (count);
|
||||
}
|
||||
|
||||
int
|
||||
sndopen (dev_t dev, int flags)
|
||||
{
|
||||
int retval;
|
||||
|
||||
dev = minor (dev);
|
||||
|
||||
/* printf("SND: Minor number is now : %ld\n",dev); */
|
||||
|
||||
DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
|
||||
|
||||
if ((dev >= SND_NDEVS) || (dev < 0))
|
||||
{
|
||||
printk ("Invalid minor device %d\n", dev);
|
||||
FIX_RETURN (-ENODEV);
|
||||
}
|
||||
|
||||
if (!soundcard_configured && dev)
|
||||
{
|
||||
printk ("SoundCard Error: The soundcard system has not been configured\n");
|
||||
FIX_RETURN (-ENODEV);
|
||||
}
|
||||
|
||||
files[dev].mode = 0;
|
||||
|
||||
if (flags & FREAD && flags & FWRITE)
|
||||
files[dev].mode = OPEN_READWRITE;
|
||||
else if (flags & FREAD)
|
||||
files[dev].mode = OPEN_READ;
|
||||
else if (flags & FWRITE)
|
||||
files[dev].mode = OPEN_WRITE;
|
||||
|
||||
switch (dev & 0xff) /* Changed to 0xff from 0x0f */
|
||||
{
|
||||
case SND_DEV_CTL:
|
||||
if (!soundcards_installed)
|
||||
if (soundcard_configured)
|
||||
{
|
||||
printk ("Soundcard not installed\n");
|
||||
FIX_RETURN (-ENODEV);
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_DEV_SEQ:
|
||||
if ((retval = sequencer_open (dev, &files[dev])) < 0)
|
||||
FIX_RETURN (retval);
|
||||
break;
|
||||
|
||||
/** UWM stuff **/
|
||||
|
||||
#ifndef EXCLUDE_CHIP_MIDI
|
||||
case CMIDI_DEV_PRO:
|
||||
FIX_RETURN ( CMIDI_open (dev, &files[dev]) );
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef EXCLUDE_MPU401
|
||||
case SND_DEV_MIDIN:
|
||||
if ((retval = MIDIbuf_open (dev, &files[dev])) < 0)
|
||||
FIX_RETURN (retval);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
if ((retval = audio_open (dev, &files[dev])) < 0)
|
||||
FIX_RETURN (retval);
|
||||
break;
|
||||
|
||||
case SND_DEV_DSP:
|
||||
if ((retval = dsp_open (dev, &files[dev], 8)) < 0)
|
||||
FIX_RETURN (retval);
|
||||
break;
|
||||
|
||||
case SND_DEV_DSP16:
|
||||
if ((retval = dsp_open (dev, &files[dev], 16)) < 0)
|
||||
FIX_RETURN (retval);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk ("Invalid minor device %d\n", dev);
|
||||
FIX_RETURN (-ENODEV);
|
||||
}
|
||||
|
||||
sbc_devices[dev].usecount++;
|
||||
in_use++;
|
||||
|
||||
FIX_RETURN (0);
|
||||
}
|
||||
|
||||
int
|
||||
sndclose (dev_t dev, int flags)
|
||||
{
|
||||
|
||||
dev = minor (dev);
|
||||
|
||||
DEB (printk ("sound_release(dev=%d)\n", dev));
|
||||
|
||||
switch (dev & 0xff) /* Changed to 0xff from 0x0f */
|
||||
{
|
||||
case SND_DEV_SEQ:
|
||||
sequencer_release (dev, &files[dev]);
|
||||
break;
|
||||
|
||||
#ifndef EXCLUDE_CHIP_MIDI
|
||||
case CMIDI_DEV_PRO:
|
||||
CMIDI_close (dev, &files[dev]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_MPU401
|
||||
case SND_DEV_MIDIN:
|
||||
MIDIbuf_release (dev, &files[dev]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
audio_release (dev, &files[dev]);
|
||||
break;
|
||||
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
dsp_release (dev, &files[dev]);
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
sbc_devices[dev].usecount--;
|
||||
in_use--; /* If not control port */
|
||||
|
||||
FIX_RETURN (0);
|
||||
}
|
||||
|
||||
int
|
||||
sndioctl (dev_t dev, int cmd, caddr_t arg, int mode)
|
||||
{
|
||||
dev = minor (dev);
|
||||
|
||||
DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
|
||||
|
||||
switch (dev & 0x0f)
|
||||
{
|
||||
|
||||
case SND_DEV_CTL:
|
||||
if (!num_mixers)
|
||||
FIX_RETURN (-ENODEV);
|
||||
|
||||
if (dev >= num_mixers)
|
||||
FIX_RETURN (-ENODEV);
|
||||
|
||||
FIX_RETURN (mixer_devs[dev]->ioctl (dev, cmd, (unsigned int) arg));
|
||||
break;
|
||||
|
||||
case SND_DEV_SEQ:
|
||||
FIX_RETURN (sequencer_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
|
||||
break;
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
FIX_RETURN (audio_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
|
||||
break;
|
||||
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
FIX_RETURN (dsp_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
|
||||
break;
|
||||
|
||||
#ifndef EXCLUDE_MPU401
|
||||
case SND_DEV_MIDIN:
|
||||
FIX_RETURN (MIDIbuf_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
FIX_RETURN (-EPERM);
|
||||
break;
|
||||
}
|
||||
|
||||
FIX_RETURN (-EPERM);
|
||||
}
|
||||
|
||||
int
|
||||
sndselect (int dev, int rw)
|
||||
{
|
||||
dev = minor (dev);
|
||||
|
||||
DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
|
||||
|
||||
FIX_RETURN (0);
|
||||
}
|
||||
|
||||
static short
|
||||
ipri_to_irq (short ipri)
|
||||
{
|
||||
/*
|
||||
* Converts the ipri (bitmask) to the corresponding irq number
|
||||
*/
|
||||
int irq;
|
||||
|
||||
for (irq = 0; irq < 16; irq++)
|
||||
if (ipri == (1 << irq))
|
||||
return irq;
|
||||
|
||||
return -1; /* Invalid argument */
|
||||
}
|
||||
|
||||
int
|
||||
sndprobe (struct isa_device *dev)
|
||||
{
|
||||
struct address_info hw_config;
|
||||
|
||||
hw_config.io_base = dev->id_iobase;
|
||||
hw_config.irq = ipri_to_irq (dev->id_irq);
|
||||
hw_config.dma = dev->id_drq;
|
||||
|
||||
|
||||
return sndtable_probe (dev->id_unit, &hw_config);
|
||||
}
|
||||
|
||||
int
|
||||
sndattach (struct isa_device *dev)
|
||||
{
|
||||
int i;
|
||||
static int dsp_initialized = 0;
|
||||
static int midi_initialized = 0;
|
||||
static int seq_initialized = 0;
|
||||
static int generic_midi_initialized = 0;
|
||||
unsigned long mem_start = 0xefffffff;
|
||||
struct address_info hw_config;
|
||||
|
||||
hw_config.io_base = dev->id_iobase;
|
||||
hw_config.irq = ipri_to_irq (dev->id_irq);
|
||||
hw_config.dma = dev->id_drq;
|
||||
|
||||
if (dev->id_unit) /* Card init */
|
||||
if (!sndtable_init_card (dev->id_unit, &hw_config))
|
||||
{
|
||||
printf (" <Driver not configured>");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init the high level sound driver
|
||||
*/
|
||||
|
||||
if (!(soundcards_installed = sndtable_get_cardcount ()))
|
||||
{
|
||||
printf (" <No such hardware>");
|
||||
return FALSE; /* No cards detected */
|
||||
}
|
||||
|
||||
#ifndef EXCLUDE_AUDIO
|
||||
soundcard_configured = 1;
|
||||
if (num_dspdevs)
|
||||
sound_mem_init ();
|
||||
#endif
|
||||
|
||||
if (num_dspdevs && !dsp_initialized) /* Audio devices present */
|
||||
{
|
||||
dsp_initialized = 1;
|
||||
mem_start = DMAbuf_init (mem_start);
|
||||
mem_start = audio_init (mem_start);
|
||||
mem_start = dsp_init (mem_start);
|
||||
}
|
||||
|
||||
/** UWM stuff **/
|
||||
|
||||
#ifndef EXCLUDE_CHIP_MIDI
|
||||
|
||||
if (!generic_midi_initialized)
|
||||
{
|
||||
generic_midi_initialized = 1;
|
||||
mem_start = CMIDI_init (mem_start);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_MPU401
|
||||
if (num_midis && !midi_initialized)
|
||||
{
|
||||
midi_initialized = 1;
|
||||
mem_start = MIDIbuf_init (mem_start);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((num_midis + num_synths) && !seq_initialized)
|
||||
{
|
||||
seq_initialized = 1;
|
||||
mem_start = sequencer_init (mem_start);
|
||||
}
|
||||
|
||||
for (i = 0; i < SND_NDEVS; i++)
|
||||
{
|
||||
sbc_devices[i].usecount = 0;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
tenmicrosec (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
inb (0x80);
|
||||
}
|
||||
|
||||
#ifdef EXCLUDE_GUS
|
||||
void
|
||||
gusintr (int unit)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
request_sound_timer (int count)
|
||||
{
|
||||
static int current = 0;
|
||||
int tmp = count;
|
||||
|
||||
if (count < 0)
|
||||
timeout (sequencer_timer, 0, -count);
|
||||
else
|
||||
{
|
||||
|
||||
if (count < current)
|
||||
current = 0; /* Timer restarted */
|
||||
|
||||
count = count - current;
|
||||
|
||||
current = tmp;
|
||||
|
||||
if (!count)
|
||||
count = 1;
|
||||
|
||||
timeout (sequencer_timer, 0, count);
|
||||
}
|
||||
timer_running = 1;
|
||||
}
|
||||
|
||||
void
|
||||
sound_stop_timer (void)
|
||||
{
|
||||
if (timer_running)
|
||||
untimeout (sequencer_timer, 0);
|
||||
timer_running = 0;
|
||||
}
|
||||
|
||||
#ifndef EXCLUDE_AUDIO
|
||||
static void
|
||||
sound_mem_init (void)
|
||||
{
|
||||
int i, dev;
|
||||
unsigned long dma_pagesize;
|
||||
static unsigned long dsp_init_mask = 0;
|
||||
|
||||
for (dev = 0; dev < num_dspdevs; dev++) /* Enumerate devices */
|
||||
if (!(dsp_init_mask & (1 << dev))) /* Not already done */
|
||||
if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0)
|
||||
{
|
||||
dsp_init_mask |= (1 << dev);
|
||||
|
||||
if (sound_dma_automode[dev])
|
||||
{
|
||||
sound_dma_automode[dev] = 0; /* Not possible with 386BSD */
|
||||
}
|
||||
|
||||
if (sound_buffcounts[dev] == 1)
|
||||
{
|
||||
sound_buffcounts[dev] = 2;
|
||||
sound_buffsizes[dev] /= 2;
|
||||
}
|
||||
|
||||
if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */
|
||||
sound_buffsizes[dev] = 65536;
|
||||
|
||||
if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
|
||||
dma_pagesize = 131072; /* 128k */
|
||||
else
|
||||
dma_pagesize = 65536;
|
||||
|
||||
/* More sanity checks */
|
||||
|
||||
if (sound_buffsizes[dev] > dma_pagesize)
|
||||
sound_buffsizes[dev] = dma_pagesize;
|
||||
sound_buffsizes[dev] &= 0xfffff000; /* Truncate to n*4k */
|
||||
if (sound_buffsizes[dev] < 4096)
|
||||
sound_buffsizes[dev] = 4096;
|
||||
|
||||
/* Now allocate the buffers */
|
||||
|
||||
for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
|
||||
{
|
||||
/*
|
||||
* The DMA buffer allocation algorithm hogs memory. We allocate
|
||||
* a memory area which is two times the requires size. This
|
||||
* guarantees that it contains at least one valid DMA buffer.
|
||||
*
|
||||
* This really needs some kind of finetuning.
|
||||
*/
|
||||
char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT);
|
||||
unsigned long addr, rounded;
|
||||
|
||||
if (tmpbuf == NULL)
|
||||
{
|
||||
printk ("snd: Unable to allocate %d bytes of buffer\n",
|
||||
2 * sound_buffsizes[dev]);
|
||||
return;
|
||||
}
|
||||
|
||||
addr = kvtop (tmpbuf);
|
||||
/*
|
||||
* Align the start address
|
||||
*/
|
||||
rounded = (addr & ~(dma_pagesize - 1)) + dma_pagesize;
|
||||
|
||||
snd_raw_buf[dev][snd_raw_count[dev]] =
|
||||
&tmpbuf[rounded - addr]; /* Compute offset */
|
||||
/*
|
||||
* Use virtual address as the physical address, since
|
||||
* isa_dmastart performs the phys address computation.
|
||||
*/
|
||||
snd_raw_buf_phys[dev][snd_raw_count[dev]] =
|
||||
(unsigned long) snd_raw_buf[dev][snd_raw_count[dev]];
|
||||
}
|
||||
} /* for dev */
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct isa_driver snddriver =
|
||||
{sndprobe, sndattach, "snd"};
|
||||
|
||||
int
|
||||
snd_ioctl_return (int *addr, int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return value; /* Error */
|
||||
suword (addr, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
29
sys/i386/isa/sound/tuning.h
Normal file
29
sys/i386/isa/sound/tuning.h
Normal 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
|
69
sys/i386/isa/sound/ulaw.h
Normal file
69
sys/i386/isa/sound/ulaw.h
Normal 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,
|
||||
};
|
Loading…
Reference in a new issue