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:
Jordan K. Hubbard 1993-10-23 10:55:52 +00:00
parent dad544ec41
commit 6b8afe4d37
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=653
46 changed files with 15667 additions and 0 deletions

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

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

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

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

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

View 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

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

View 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

View 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

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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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"
};

File diff suppressed because it is too large Load diff

View 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);
}

View 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);
}

View 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);
}

View 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);
}

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

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

View 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

View 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

View 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 *) &params, 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

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

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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

View 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

View 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

View file

@ -0,0 +1,29 @@
#ifdef SEQUENCER_C
unsigned short semitone_tuning[24] =
{
/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
};
unsigned short cent_tuning[100] =
{
/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
/* 96 */ 10570, 10576, 10582, 10589
};
#else
extern unsigned short semitone_tuning[24];
extern unsigned short cent_tuning[100];
#endif

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

@ -0,0 +1,69 @@
static unsigned char ulaw_dsp[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2,
5, 9, 13, 17, 21, 25, 29, 33,
37, 41, 45, 49, 53, 57, 61, 65,
68, 70, 72, 74, 76, 78, 80, 82,
84, 86, 88, 90, 92, 94, 96, 98,
100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115,
115, 116, 116, 117, 117, 118, 118, 119,
119, 120, 120, 121, 121, 122, 122, 123,
123, 123, 124, 124, 124, 124, 125, 125,
125, 125, 126, 126, 126, 126, 127, 127,
127, 127, 127, 127, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
252, 248, 244, 240, 236, 232, 228, 224,
220, 216, 212, 208, 204, 200, 196, 192,
189, 187, 185, 183, 181, 179, 177, 175,
173, 171, 169, 167, 165, 163, 161, 159,
157, 156, 155, 154, 153, 152, 151, 150,
149, 148, 147, 146, 145, 144, 143, 142,
142, 141, 141, 140, 140, 139, 139, 138,
138, 137, 137, 136, 136, 135, 135, 134,
134, 134, 133, 133, 133, 133, 132, 132,
132, 132, 131, 131, 131, 131, 130, 130,
130, 130, 130, 130, 129, 129, 129, 129,
129, 129, 129, 129, 128, 128, 128, 128,
};
static unsigned char dsp_ulaw[] = {
31, 31, 31, 32, 32, 32, 32, 33,
33, 33, 33, 34, 34, 34, 34, 35,
35, 35, 35, 36, 36, 36, 36, 37,
37, 37, 37, 38, 38, 38, 38, 39,
39, 39, 39, 40, 40, 40, 40, 41,
41, 41, 41, 42, 42, 42, 42, 43,
43, 43, 43, 44, 44, 44, 44, 45,
45, 45, 45, 46, 46, 46, 46, 47,
47, 47, 47, 48, 48, 49, 49, 50,
50, 51, 51, 52, 52, 53, 53, 54,
54, 55, 55, 56, 56, 57, 57, 58,
58, 59, 59, 60, 60, 61, 61, 62,
62, 63, 63, 64, 65, 66, 67, 68,
69, 70, 71, 72, 73, 74, 75, 76,
77, 78, 79, 81, 83, 85, 87, 89,
91, 93, 95, 99, 103, 107, 111, 119,
255, 247, 239, 235, 231, 227, 223, 221,
219, 217, 215, 213, 211, 209, 207, 206,
205, 204, 203, 202, 201, 200, 199, 198,
197, 196, 195, 194, 193, 192, 191, 191,
190, 190, 189, 189, 188, 188, 187, 187,
186, 186, 185, 185, 184, 184, 183, 183,
182, 182, 181, 181, 180, 180, 179, 179,
178, 178, 177, 177, 176, 176, 175, 175,
175, 175, 174, 174, 174, 174, 173, 173,
173, 173, 172, 172, 172, 172, 171, 171,
171, 171, 170, 170, 170, 170, 169, 169,
169, 169, 168, 168, 168, 168, 167, 167,
167, 167, 166, 166, 166, 166, 165, 165,
165, 165, 164, 164, 164, 164, 163, 163,
163, 163, 162, 162, 162, 162, 161, 161,
161, 161, 160, 160, 160, 160, 159, 159,
};