Commit 3dd3f561 authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.31

parent cc8a3f45
VERSION = 1 VERSION = 1
PATCHLEVEL = 1 PATCHLEVEL = 1
SUBLEVEL = 30 SUBLEVEL = 31
all: Version zImage all: Version zImage
......
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
#define MAJOR_NR FLOPPY_MAJOR #define MAJOR_NR FLOPPY_MAJOR
#include "blk.h" #include "blk.h"
static unsigned int changed_floppies = 0, fake_change = 0; static unsigned int changed_floppies = 0, fake_change = 0, read_only = 0;
static int initial_reset_flag = 0; static int initial_reset_flag = 0;
static int need_configure = 1; /* for 82077 */ static int need_configure = 1; /* for 82077 */
...@@ -150,7 +150,7 @@ static unsigned char reply_buffer[MAX_REPLIES]; ...@@ -150,7 +150,7 @@ static unsigned char reply_buffer[MAX_REPLIES];
#define ST0 (reply_buffer[0]) #define ST0 (reply_buffer[0])
#define ST1 (reply_buffer[1]) #define ST1 (reply_buffer[1])
#define ST2 (reply_buffer[2]) #define ST2 (reply_buffer[2])
#define ST3 (reply_buffer[3]) #define ST3 (reply_buffer[0]) /* result of GETSTATUS */
/* /*
* This struct defines the different floppy types. * This struct defines the different floppy types.
...@@ -1037,8 +1037,20 @@ static void shake_one(void) ...@@ -1037,8 +1037,20 @@ static void shake_one(void)
output_byte(1); output_byte(1);
} }
static void check_readonly(void)
{
unsigned long mask = 1 << current_drive;
read_only &= ~mask;
output_byte(FD_GETSTATUS);
output_byte(current_drive);
if ((result() == 1) && (ST3 & ST3_RY) && !(ST3 & ST3_FT) && (ST3 & ST3_WP))
read_only |= mask;
}
static void floppy_ready(void) static void floppy_ready(void)
{ {
check_readonly();
if (inb(FD_DIR) & 0x80) { if (inb(FD_DIR) & 0x80) {
changed_floppies |= 1<<current_drive; changed_floppies |= 1<<current_drive;
buffer_track = -1; buffer_track = -1;
...@@ -1368,6 +1380,16 @@ static void config_types(void) ...@@ -1368,6 +1380,16 @@ static void config_types(void)
printk("\n"); printk("\n");
} }
static void floppy_release(struct inode * inode, struct file * filp)
{
fsync_dev(inode->i_rdev);
if (!fd_ref[inode->i_rdev & 3]--) {
printk("floppy_release with fd_ref == 0");
fd_ref[inode->i_rdev & 3] = 0;
}
floppy_release_irq_and_dma();
}
/* /*
* floppy_open check for aliasing (/dev/fd0 can be the same as * floppy_open check for aliasing (/dev/fd0 can be the same as
* /dev/PS0 etc), and disallows simultaneous access to the same * /dev/PS0 etc), and disallows simultaneous access to the same
...@@ -1390,19 +1412,16 @@ static int floppy_open(struct inode * inode, struct file * filp) ...@@ -1390,19 +1412,16 @@ static int floppy_open(struct inode * inode, struct file * filp)
buffer_drive = buffer_track = -1; buffer_drive = buffer_track = -1;
if (old_dev && old_dev != inode->i_rdev) if (old_dev && old_dev != inode->i_rdev)
invalidate_buffers(old_dev); invalidate_buffers(old_dev);
if (filp && filp->f_mode) if (filp && filp->f_mode) {
check_disk_change(inode->i_rdev); check_disk_change(inode->i_rdev);
return 0; if (filp->f_mode & 2) {
} if (1 & (read_only >> drive)) {
floppy_release(inode, filp);
static void floppy_release(struct inode * inode, struct file * filp) return -EACCES;
{ }
fsync_dev(inode->i_rdev); }
if (!fd_ref[inode->i_rdev & 3]--) {
printk("floppy_release with fd_ref == 0");
fd_ref[inode->i_rdev & 3] = 0;
} }
floppy_release_irq_and_dma(); return 0;
} }
static int check_floppy_change(dev_t dev) static int check_floppy_change(dev_t dev)
......
...@@ -38,7 +38,7 @@ static const char RCSid[] = "$Header:"; ...@@ -38,7 +38,7 @@ static const char RCSid[] = "$Header:";
* Time out in seconds for disks and Magneto-opticals (which are slower). * Time out in seconds for disks and Magneto-opticals (which are slower).
*/ */
#define SD_TIMEOUT 300 #define SD_TIMEOUT 600
#define SD_MOD_TIMEOUT 750 #define SD_MOD_TIMEOUT 750
#define CLUSTERABLE_DEVICE(SC) (SC->host->hostt->use_clustering && \ #define CLUSTERABLE_DEVICE(SC) (SC->host->hostt->use_clustering && \
......
...@@ -636,7 +636,6 @@ void sr_attach(Scsi_Device * SDp){ ...@@ -636,7 +636,6 @@ void sr_attach(Scsi_Device * SDp){
static void sr_init_done (Scsi_Cmnd * SCpnt) static void sr_init_done (Scsi_Cmnd * SCpnt)
{ {
struct request * req; struct request * req;
struct task_struct * p;
req = &SCpnt->request; req = &SCpnt->request;
req->dev = 0xfffe; /* Busy, but indicate request done */ req->dev = 0xfffe; /* Busy, but indicate request done */
......
...@@ -21,13 +21,12 @@ extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg); ...@@ -21,13 +21,12 @@ extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
static void sr_ioctl_done(Scsi_Cmnd * SCpnt) static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
{ {
struct request * req; struct request * req;
struct task_struct * p;
req = &SCpnt->request; req = &SCpnt->request;
req->dev = 0xfffe; /* Busy, but indicate request done */ req->dev = 0xfffe; /* Busy, but indicate request done */
if (req->sem != NULL) { if (req->sem != NULL) {
up(&req->sem); up(req->sem);
} }
} }
......
NOTE! This driver version is not compatible with the version 1.0c. NOTE!
This means you have to use the latest version of the snd-util
package (2.0). The earlier ones (from 1.0) will not work. If you have This is an ALPHA TEST VERSION (pre 3.0). The latest
other programs using ioctl calls of the driver, they must be released version of this driver is now part of
recompiled. Most of them will not work without some source Linux kernel distribution. For other operating systems
modifications. use the snd-driv-2.5.tar.gz package.
This particular version contains lots of new features
BUT THERE ARE NO APPLICATIONS WHICH USE THEM. So there
is no need to install this version as long as you are
not developing the driver or applications which use it.
All new features are in the /dev/sequencer and /dev/midi
parts of the driver.
This version is little bit incomplete. Some features have
not been implemented for each soundcards yet. All features
of v2.4 should work OK.
CAUTION!
This version of driver works with applications written and
compiled for v2.*. The problem is that APPLICATIONS COMPILED
WITH soundcard.h OF THIS VERSION WILL NOT WORK WITH OLDER DRIVER.
Be carefull when distributing applications compiled with this
version (just the apps using /dev/sequencer are incompatible).
Hannu
Changelog for version 2.5a Changelog for version 2.90
-------------------------- ------------------------------------
Since 2.5 This is an intermediate release (v3.0 prototype with some experimental
- Minor fix to read from /dev/sequencer features disabled). See experimental.txt for more info.
Since pre-3.0-949712
- GUS MAX support
- Partially working MSS/WSS support (could work with some cards).
- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
(GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
GUS MAX, but it doesn't work yet.
Since pre-3.0-940426
- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
This codec chip is used in various soundcards. This version is developed
for the 16 bit daughtercard of GUS. It should work with other cards also
if the following requirements are met:
- The I/O, IRQ and DMA settings are jumper selectable or
the card is initialized by booting DOS before booting Linux (etc.).
- You add the IO, IRQ and DMA settings manually to the local.h.
(Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
the base address bust be the base address of the codec chip not the
card itself. For the GUS16 these are the same but most MSS compatible
cards have the codec located at card_base+4.
- Some minor changes
Since 2.5 (******* MAJOR REWRITE ***********)
This version is based on v2.3. I have tried to maintain two versions
together so that this one should have the same features than v2.5.
Something may still be missing. If you notice such things, please let me
know.
The Readme.v30 contains more details.
- /dev/midi## devices.
- /dev/sequencer2
Since 2.5-beta2 Since 2.5-beta2
- Some fine tuning to the GUS v3.7 mixer code. - Some fine tuning to the GUS v3.7 mixer code.
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# #
# #
VERSION = 2.5a VERSION = 2.90
TARGET_OS = linux TARGET_OS = linux
.c.s: .c.s:
...@@ -19,7 +19,8 @@ OBJS = soundcard.o audio.o dmabuf.o sb_dsp.o dev_table.o \ ...@@ -19,7 +19,8 @@ OBJS = soundcard.o audio.o dmabuf.o sb_dsp.o dev_table.o \
opl3.o sequencer.o midibuf.o sb_card.o pas2_card.o adlib_card.o \ opl3.o sequencer.o midibuf.o sb_card.o pas2_card.o adlib_card.o \
pas2_pcm.o pas2_mixer.o pas2_midi.o gus_card.o gus_wave.o mpu401.o \ pas2_pcm.o pas2_mixer.o pas2_midi.o gus_card.o gus_wave.o mpu401.o \
gus_midi.o gus_vol.o patmgr.o sb_mixer.o sb16_dsp.o sb_midi.o \ gus_midi.o gus_vol.o patmgr.o sb_mixer.o sb16_dsp.o sb_midi.o \
sb16_midi.o sound_switch.o ics2101.o sb16_midi.o sound_switch.o midi_synth.o uart6850.o sound_timer.o \
sys_timer.o ics2101.o ad1848.o pss.o
all: local.h sound.a all: local.h sound.a
......
Release notes for the Linux Sound Driver 2.5a VoxWare v2.90 release notes
--------------------------------------------- --------------------------
This directory contains just the Linux version. The portable version
(SCO/ISC, FreeBSD/NetBSD and SVR4.2) is available from sunsite.unc.edu:
pub/Linux/kernel/sound. This version includes some hidden features which
are described in the file experimental.txt
This is mainly a bug fix release. There are couple of new things such as Some of these features are not enabled by default. Look at
linear volume mode for GUS and MIDI recording for SB 2.0 and SB Pro. experimental.txt for more info.
Also this version supports the mixer of GUS v3.7. (Support for GUS MAX and
the 16-bit daughtercard is coming sooner or later). I just decided to release this version with some
incompletely implemented features disabled since
NOTE! The sound driver is a part of the Linux kernel distribution also. there are some new features required by a popular
Check that your kernel doesn't have more recent version than this application. In addition there is also support
when installing a separately distributed sound driver. The for the GUS MAX and the 16 bit sampling option of GUS.
version number of this driver is defined in the makefile. Also the Windows Sound System stuff is there but may not
work yet (may work with some WSS compatible cards).
This version contains a driver for the SB16 also.
The SB16 driver requires separate DMA channels for the 8 and 16 bit ********* IMPORTANT *****************************************
modes. There should be a way to share the 8 bit DMA channels between Linux 1.0 or later is required to by this driver version.
these modes but this feature is not supported yet.
The SB16 DSP support is by Joerg Schubert (jsb@sth.ruhr-uni-bochum.de). Don't distribute binaries which use /dev/sequencer and are
compiled with the soundcard.h of this version. They will
The SB16 driver has also the Midi input capability even at the same not work with version 2.x of the driver.
time with the /dev/dsp. Also the WaveBlaster daughter board is supported. *************************************************************
No support for the ASP chip yet (the ASP chip can be installed but it's
not used by the driver).
You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z
packages to use this driver. They should be in the same packages to use this driver. They should be in the same
...@@ -31,122 +30,16 @@ ftp site or BBS from where you got this driver. For ...@@ -31,122 +30,16 @@ ftp site or BBS from where you got this driver. For
example at nic.funet.fi:pub/OS/Linux/*. example at nic.funet.fi:pub/OS/Linux/*.
If you are looking for the installation instructions, please If you are looking for the installation instructions, please
look at $OS/Readme. look at linux/Readme.
This version supports the following soundcards:
GUS, SoundBlaster, SB Pro, SB16, 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
Most of the features of the /dev/sequencer device file are
available just for GUS owners.
Compatibility with the earlier versions Compatibility with the earlier versions
--------------------------------------- ---------------------------------------
In this version the ultrasound.h no longer includes the sys/soundcard.h This version is backward compatible with the version 2.X. All programs
You have to change the gmod.c of the snd-util-2.0 package and to add an compiled with sys/soundcard.h of v2.X should work without problems.
include for it. PROGRAMS COMPILED WITH THE sys/soundcard.h OF THIS VERSION WILL NOT
WORK WITH v2.X DRIVER. BE CAREFULL WHEN DISTRIBUTING BINARIES COMPILED
IMPORTANT!!!!!!!!!!!!!!!!!!!!!! FOR THIS VERSION.
This version is not binary or source compatible with the version 1.0c.
The ioctl() interface has changed completely since version 1.0c. All
programs using this driver must be at least recompiled.
The snd-util-2.0 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.
The following ioctl calls have changed:
SNDCTL_SEQ_GETOUTCOUNT
SNDCTL_SEQ_GETINCOUNT
SNDCTL_SEQ_TESTMIDI
SNDCTL_DSP_SPEED
SNDCTL_DSP_STEREO
SNDCTL_DSP_GETBLKSIZE
SNDCTL_DSP_SAMPLESIZE
SOUND_PCM_WRITE_CHANNELS
SOUND_PCM_WRITE_FILTER
SOUND_PCM_READ_RATE
SOUND_PCM_READ_CHANNELS
SOUND_PCM_READ_BITS
SOUND_PCM_READ_FILTER
SOUND_PCM_WRITE_BITS
SOUND_PCM_WRITE_RATE
SOUND_MIXER_READ_* (several ones)
SOUND_MIXER_WRITE_* (several ones)
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-2.0 package for further info.
Known bugs/limitations
----------------------
- High speed recording of long audio samples (>20 second) to disk
is not possible. Everything works until next sync() which delays the
recording process too much. A delay longer than 0.1 to 0.3 seconds is
too much.
- The SB16 driver sometimes swaps the left and right channels together.
- 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 (like mine).
Try to avoid
switching between VTs while patches are being loaded to the GUS.
This problem disappears completely if you define GUS_PATCH_NO_DMA in the
local.h (after make config in linux). The drawback is that patch loading
without DMA takes several times longer than with DMA.
- There is a skeleton of the patch manager support. It don't work in
this version.
Future development
------------------
- Since this driver is no longer just the Linux Sound Driver, it's time
to give it a new name. I have planned to use name VoxWare.
- I'm writing a Hacker's guide to the VoxWare sound driver. Should
be ready within this(/next) year (alpha version).
- Completion of the ISC, SCO and BSD ports. Port to SVR4.2.
- I'm interested to implement/include support for new soundcards and
operating systems.
Hint for the soundcard and OS manufacturers:
I'm collecting soundcards (high end ones) and SDKs for them. In
addition I'm collecting PC operating systems. I will be happy if
somebody sends me such items. In addition such kind of donation
makes it easier to change the VoxWare driver to support your
soundcard or operating system. However, please contact me before
sending anything.
I will propably release some fix versions within this and next year. At
least when the non-Linux versions get ready. The next major release (3.0)
will be quite complete rewrite and released after about a year (end of 94 or
beginning of 95).
Contributors Contributors
------------ ------------
...@@ -155,30 +48,32 @@ This driver contains code by several contributors. In addition several other ...@@ -155,30 +48,32 @@ This driver contains code by several contributors. In addition several other
persons have given usefull suggestions. The following is a list of major persons have given usefull suggestions. The following is a list of major
contributors. (I could have forgotten some names.) contributors. (I could have forgotten some names.)
Craig Metz 1/2 of the PAS16 Mixer and PCM support Craig Metz 1/2 of the PAS16 Mixer and PCM support
Rob Hooft Volume computation algorithm for the FM synth. Rob Hooft Volume computation algorithm for the FM synth.
Mika Liljeberg uLaw encoding and decoding routines Mika Liljeberg uLaw encoding and decoding routines
Greg Lee Volume computation algorithm for the GUS and Greg Lee Volume computation algorithm for the GUS and
lot's of valuable suggestions. lot's of valuable suggestions.
Andy Warner Initial ISC port Andy Warner ISC port
Jim Lowe Initial FreeBSD port Jim Lowe FreeBSD port
Anders Baekgaard Bughunting and valuable suggestions. Anders Baekgaard Bughunting and valuable suggestions.
Joerg Schubert SB16 DSP support. Joerg Schubert SB16 DSP support.
Andrew Robinson Improvements to the GUS driver Andrew Robinson Improvements to the GUS driver
Megens SA MIDI recording for SB and SB Pro. Megens SA MIDI recording for SB and SB Pro.
Mikael Nordqvist Linear volume support for GUS.
Mikael Nordqvist Linear volume support for GUS. Mikael Nordqvist Linear volume support for GUS.
Ian Hartas SVR4.2 port Ian Hartas SVR4.2 port
Markus Aroharju and Markus Aroharju and
Risto Kankkunen Major contributions to the mixer support Risto Kankkunen Major contributions to the mixer support
of GUS v3.7. of GUS v3.7.
Hunyue Yau Sound Galaxy NX Pro mixer support. Marc Hoffman PSS support.
Regards, Regards,
Hannu Savolainen Hannu Savolainen
hannu@voxware.pp.fi, Hannu.Savolainen@Helsinki.fi hannu@voxware.pp.fi
Snail mail: Hannu Savolainen Snail mail: Hannu Savolainen
Pallaksentie 4 A 2 Pallaksentie 4 A 2
00970 Helsinki 00970 Helsinki
Finland Finland
FAX: +358 0 395 1968 (usually not connected)
Sound Driver version 2.5 for Linux
----------------------------------
NOTE! The sound driver is now a part of the Linux kernel distribution.
Check that your kernel doesn't have more recent version than this
when installing a separately distributed sound driver. The
version number of this driver is defined in the makefile.
Installation Installation
------------ ------------
- Since this driver is a part of the Linux kernel distribution, no - Since you are reading this, you have already installed the files so
special steps are required to build the driver itself. let's skip this step. To be serious, the sound driver belongs
to linux/drivers/sound.
- In case you are installing a separately distributed sound driver, - To build the device files you need to run the enclosed shell scrip
you have to do some additional steps. (see below).
- Remove all files from the linux/drivers/sound. Old files could
sometimes cause trouble.
- cd linux/drivers.
- gunzip -c snd-driv-X.Y.tar.gz|tar xvf -
- cd ./sound
- cp soundcard.h ultrasound.h /usr/include/linux
- To build the device files for this driver, you need to run the enclosed - If you are installing a separately distributed version, copy the
shell script (at the end of this file). soundcard.h to /usr/include/linux. It may contain some new stuff.
- Create /usr/include/sys/soundcard.h whic contains just a line: - Copy the sound/ultrasound.h to /usr/include/sys
#include <linux/soundcard.h> (Remove the old one from /usr/include/sys /usr/include/linux first).
- Create /usr/include/sys/ultrasound.h whic contains just a line: - Ensure you have the following symlink:
#include <linux/ultrasound.h> ln -s /usr/include/linux/soundcard.h /usr/include/sys/soundcard.h
- Configure and compile Linux as normally (remember to include the
sound support during "make config").
Boot time configuration (using lilo) Boot time configuration (using lilo)
------------------------------------ ------------------------------------
...@@ -61,6 +51,8 @@ Each option is encoded as the following: ...@@ -61,6 +51,8 @@ Each option is encoded as the following:
5=MPU-401 UART midi 5=MPU-401 UART midi
6=SB16 (16 bit DMA number) 6=SB16 (16 bit DMA number)
7=SB16 Midi (MPU-401 emulation) 7=SB16 Midi (MPU-401 emulation)
(There are some new ones also but they are currently
not documented).
These are the configuration templates for various soundcards: These are the configuration templates for various soundcards:
...@@ -174,43 +166,76 @@ If you want to play modules on a 386sx while recompiling the world, buy a GUS. ...@@ -174,43 +166,76 @@ If you want to play modules on a 386sx while recompiling the world, buy a GUS.
It runs without burning your CPU. It runs without burning your CPU.
Hannu Savolainen Hannu Savolainen
Hannu.Savolainen@Helsinki.fi hannu@voxware.pp.fi
(hannu@voxware.pp.fi (april 94 ->))
----------------- cut here ------------------------------ ----------------- cut here ------------------------------
#!/bin/sh #!/bin/sh
# #
# soudinstall # soudinstall
# #
# by Craig Metz - cmetz@thor.tjhsst.edu
# #
# Create the devices # Create the devices
# #
# Mixer (14, 0) # Mixer devices
# #
if [ -e /dev/mixer ]; then if [ -e /dev/mixer ]; then
rm -f /dev/mixer rm -f /dev/mixer
fi fi
mknod -m 666 /dev/mixer c 14 0 mknod -m 666 /dev/mixer c 14 0
if [ -e /dev/mixer1 ]; then if [ -e /dev/mixer1 ]; then
rm -f /dev/mixer1 rm -f /dev/mixer1
fi fi
mknod -m 666 /dev/mixer1 c 14 16 mknod -m 666 /dev/mixer1 c 14 16
#
# Sequencer (14, 1) # Sequencer (14, 1)
# #
if [ -e /dev/sequencer ]; then if [ -e /dev/sequencer ]; then
rm -f /dev/sequencer rm -f /dev/sequencer
fi fi
mknod -m 666 /dev/sequencer c 14 1 mknod -m 666 /dev/sequencer c 14 1
#
# MIDI (14, 2) [ Not implemented ] if [ -e /dev/patmgr0 ]; then
rm -f /dev/patmgr0
fi
mknod -m 666 /dev/patmgr0 c 14 17
if [ -e /dev/patmgr1 ]; then
rm -f /dev/patmgr1
fi
mknod -m 666 /dev/patmgr1 c 14 33
### # Sequencer2 (14, 8)
### #
### if [ -e /dev/sequencer2 ]; then
### rm -f /dev/sequencer2
### fi
### mknod -m 666 /dev/sequencer2 c 14 8
# Midi devices
# #
if [ -e /dev/midi ]; then if [ -e /dev/midi ]; then
rm -f /dev/midi rm -f /dev/midi # Old name. Don't use it
fi fi
mknod -m 666 /dev/midi c 14 2 ### if [ -e /dev/midi00 ]; then
### rm -f /dev/midi00
### fi
### mknod -m 666 /dev/midi00 c 14 2
###
### if [ -e /dev/midi01 ]; then
### rm -f /dev/midi01
### fi
### mknod -m 666 /dev/midi01 c 14 18
###
### if [ -e /dev/midi02 ]; then
### rm -f /dev/midi02
### fi
### mknod -m 666 /dev/midi02 c 14 34
###
### if [ -e /dev/midi03 ]; then
### rm -f /dev/midi03
### fi
### mknod -m 666 /dev/midi03 c 14 50
# #
# DSP (14, 3) # DSP (14, 3)
# #
...@@ -219,14 +244,14 @@ if [ -e /dev/dsp ]; then ...@@ -219,14 +244,14 @@ if [ -e /dev/dsp ]; then
fi fi
mknod -m 666 /dev/dsp c 14 3 mknod -m 666 /dev/dsp c 14 3
# #
# SPARC audio (14, 4) [ Not fully implemented ] # SPARC compatible /dev/audio (14, 4)
# #
if [ -e /dev/audio ]; then if [ -e /dev/audio ]; then
rm -f /dev/audio rm -f /dev/audio
fi fi
mknod -m 666 /dev/audio c 14 4 mknod -m 666 /dev/audio c 14 4
# #
# DSP2 (14, 19) /dev/dsp for the second soundcard. # DSP1 (14, 19) /dev/dsp for the second soundcard.
# Also the SB emulation part of the # Also the SB emulation part of the
# PAS16 card. # PAS16 card.
# #
...@@ -235,7 +260,7 @@ if [ -e /dev/dsp1 ]; then ...@@ -235,7 +260,7 @@ if [ -e /dev/dsp1 ]; then
fi fi
mknod -m 666 /dev/dsp1 c 14 19 mknod -m 666 /dev/dsp1 c 14 19
# #
# SPARC audio1 (14, 20) [ Not fully implemented ] # SPARC audio1 (14, 20)
# /dev/audio for the second soundcard. # /dev/audio for the second soundcard.
# Also the SB emulation part of the # Also the SB emulation part of the
# PAS16 card. # PAS16 card.
......
VoxWare v3.0
------------
This is a PROTOTYPE of the VoxWare v3.0 to be relased late 94.
All features of v2.5 should work as earlier. There could be some
omissions but they are unintentional. I started this version thread
after v2.3 so all features implemented before it are there.
Even this is a prototype, there should not be any fatal bugs. The
prototype just means that I don't have implemented all features
completely. Mainly in the /dev/sequener2 driver.
For example recording from /dev/sequencer2 won't work
with other cards than a full features MPU-401 or clones. As well the
way how the MIDI controllers are handled will change.
IMPORTANT!!!!!!!!!!!!!!!!!
Don't distribute any binaries compiled with the soundcard.h of this version.
They will not work together with older drivers.
New features
============
There are now two new device interfaces. The /dev/midi## is a raw
tty like interface to MIDI ports. There is a device file for each MIDI
port on your system. They are named (/dev/midi00 to /dev/midiNN).
The second addition is the /dev/sequencer2 which is higher level interface
than the old /dev/sequencer. It's intended for writing device independent
applications like sequencers.
/dev/midi##
-----------
This interface should be usefull for applications like MIDI sysex librarians.
There are (currently) no timing features so making music could be impossible.
There are as many /dev/midi## devices as there are MIDI ports in the system.
The /dev/midi00 is connected to the first one, /dev/midi01 to the second etc.
These devices work like tty devices in raw mode. Everything written to them is
sent out to the MIDI port. There is currently an extra delay of at most
1/100th of sec but it will be removed later.
The reading algorithm is little bit more complicated. There are two different
cases:
1) There is at least one byte in the input buffer.
The read returns as many bytes as it can without waiting for more bytes.
For example when a process reads 100 bytes and there are 10 bytes in the
buffer, the read returns just 10 bytes.
2) The input buffer is empty when the process calls read.
The read waits for the first byte and then continues as in case 1. By
default it waits infinitely but there is an ioctl for setting a timeout
for this. The ioctl(fd, SNDCTL_MIDI_PRETIME, &time) changes the timeout.
The time is given in 1/10th of seconds (10 means one second).
Other ioctl calls:
ioctl(fd, SNDCTL_MIDI_MPUMODE, &mode) is available for full MPU-401
compatible devices such as MPU-IPC-T, MQ PC Midi Card or MQX-32.
It's not available for the so called MPU UART ports of some soundcards
(PAS16, SB16 etc). By default the MIDI port is in UART mode after open.
If this ioctl is called with mode=1, the interface is put to the intelligent
(coprocessor) mode. NOTE! The MIDI port will be reset when this ioctl is called.
It could have some strange effects if not called immediately after open. This
vall returns EINVAL if the midi port doesn't support the MPU-401 intelligent
mode.
ioctl(fd, SNDCTL_MIDI_MPUCMD, &cmdstruct) is valid only if the MIDI port
is put to the coprocessor mode using ioctl(SNDCTL_MIDI_MPUMODE). It's used to
send commands to a MPU-401 compatible MIDI cards. Please refer to the
MPU-401 Technical Reference Manual (or Music Quest Technical Reference
Manual) for descriptions of the commands.
The argument of SNDCTL_MIDI_MPUCOMMAND is of type mpu_command_rec. It
has the following fields:
typedef struct {
unsigned char cmd;
char nr_args, nr_returns;
unsigned char data[30];
} mpu_command_rec;
where:
cmd Contains the command number.
nr_args Number of arguments of the command.
MUST BE INITIALIZED BEFORE CALL
nr_returns Number of bytes returned by the command.
MUST BE INITIALIZED BEFORE CALL
data Buffer for the command arguments and returned
data.
Be extremely carefull with the nr_args and nr_returns fields. They
must match the command. An incorrect value will put the card and
the driver out of sync. Refer to the MPU-401/MQX-32M documentation for further
datails.
/dev/sequencer2 (if you find a better name, please let me know).
---------------
This device file works much like the /dev/sequencer which has been present
since the beginning. The main differences are the following:
- /dev/sequencer makes the MIDI ports to look like the synth devices. In fact
the result is somewhere between the MIDI specification and the synth devices of
/dev/sequencer. Both kind of devices are accessed using the SEQ_START_NOTE()
like macros. The voice number parameters of the API macros have been redefined
to denote MIDI channels. This means that the driver allocates voices for
the channels automaticly (this is a responsibility/right of an application
with /dev/sequencer). The result is that a SEQ_START_NOTE() macro has
similar effects for a synth channel than on a MIDI port. This kind of
solution provides better device independence than the /dev/sequencer. The
drawback is that the new interface doesn't permit so low level access to the
device as the /dev/sequencer does. An application developer must choose between
these two interfaces. I think the old /dev/sequencer is better for applications
like module players while the new one is better for making generic sequencer
programs.
- There are no separate MIDI devices with the /dev/sequencer2. The
ioctl(SNDCTL_SEQ_NRMIDIS) returns always zero. Instead the MIDI ports are
shown as synth devices. ioctl(SNDCTL_SEQ_NRSYNTHS) on /dev/sequencer2 will
return sum of internal synthesizers (GUS, OPL3) and MIDI ports in the systems.
- The new interface is used much like the ordinary /dev/sequencer. The
event format is new so you have to use the API macros defined in the
sys/soundcard.h. The interface is will propably change before the final 3.0
release but using the API macros should ensure compatibility in source level.
The new event format is not recognized by version 2.X so don't try to
distribute binaries compiled with soundcard.h of v3.X.
- The basic API useage is similar to the current one. There are some new
macros but the older ones should work as earlier. The most important
incompatibility is that the /dev/sequencer2 driver allocates voices itself.
The other one is that the application must send SEQ_START_TIMER() as it's
first event. Otherwise the timer is not started and the application waits
infinitely.
There are several new features but I don't document them here. There are
some info in the soundcard.h (near the end). I have also included some
sample code in the directory v30. Full documentation will
appear in the Hacker's Guide later.
Don't hesitate to contact me in case you have questions or comments.
Hannu Savolainen
hannu@voxware.pp.fi
This diff is collapsed.
...@@ -37,18 +37,48 @@ ...@@ -37,18 +37,48 @@
#define ON 1 #define ON 1
#define OFF 0 #define OFF 0
static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a static int wr_buff_no[MAX_AUDIO_DEV]; /*
* != -1, if there is
* a incomplete output
* block in the queue.
*/
static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV];
* incomplete output block */ static int audio_mode[MAX_AUDIO_DEV];
static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV];
static int audio_mode[MAX_DSP_DEV];
#define AM_NONE 0 #define AM_NONE 0
#define AM_WRITE 1 #define AM_WRITE 1
#define AM_READ 2 #define AM_READ 2
static char *wr_dma_buf[MAX_DSP_DEV]; static char *wr_dma_buf[MAX_AUDIO_DEV];
static int audio_format[MAX_AUDIO_DEV];
static int local_conversion[MAX_AUDIO_DEV];
static int
set_format (int dev, int fmt)
{
if (fmt != AFMT_QUERY)
{
local_conversion[dev] = 0;
if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
if (fmt == AFMT_MU_LAW)
{
fmt = AFMT_U8;
local_conversion[dev] = AFMT_MU_LAW;
}
else
fmt = AFMT_U8; /* This is always supported */
audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, fmt, 1);
}
if (local_conversion[dev]) /* This shadows the HW format */
return local_conversion[dev];
return audio_format[dev];
}
int int
audio_open (int dev, struct fileinfo *file) audio_open (int dev, struct fileinfo *file)
...@@ -68,12 +98,21 @@ audio_open (int dev, struct fileinfo *file) ...@@ -68,12 +98,21 @@ audio_open (int dev, struct fileinfo *file)
if ((ret = DMAbuf_open (dev, mode)) < 0) if ((ret = DMAbuf_open (dev, mode)) < 0)
return ret; return ret;
if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits) local_conversion[dev] = 0;
if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, bits, 1) != bits)
{ {
audio_release (dev, file); audio_release (dev, file);
return RET_ERROR (ENXIO); return RET_ERROR (ENXIO);
} }
if (dev_type == SND_DEV_AUDIO)
{
set_format (dev, AFMT_MU_LAW);
}
else
set_format (dev, bits);
wr_buff_no[dev] = -1; wr_buff_no[dev] = -1;
audio_mode[dev] = AM_NONE; audio_mode[dev] = AM_NONE;
...@@ -128,21 +167,24 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) ...@@ -128,21 +167,24 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{ {
int c, p, l; int c, p, l;
int err; int err;
int dev_type = dev & 0x0f;
dev = dev >> 4; dev = dev >> 4;
p = 0; p = 0;
c = count; c = count;
if (audio_mode[dev] == AM_READ) /* Direction changed */ if (audio_mode[dev] == AM_READ) /*
* Direction changed
*/
{ {
wr_buff_no[dev] = -1; wr_buff_no[dev] = -1;
} }
audio_mode[dev] = AM_WRITE; audio_mode[dev] = AM_WRITE;
if (!count) /* Flush output */ if (!count) /*
* Flush output
*/
{ {
if (wr_buff_no[dev] >= 0) if (wr_buff_no[dev] >= 0)
{ {
...@@ -154,11 +196,17 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) ...@@ -154,11 +196,17 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
} }
while (c) while (c)
{ /* Perform output blocking */ { /*
if (wr_buff_no[dev] < 0) /* There is no incomplete buffers */ * 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) if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0)
return wr_buff_no[dev]; {
return wr_buff_no[dev];
}
wr_buff_ptr[dev] = 0; wr_buff_ptr[dev] = 0;
} }
...@@ -166,21 +214,27 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) ...@@ -166,21 +214,27 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
l = (wr_buff_size[dev] - wr_buff_ptr[dev]); l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
if (!dsp_devs[dev]->copy_from_user) if (!audio_devs[dev]->copy_from_user)
{ /* No device specific copy routine */ { /*
* No device specific copy routine
*/
COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l); COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
} }
else else
dsp_devs[dev]->copy_from_user (dev, audio_devs[dev]->copy_from_user (dev,
wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l); wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l);
/* Insert local processing here */ /*
* Insert local processing here
*/
if (dev_type == SND_DEV_AUDIO) if (local_conversion[dev] == AFMT_MU_LAW)
{ {
#ifdef linux #ifdef linux
/* This just allows interrupts while the conversion is running */ /*
* This just allows interrupts while the conversion is running
*/
__asm__ ("sti"); __asm__ ("sti");
#endif #endif
translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l); translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
...@@ -193,7 +247,9 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) ...@@ -193,7 +247,9 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
if (wr_buff_ptr[dev] >= wr_buff_size[dev]) if (wr_buff_ptr[dev] >= wr_buff_size[dev])
{ {
if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0) if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
return err; {
return err;
}
wr_buff_no[dev] = -1; wr_buff_no[dev] = -1;
} }
...@@ -209,7 +265,6 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) ...@@ -209,7 +265,6 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
int c, p, l; int c, p, l;
char *dmabuf; char *dmabuf;
int buff_no; int buff_no;
int dev_type = dev & 0x0f;
dev = dev >> 4; dev = dev >> 4;
p = 0; p = 0;
...@@ -235,12 +290,16 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) ...@@ -235,12 +290,16 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
if (l > c) if (l > c)
l = c; l = c;
/* Insert any local processing here. */ /*
* Insert any local processing here.
*/
if (dev_type == SND_DEV_AUDIO) if (local_conversion[dev] == AFMT_MU_LAW)
{ {
#ifdef linux #ifdef linux
/* This just allows interrupts while the conversion is running */ /*
* This just allows interrupts while the conversion is running
*/
__asm__ ("sti"); __asm__ ("sti");
#endif #endif
...@@ -262,7 +321,6 @@ int ...@@ -262,7 +321,6 @@ int
audio_ioctl (int dev, struct fileinfo *file, audio_ioctl (int dev, struct fileinfo *file,
unsigned int cmd, unsigned int arg) unsigned int cmd, unsigned int arg)
{ {
int dev_type = dev & 0x0f;
dev = dev >> 4; dev = dev >> 4;
...@@ -293,22 +351,32 @@ audio_ioctl (int dev, struct fileinfo *file, ...@@ -293,22 +351,32 @@ audio_ioctl (int dev, struct fileinfo *file,
return DMAbuf_ioctl (dev, cmd, arg, 0); return DMAbuf_ioctl (dev, cmd, arg, 0);
break; break;
default: case SNDCTL_DSP_GETFMTS:
if (dev_type == SND_DEV_AUDIO) return IOCTL_OUT (arg, audio_devs[dev]->format_mask);
return RET_ERROR (EIO); break;
case SNDCTL_DSP_SETFMT:
return IOCTL_OUT (arg, set_format (dev, IOCTL_IN (arg)));
default:
return DMAbuf_ioctl (dev, cmd, arg, 0); return DMAbuf_ioctl (dev, cmd, arg, 0);
break;
} }
} }
long long
audio_init (long mem_start) audio_init (long mem_start)
{ {
/*
* NOTE! This routine could be called several times during boot.
*/
return mem_start; return mem_start;
} }
#else #else
/* Stub versions */ /*
* Stub versions
*/
int int
audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
......
This diff is collapsed.
...@@ -32,101 +32,138 @@ ...@@ -32,101 +32,138 @@
#ifdef CONFIGURE_SOUNDCARD #ifdef CONFIGURE_SOUNDCARD
int
snd_find_driver (int type)
{
int i, n = sizeof (sound_drivers) / sizeof (struct driver_info);
for (i = 0; i < (n - 1); i++)
if (sound_drivers[i].card_type == type)
return i;
return -1; /*
* Not found
*/
}
long long
sndtable_init (long mem_start) sndtable_init (long mem_start)
{ {
int i, n = sizeof (supported_drivers) / sizeof (struct card_info); int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
int drv;
for (i = 0; i < (n - 1); i++) for (i = 0; i < (n - 1); i++)
if (supported_drivers[i].enabled) if (snd_installed_cards[i].enabled)
if (supported_drivers[i].probe (&supported_drivers[i].config)) if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
snd_installed_cards[i].enabled = 0; /*
* Mark as not detected
*/
else if (sound_drivers[drv].probe (&snd_installed_cards[i].config))
{ {
#ifndef SHORT_BANNERS #ifndef SHORT_BANNERS
printk ("snd%d", printk ("snd%d",
supported_drivers[i].card_type); snd_installed_cards[i].card_type);
#endif #endif
mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config); mem_start = sound_drivers[drv].attach (mem_start, &snd_installed_cards[i].config);
#ifndef SHORT_BANNERS #ifndef SHORT_BANNERS
printk (" at 0x%x irq %d drq %d\n", printk (" at 0x%x irq %d drq %d\n",
supported_drivers[i].config.io_base, snd_installed_cards[i].config.io_base,
supported_drivers[i].config.irq, snd_installed_cards[i].config.irq,
supported_drivers[i].config.dma); snd_installed_cards[i].config.dma);
#endif #endif
} }
else else
supported_drivers[i].enabled = 0; /* Mark as not detected */ snd_installed_cards[i].enabled = 0; /*
* Mark as not detected
*/
return mem_start; return mem_start;
} }
int int
sndtable_probe (int unit, struct address_info *hw_config) sndtable_probe (int unit, struct address_info *hw_config)
{ {
int i, n = sizeof (supported_drivers) / sizeof (struct card_info); int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
if (!unit) if (!unit)
return TRUE; return TRUE;
for (i = 0; i < (n - 1); i++)
if (supported_drivers[i].card_type == unit)
{
supported_drivers[i].config.io_base = hw_config->io_base;
supported_drivers[i].config.irq = hw_config->irq;
supported_drivers[i].config.dma = hw_config->dma;
if (supported_drivers[i].probe (hw_config))
return 1;
supported_drivers[i].enabled = 0; /* Mark as not detected */
return 0;
}
return FALSE; for (i = 0; i < (n - 1); i++)
} if (snd_installed_cards[i].enabled)
if (snd_installed_cards[i].card_type == unit)
{
int drv;
snd_installed_cards[i].config.io_base = hw_config->io_base;
snd_installed_cards[i].config.irq = hw_config->irq;
snd_installed_cards[i].config.dma = hw_config->dma;
if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
snd_installed_cards[i].enabled = 0; /*
* Mark as not
* detected
*/
else if (sound_drivers[drv].probe (hw_config))
return 1;
snd_installed_cards[i].enabled = 0; /*
* Mark as not detected
*/
return 0;
}
return FALSE;
}
int int
sndtable_init_card (int unit, struct address_info *hw_config) sndtable_init_card (int unit, struct address_info *hw_config)
{ {
int i, n = sizeof (supported_drivers) / sizeof (struct card_info); int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
if (!unit)
{
if (sndtable_init (0) != 0)
panic ("snd: Invalid memory allocation\n");
return TRUE;
}
for (i = 0; i < (n - 1); i++) if (!unit)
if (supported_drivers[i].card_type == unit)
{ {
supported_drivers[i].config.io_base = hw_config->io_base; if (sndtable_init (0) != 0)
supported_drivers[i].config.irq = hw_config->irq; panic ("snd: Invalid memory allocation\n");
supported_drivers[i].config.dma = hw_config->dma;
if (supported_drivers[i].attach (0, hw_config) != 0)
panic ("snd#: Invalid memory allocation\n");
return TRUE; return TRUE;
} }
return FALSE; for (i = 0; i < (n - 1); i++)
} if (snd_installed_cards[i].card_type == unit)
{
int drv;
snd_installed_cards[i].config.io_base = hw_config->io_base;
snd_installed_cards[i].config.irq = hw_config->irq;
snd_installed_cards[i].config.dma = hw_config->dma;
if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
snd_installed_cards[i].enabled = 0; /*
* Mark as not detected
*/
else if (sound_drivers[drv].attach (0, hw_config) != 0)
panic ("snd#: Invalid memory allocation\n");
return TRUE;
}
return FALSE;
}
int int
sndtable_get_cardcount (void) sndtable_get_cardcount (void)
{ {
return num_dspdevs + num_mixers + num_synths + num_midis; return num_audiodevs + num_mixers + num_synths + num_midis;
} }
#ifdef linux #ifdef linux
void void
sound_setup (char *str, int *ints) sound_setup (char *str, int *ints)
{ {
int i, n = sizeof (supported_drivers) / sizeof (struct card_info); int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
/* /*
* First disable all drivers * First disable all drivers
*/ */
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
supported_drivers[i].enabled = 0; snd_installed_cards[i].enabled = 0;
if (ints[0] == 0 || ints[1] == 0) if (ints[0] == 0 || ints[1] == 0)
return; return;
...@@ -145,7 +182,9 @@ sound_setup (char *str, int *ints) ...@@ -145,7 +182,9 @@ sound_setup (char *str, int *ints)
if (card_type > 127) if (card_type > 127)
{ {
/* Add any future extensions here */ /*
* Add any future extensions here
*/
return; return;
} }
...@@ -155,17 +194,20 @@ sound_setup (char *str, int *ints) ...@@ -155,17 +194,20 @@ sound_setup (char *str, int *ints)
ptr = -1; ptr = -1;
for (j = 0; j < n && ptr == -1; j++) for (j = 0; j < n && ptr == -1; j++)
if (supported_drivers[j].card_type == card_type) if (snd_installed_cards[j].card_type == card_type &&
!snd_installed_cards[j].enabled) /*
* Not already found
*/
ptr = j; ptr = j;
if (ptr == -1) if (ptr == -1)
printk ("Sound: Invalid setup parameter 0x%08x\n", val); printk ("Sound: Invalid setup parameter 0x%08x\n", val);
else else
{ {
supported_drivers[ptr].enabled = 1; snd_installed_cards[ptr].enabled = 1;
supported_drivers[ptr].config.io_base = ioaddr; snd_installed_cards[ptr].config.io_base = ioaddr;
supported_drivers[ptr].config.irq = irq; snd_installed_cards[ptr].config.irq = irq;
supported_drivers[ptr].config.dma = dma; snd_installed_cards[ptr].config.dma = dma;
} }
} }
} }
...@@ -174,24 +216,27 @@ sound_setup (char *str, int *ints) ...@@ -174,24 +216,27 @@ sound_setup (char *str, int *ints)
void void
sound_chconf (int card_type, int ioaddr, int irq, int dma) sound_chconf (int card_type, int ioaddr, int irq, int dma)
{ {
int i, n = sizeof (supported_drivers) / sizeof (struct card_info); int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
int ptr, j; int ptr, j;
ptr = -1; ptr = -1;
for (j = 0; j < n && ptr == -1; j++) for (j = 0; j < n && ptr == -1; j++)
if (supported_drivers[j].card_type == card_type) if (snd_installed_cards[j].card_type == card_type &&
!snd_installed_cards[j].enabled) /*
* Not already found
*/
ptr = j; ptr = j;
if (ptr != -1) if (ptr != -1)
{ {
supported_drivers[ptr].enabled = 1; snd_installed_cards[ptr].enabled = 1;
if (ioaddr) if (ioaddr)
supported_drivers[ptr].config.io_base = ioaddr; snd_installed_cards[ptr].config.io_base = ioaddr;
if (irq) if (irq)
supported_drivers[ptr].config.irq = irq; snd_installed_cards[ptr].config.irq = irq;
if (dma) if (dma)
supported_drivers[ptr].config.dma = dma; snd_installed_cards[ptr].config.dma = dma;
} }
} }
...@@ -201,17 +246,24 @@ struct address_info * ...@@ -201,17 +246,24 @@ struct address_info *
sound_getconf (int card_type) sound_getconf (int card_type)
{ {
int j, ptr; int j, ptr;
int n = sizeof (supported_drivers) / sizeof (struct card_info); int n = sizeof (snd_installed_cards) / sizeof (struct card_info);
ptr = -1; ptr = -1;
for (j = 0; j < n && ptr == -1; j++) for (j = 0; j < n && ptr == -1; j++)
if (supported_drivers[j].card_type == card_type) if (snd_installed_cards[j].card_type == card_type)
ptr = j; ptr = j;
if (ptr == -1) if (ptr == -1)
return (struct address_info *) NULL; return (struct address_info *) NULL;
return &supported_drivers[ptr].config; return &snd_installed_cards[ptr].config;
}
#else
void
sound_setup (char *str, int *ints)
{
} }
#endif #endif
This diff is collapsed.
This diff is collapsed.
This version contains some features which is are NOT enabled by default.
I'm trying to release an official/reliable version soon so that the
Linux version of Doom (and other games) becomes possible. For that reason
I have disabled some features which are not reliable enough to be
released for wide public. If you are interested to try them, please
read this file carefully.
There are currently following goodies which I have disabled:
1) ECHO PSS (Personal Sound System support)
This version contains support for soundcards based on the AD20msp614
chipset made by Analog Devices. This chipset consist of the
AD1848 codec, ADSP-21xx DSP chip and a ESC614 ASIC and is used in some
soundcards made by Orchid, Cardinal, Echo Speech Corporation, Western
Digital and Wearnes Technology. The PSS support is by Marc M. Hoffman
(marc.hoffman@analog.com). I received this stuff about a week ago and
have not been able to test it yet.
If you are interested, remove the B(OPT_PSS) from the DISABLED_OPTIONS
(see above).
You have also to enable the MSS support since I have not integrated
the AD1848 driver with the PSS one yet.
2) WSS/MSS (Microsoft Sound System) support
The MSS standard is based on the AD1848 codec by Analog Devices.
Since I don't know how the software configuration of the MSS works
so it's not supported yet. This driver should work if your card
has jumpers for the I/O base, IRQ and DMA or there is a way to configure
them using DOS. You could try this if you have a soundcard with
AD1848 codec. I have tried to use this with Aztech SG NX Pro 16 without
success.
If you are interested, remove the B(OPT_MSS) from the DISABLED_OPTIONS
(see above).
3) /dev/sequencer2
This version has a new device file called /dev/sequence2. I have not
implemented all parts of it but it's there. It's only interesting if
you are writing a sequencer program yourself. Enable by creating
the device file /dev/sequencer (minor 8).
4) /dev/midi##
These are tty like raw devices for MIDI ports. Since there is a minor
incompatibility between different versions of Linux, I have disabled
this feature by default. You just need to create the device files yourself.
IMPORTANT! If you get warning at line 64 of midibuf.c,
don't try to use /dev/midi## files. Otherwise your
system halts. You may also try to fix the
DEFINE_TIMER() macro in os.h (just remove the 2nd NULL).
This could happen with some earlier versions of Linux
(before 1.1.0???).
5) Support for hardware based u-Law/A-Law and ADPCM formats.
The AD1848 (and compatibles) are able to do compression and
decompression by hardware. This version has experimental support
for some of them. Currently they are implemented just in the
AD1848 driver. The GUS MAX (and the 16 bit daughtercard) support
also 16->4 bit ADPCM (the IMA one) but it don't work yet.
The argument ioctl(SNDCTL_DSP_SAMPLESIZE) can have some new values
in addition to the 8 and 16 supported earlier. Look at soundcard.h
for more info.
(In case somebody dares to ask: The ASP chip of SB16 is not supported
so the hardware compression/decompression doesn't work with it. Also
the ADPCM format is different than the standard (IMA) one (I guess).
This feature is enabled by default.
5) Real time fix to /dev/dsp and /dev/audio drivers
The following feature should help game writers. This stuff is enabled
by default.
---------------- cut here ---------------------
There is a new ioctl called SNDCTL_DSP_SETFRAGMENT. It accepts a
int parameter which has format 0x00nn00ss where the nn is max number of
buffer fragments (between 0x02 and 0xff) and the ss gives indirectly the
size of a buffer fragment (fragment_size = (1 << ss)). Valid sizes are
between (ss=0x07 -> 128 bytes and ss=0x11 (17 dec) -> 128k).
This ioctl must be used ONCE between open() and first call to
read()/write() or ioctl(SNDCTL_DSP_GETBLKSIZE).
You need just to force the fragment size to a value which is sufficiently
short (gives the 1/20th of sec with the speed/#channels/#bits you are using).
Using a small number of fragments offers (I guess) a significant advantage.
For example with 2 fragments the driver works as the following (at least
I hope so). Assuming that the process writes exactly 'fragment_size' of
bytes each time (this is really important).
1) When the process writes the first fragment, it will be copied to
the DMA buffer area and the playback begins. The write() returns
immediately and the process is free to continue.
2a) If the fragment gets played before the application writes a new
one, the device will be stoppen and restarted which causes a click.
When the process calls write next time, it will be processes as
in step 1.
2b) If the process calls write before the buffer underflows, the
data will be queued and the process is free to continue. (There
is now one full and one partially played fragment in the kernel
buffers. This gives average delay of 1.5*fragment_time (for
example 1/20th sec) before the last byte in the buffer gets played.
3a) If the device gets both fragments played before the next write
(underflow), there will be a click. The write will be processed as
in step 1.
3b) If the 1st fragment gets played before next write (the process
calls write during playback of the second fragment), it will be
processed as step 2b.
3c) If the process writes 3rd fragment when there is already 2
fragments in the queue (1 playing and 1 waiting), the process
will block until the 1st fragment gets played. It will then be
woken up and it continues as in step 2b. This means that
the process blocks for at most the time required to play a
buffer fragment.
This method syncronizes the process and the audio device together
automaticly. The process will block at most the 'fragment_time'. Usually
less, depending on how much it needs time to do other things. The maximum
delay between writing a byte and the time when it finally plays is
at most 3 times the 'fragment_time'.
The delay depends on how much time the program needs to do it's
computations for the next sample (updating screen etc). If it's about
80% of the 'fragment_time' the game will run almost without delays. If it
uses more time, there is a risk that the audio buffer gets empty.
The application code should be something like the following:
int frag = 0x00020008; /* 2 fragments of 2^8=256 bytes */
int frag_size;
int fd=open("/dev/dsp");
ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
ioctl(NDCTL_DSP_SPEED); /* And #channels & #bits if required */
/*
* Query the actual fragment sice since the driver may refuse
* the requested one (unlikely but possible?)
*/
ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size);
while(True)
{
do_computations();
write(fd, buf, frag_size); /* Always the same size!!!!!!! */
}
I have tested this with a modified version of str.c. The algorithm works
as long as the playing program gets enough time to run. Hitting ENTER on
another virtual console causes a pause/click (with 2 frags of 64 bytes).
------------------- cut here ---------------------
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
void gusintr (int); void gusintr (int);
int gus_base, gus_irq, gus_dma; int gus_base, gus_irq, gus_dma;
extern int gus_wave_volume;
extern int gus_pcm_volume;
extern int have_gus_max;
long long
attach_gus_card (long mem_start, struct address_info *hw_config) attach_gus_card (long mem_start, struct address_info *hw_config)
...@@ -44,11 +47,16 @@ attach_gus_card (long mem_start, struct address_info *hw_config) ...@@ -44,11 +47,16 @@ attach_gus_card (long mem_start, struct address_info *hw_config)
snd_set_irq_handler (hw_config->irq, gusintr); snd_set_irq_handler (hw_config->irq, gusintr);
if (gus_wave_detect (hw_config->io_base)) /* Try first the default */ 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); mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
#ifndef EXCLUDE_MIDI #ifndef EXCLUDE_MIDI
mem_start = gus_midi_init (mem_start); mem_start = gus_midi_init (mem_start);
#endif
#ifndef EXCLUDE_SEQUENCER
sound_timer_init (hw_config->io_base + 8);
#endif #endif
return mem_start; return mem_start;
} }
...@@ -60,20 +68,27 @@ attach_gus_card (long mem_start, struct address_info *hw_config) ...@@ -60,20 +68,27 @@ attach_gus_card (long mem_start, struct address_info *hw_config)
*/ */
for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
if (io_addr != hw_config->io_base) /* Already tested */ if (io_addr != hw_config->io_base) /*
* Already tested
*/
if (gus_wave_detect (io_addr)) if (gus_wave_detect (io_addr))
{ {
printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base); printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base);
mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma); mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
#ifndef EXCLUDE_MIDI #ifndef EXCLUDE_MIDI
mem_start = gus_midi_init (mem_start); mem_start = gus_midi_init (mem_start);
#endif
#ifndef EXCLUDE_SEQUENCER
sound_timer_init (io_addr + 8);
#endif #endif
return mem_start; return mem_start;
} }
#endif #endif
return mem_start; /* Not detected */ return mem_start; /*
* Not detected
*/
} }
int int
...@@ -91,7 +106,9 @@ probe_gus (struct address_info *hw_config) ...@@ -91,7 +106,9 @@ probe_gus (struct address_info *hw_config)
*/ */
for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
if (io_addr != hw_config->io_base) /* Already tested */ if (io_addr != hw_config->io_base) /*
* Already tested
*/
if (gus_wave_detect (io_addr)) if (gus_wave_detect (io_addr))
return 1; return 1;
...@@ -101,7 +118,7 @@ probe_gus (struct address_info *hw_config) ...@@ -101,7 +118,7 @@ probe_gus (struct address_info *hw_config)
} }
void void
gusintr (int unit) gusintr (int irq)
{ {
unsigned char src; unsigned char src;
...@@ -109,6 +126,11 @@ gusintr (int unit) ...@@ -109,6 +126,11 @@ gusintr (int unit)
sti (); sti ();
#endif #endif
#ifndef EXCLUDE_GUSMAX
if (have_gus_max)
ad1848_interrupt (irq);
#endif
while (1) while (1)
{ {
if (!(src = INB (u_IrqStatus))) if (!(src = INB (u_IrqStatus)))
...@@ -128,8 +150,11 @@ gusintr (int unit) ...@@ -128,8 +150,11 @@ gusintr (int unit)
if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
{ {
printk ("T"); #ifndef EXCLUDE_SEQUENCER
gus_write8 (0x45, 0); /* Timer control */ sound_timer_interrupt ();
#else
gus_write8 (0x45, 0); /* Stop timers */
#endif
} }
if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
...@@ -140,3 +165,29 @@ gusintr (int unit) ...@@ -140,3 +165,29 @@ gusintr (int unit)
} }
#endif #endif
/*
* Some extra code for the 16 bit sampling option
*/
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS16)
int
probe_gus_db16 (struct address_info *hw_config)
{
return ad1848_detect (hw_config->io_base);
}
long
attach_gus_db16 (long mem_start, struct address_info *hw_config)
{
gus_pcm_volume = 100;
gus_wave_volume = 90;
ad1848_init ("GUS 16 bit sampling", hw_config->io_base,
hw_config->irq,
hw_config->dma,
hw_config->dma);
return mem_start;
}
#endif
...@@ -79,7 +79,9 @@ gus_midi_open (int dev, int mode, ...@@ -79,7 +79,9 @@ gus_midi_open (int dev, int mode,
gus_midi_control |= MIDI_ENABLE_XMIT; gus_midi_control |= MIDI_ENABLE_XMIT;
} }
OUTB (gus_midi_control, u_MidiControl); /* Enable */ OUTB (gus_midi_control, u_MidiControl); /*
* Enable
*/
midi_busy = 1; midi_busy = 1;
qlen = qhead = qtail = output_used = 0; qlen = qhead = qtail = output_used = 0;
...@@ -105,7 +107,9 @@ dump_to_midi (unsigned char midi_byte) ...@@ -105,7 +107,9 @@ dump_to_midi (unsigned char midi_byte)
} }
else else
{ {
/* Enable Midi xmit interrupts (again) */ /*
* Enable Midi xmit interrupts (again)
*/
gus_midi_control |= MIDI_ENABLE_XMIT; gus_midi_control |= MIDI_ENABLE_XMIT;
OUTB (gus_midi_control, u_MidiControl); OUTB (gus_midi_control, u_MidiControl);
} }
...@@ -117,7 +121,9 @@ dump_to_midi (unsigned char midi_byte) ...@@ -117,7 +121,9 @@ dump_to_midi (unsigned char midi_byte)
static void static void
gus_midi_close (int dev) gus_midi_close (int dev)
{ {
/* Reset FIFO pointers, disable intrs */ /*
* Reset FIFO pointers, disable intrs
*/
OUTB (MIDI_RESET, u_MidiControl); OUTB (MIDI_RESET, u_MidiControl);
midi_busy = 0; midi_busy = 0;
...@@ -149,14 +155,18 @@ gus_midi_out (int dev, unsigned char midi_byte) ...@@ -149,14 +155,18 @@ gus_midi_out (int dev, unsigned char midi_byte)
if (!qlen) if (!qlen)
if (dump_to_midi (midi_byte)) if (dump_to_midi (midi_byte))
return 1; /* OK */ return 1; /*
* OK
*/
/* /*
* Put to the local queue * Put to the local queue
*/ */
if (qlen >= 256) if (qlen >= 256)
return 0; /* Local queue full */ return 0; /*
* Local queue full
*/
DISABLE_INTR (flags); DISABLE_INTR (flags);
...@@ -213,9 +223,14 @@ gus_midi_buffer_status (int dev) ...@@ -213,9 +223,14 @@ gus_midi_buffer_status (int dev)
return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY); return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
} }
#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static struct midi_operations gus_midi_operations = static struct midi_operations gus_midi_operations =
{ {
{"Gravis UltraSound", 0, 0, SNDCARD_GUS}, {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
&std_midi_synth,
gus_midi_open, gus_midi_open,
gus_midi_close, gus_midi_close,
gus_midi_ioctl, gus_midi_ioctl,
...@@ -223,16 +238,25 @@ static struct midi_operations gus_midi_operations = ...@@ -223,16 +238,25 @@ static struct midi_operations gus_midi_operations =
gus_midi_start_read, gus_midi_start_read,
gus_midi_end_read, gus_midi_end_read,
gus_midi_kick, gus_midi_kick,
NULL, /* command */ NULL, /*
gus_midi_buffer_status * command
*/
gus_midi_buffer_status,
NULL
}; };
long long
gus_midi_init (long mem_start) gus_midi_init (long mem_start)
{ {
if (num_midis >= MAX_MIDI_DEV)
{
printk ("Sound: Too many midi devices detected\n");
return mem_start;
}
OUTB (MIDI_RESET, u_MidiControl); OUTB (MIDI_RESET, u_MidiControl);
my_dev = num_midis; std_midi_synth.midi_dev = my_dev = num_midis;
midi_devs[num_midis++] = &gus_midi_operations; midi_devs[num_midis++] = &gus_midi_operations;
return mem_start; return mem_start;
} }
...@@ -264,7 +288,9 @@ gus_midi_interrupt (int dummy) ...@@ -264,7 +288,9 @@ gus_midi_interrupt (int dummy)
if (!qlen) if (!qlen)
{ {
/* Disable Midi output interrupts, since no data in the buffer */ /*
* Disable Midi output interrupts, since no data in the buffer
*/
gus_midi_control &= ~MIDI_ENABLE_XMIT; gus_midi_control &= ~MIDI_ENABLE_XMIT;
OUTB (gus_midi_control, u_MidiControl); OUTB (gus_midi_control, u_MidiControl);
} }
......
This diff is collapsed.
...@@ -44,34 +44,36 @@ static int left_fix[ICS_MIXDEVS] = ...@@ -44,34 +44,36 @@ static int left_fix[ICS_MIXDEVS] =
static int right_fix[ICS_MIXDEVS] = static int right_fix[ICS_MIXDEVS] =
{2, 2, 2, 1, 2, 1}; {2, 2, 2, 1, 2, 1};
static int static int
scale_vol(int vol) scale_vol (int vol)
{ {
#if 1 #if 1
/* /*
* Experimental volume scaling by Risto Kankkunen. * Experimental volume scaling by Risto Kankkunen.
* This should give smoother volume response than just * This should give smoother volume response than just
* a plain multiplication. * a plain multiplication.
*/ */
int e; int e;
if (vol < 0) if (vol < 0)
vol = 0; vol = 0;
if (vol > 100) if (vol > 100)
vol = 100; vol = 100;
vol = (31 * vol + 50) / 100; vol = (31 * vol + 50) / 100;
e = 0; e = 0;
if (vol) { if (vol)
while (vol < 16) { {
vol <<= 1; while (vol < 16)
e--; {
} vol <<= 1;
vol -= 16; e--;
e += 7; }
} vol -= 16;
return ((e << 4) + vol); e += 7;
}
return ((e << 4) + vol);
#else #else
return ((vol*127)+50)/100; return ((vol * 127) + 50) / 100;
#endif #endif
} }
...@@ -83,7 +85,7 @@ write_mix (int dev, int chn, int vol) ...@@ -83,7 +85,7 @@ write_mix (int dev, int chn, int vol)
int ctrl_addr = dev << 3; int ctrl_addr = dev << 3;
int attn_addr = dev << 3; int attn_addr = dev << 3;
vol=scale_vol(vol); vol = scale_vol (vol);
if (chn == CHN_LEFT) if (chn == CHN_LEFT)
{ {
...@@ -180,7 +182,7 @@ ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) ...@@ -180,7 +182,7 @@ ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
case SOUND_MIXER_STEREODEVS: case SOUND_MIXER_STEREODEVS:
return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD | return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD |
SOUND_MASK_SYNTH | SOUND_MASK_VOLUME| SOUND_MASK_SYNTH | SOUND_MASK_VOLUME |
SOUND_MASK_MIC); SOUND_MASK_MIC);
break; break;
......
static unsigned char ctrl_def_values[128] =
{
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 0 to 7 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 8 to 15 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 16 to 23 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 24 to 31 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
};
This diff is collapsed.
int midi_synth_ioctl (int dev,
unsigned int cmd, unsigned int arg);
int midi_synth_kill_note (int dev, int channel, int note, int velocity);
int midi_synth_set_instr (int dev, int channel, int instr_no);
int midi_synth_start_note (int dev, int channel, int note, int volume);
void midi_synth_reset (int dev);
int midi_synth_open (int dev, int mode);
void midi_synth_close (int dev);
void midi_synth_hw_control (int dev, unsigned char *event);
int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
int offs, int count, int pmgr_flag);
void midi_synth_panning (int dev, int channel, int pressure);
void midi_synth_aftertouch (int dev, int channel, int pressure);
void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
int midi_synth_patchmgr (int dev, struct patmgr_info *rec);
void midi_synth_bender (int dev, int chn, int value);
#ifndef _MIDI_SYNTH_C_
static struct synth_info std_synth_info =
{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
static struct synth_operations std_midi_synth =
{
&std_synth_info,
0,
SYNTH_TYPE_MIDI,
0,
midi_synth_open,
midi_synth_close,
midi_synth_ioctl,
midi_synth_kill_note,
midi_synth_start_note,
midi_synth_set_instr,
midi_synth_reset,
midi_synth_hw_control,
midi_synth_load_patch,
midi_synth_aftertouch,
midi_synth_controller,
midi_synth_panning,
NULL,
midi_synth_patchmgr,
midi_synth_bender
};
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -44,8 +44,8 @@ ...@@ -44,8 +44,8 @@
#include <sys/kd.h> #include <sys/kd.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/malloc.h> #include <linux/malloc.h>
#include <linux/soundcard.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/soundcard.h>
typedef char snd_rw_buf; typedef char snd_rw_buf;
...@@ -73,7 +73,7 @@ struct snd_wait { ...@@ -73,7 +73,7 @@ struct snd_wait {
#define DEFINE_WAIT_QUEUES(name, flag) static struct wait_queue *name = {NULL}; \ #define DEFINE_WAIT_QUEUES(name, flag) static struct wait_queue *name = {NULL}; \
static volatile struct snd_wait flag = {{0}} static volatile struct snd_wait flag = {{0}}
#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;} #define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
#define PROCESS_ABORTING(q, f) (f.aborting | (current->signal & ~current->blocked)) #define PROCESS_ABORTING(q, f) (/*f.aborting | */(current->signal & ~current->blocked))
#define SET_ABORT_FLAG(q, f) f.aborting = 1 #define SET_ABORT_FLAG(q, f) f.aborting = 1
#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT) #define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
#define DO_SLEEP(q, f, time_limit) \ #define DO_SLEEP(q, f, time_limit) \
...@@ -141,7 +141,7 @@ struct snd_wait { ...@@ -141,7 +141,7 @@ struct snd_wait {
#define DEFINE_TIMER(name, proc) \ #define DEFINE_TIMER(name, proc) \
static struct timer_list name = \ static struct timer_list name = \
{NULL, 0, 0, 0, proc} {NULL, NULL, 0, 0, proc}
/* /*
* The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks. * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks.
...@@ -153,3 +153,9 @@ struct snd_wait { ...@@ -153,3 +153,9 @@ struct snd_wait {
#define INB inb #define INB inb
#define OUTB outb #define OUTB outb
/*
* SND_SA_INTERRUPT is required. Otherwise the IRQ number is not passed
* the handler.
*/
#define SND_SA_INTERRUPT
...@@ -135,25 +135,25 @@ ...@@ -135,25 +135,25 @@
#define PAS_16D 4 #define PAS_16D 4
#ifdef DEFINE_TRANSLATIONS #ifdef DEFINE_TRANSLATIONS
char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */ unsigned char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
{ 4, 1, 2, 3, 0, 5, 6, 7 }; { 4, 1, 2, 3, 0, 5, 6, 7 };
char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */ unsigned char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
{ 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 }; { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 };
char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */ unsigned char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
{ 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 }; { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 };
char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */ unsigned char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
{ 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 }; { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 };
char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */ unsigned char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
{ 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 }; { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 };
char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */ unsigned char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 }; { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 };
#else #else
extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */ extern unsigned 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 unsigned 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 unsigned 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 unsigned 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 unsigned 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 */ extern unsigned char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */
#endif #endif
#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */ #define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -67,7 +67,9 @@ void ...@@ -67,7 +67,9 @@ void
pmgr_release (int dev) pmgr_release (int dev)
{ {
if (mbox[dev]) /* Killed in action. Inform the client */ if (mbox[dev]) /*
* Killed in action. Inform the client
*/
{ {
mbox[dev]->key = PM_ERROR; mbox[dev]->key = PM_ERROR;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -42,23 +42,26 @@ ...@@ -42,23 +42,26 @@
* future version of this driver. * future version of this driver.
*/ */
extern int sb_dsp_ok; /* Set to 1 after successful initialization */ extern int sb_dsp_ok; /* Set to 1 atfer successful initialization */
extern int sbc_base;
extern int sb_midi_mode; extern int sb_midi_mode;
extern int sb_midi_busy; /* 1 if the process has output to MIDI */ extern int sb_midi_busy; /*
* * * * 1 if the process has output to MIDI
*
*/
extern int sb_dsp_busy; extern int sb_dsp_busy;
extern int sb_dsp_highspeed; extern int sb_dsp_highspeed;
extern volatile int sb_irq_mode;/* IMODE_INPUT, IMODE_OUTPUT extern volatile int sb_irq_mode;
* or IMODE_NONE */
extern int sb_duplex_midi; extern int sb_duplex_midi;
extern int sb_intr_active; extern int sb_intr_active;
extern int sbc_base; int input_opened = 0;
static int my_dev;
static int input_opened = 0; void (*midi_input_intr) (int dev, unsigned char data);
static void (*midi_input_intr) (int dev, unsigned char data);
static int my_dev = 0;
static int static int
sb_midi_open (int dev, int mode, sb_midi_open (int dev, int mode,
...@@ -74,10 +77,13 @@ sb_midi_open (int dev, int mode, ...@@ -74,10 +77,13 @@ sb_midi_open (int dev, int mode,
return RET_ERROR (ENXIO); return RET_ERROR (ENXIO);
} }
if (sb_midi_busy)
return RET_ERROR (EBUSY);
if (mode != OPEN_WRITE && !sb_duplex_midi) if (mode != OPEN_WRITE && !sb_duplex_midi)
{ {
if (num_midis == 1) if (num_midis == 1)
printk ("SoundBlaster: MIDI input not supported with plain SB\n"); printk ("SoundBlaster: Midi input not currently supported\n");
return RET_ERROR (EPERM); return RET_ERROR (EPERM);
} }
...@@ -101,20 +107,20 @@ sb_midi_open (int dev, int mode, ...@@ -101,20 +107,20 @@ sb_midi_open (int dev, int mode,
sb_reset_dsp (); sb_reset_dsp ();
if (!sb_dsp_command (0xf2)) /* This is undodumented, isn't it */
return RET_ERROR (EIO); /* be nice to DSP */
if (!sb_dsp_command (0x35)) if (!sb_dsp_command (0x35))
return RET_ERROR (EIO); /* Enter the UART mode */ return RET_ERROR (EIO); /*
* Enter the UART mode
*/
sb_intr_active = 1; sb_intr_active = 1;
if ((ret = sb_get_irq ()) < 0) if ((ret = sb_get_irq ()) < 0)
{ {
sb_reset_dsp (); sb_reset_dsp ();
return 0; /* IRQ not free */ return 0; /*
* IRQ not free
*/
} }
input_opened = 1; input_opened = 1;
my_dev = dev;
midi_input_intr = input; midi_input_intr = input;
} }
...@@ -128,7 +134,9 @@ sb_midi_close (int dev) ...@@ -128,7 +134,9 @@ sb_midi_close (int dev)
{ {
if (sb_midi_mode == UART_MIDI) if (sb_midi_mode == UART_MIDI)
{ {
sb_reset_dsp (); /* The only way to kill the UART mode */ sb_reset_dsp (); /*
* The only way to kill the UART mode
*/
sb_free_irq (); sb_free_irq ();
} }
sb_intr_active = 0; sb_intr_active = 0;
...@@ -141,8 +149,6 @@ sb_midi_out (int dev, unsigned char midi_byte) ...@@ -141,8 +149,6 @@ sb_midi_out (int dev, unsigned char midi_byte)
{ {
unsigned long flags; unsigned long flags;
sb_midi_busy = 1; /* Kill all notes after close */
if (sb_midi_mode == NORMAL_MIDI) if (sb_midi_mode == NORMAL_MIDI)
{ {
DISABLE_INTR (flags); DISABLE_INTR (flags);
...@@ -153,7 +159,9 @@ sb_midi_out (int dev, unsigned char midi_byte) ...@@ -153,7 +159,9 @@ sb_midi_out (int dev, unsigned char midi_byte)
RESTORE_INTR (flags); RESTORE_INTR (flags);
} }
else else
sb_dsp_command (midi_byte); /* UART write */ sb_dsp_command (midi_byte); /*
* UART write
*/
return 1; return 1;
} }
...@@ -201,23 +209,43 @@ sb_midi_interrupt (int dummy) ...@@ -201,23 +209,43 @@ sb_midi_interrupt (int dummy)
RESTORE_INTR (flags); RESTORE_INTR (flags);
} }
#define MIDI_SYNTH_NAME "SoundBlaster Midi"
#define MIDI_SYNTH_CAPS 0
#include "midi_synth.h"
static struct midi_operations sb_midi_operations = static struct midi_operations sb_midi_operations =
{ {
{"SoundBlaster", 0, 0, SNDCARD_SB}, {"SoundBlaster", 0, 0, SNDCARD_SB},
&std_midi_synth,
sb_midi_open, sb_midi_open,
sb_midi_close, sb_midi_close,
sb_midi_ioctl, sb_midi_ioctl,
sb_midi_out, sb_midi_out,
sb_midi_start_read, sb_midi_start_read,
sb_midi_end_read, sb_midi_end_read,
NULL, /* Kick */ NULL, /*
NULL, /* command */ * Kick
NULL /* buffer_status */ */
NULL, /*
* command
*/
NULL, /*
* buffer_status
*/
NULL
}; };
void void
sb_midi_init (int model) sb_midi_init (int model)
{ {
if (num_midis >= MAX_MIDI_DEV)
{
printk ("Sound: Too many midi devices detected\n");
return;
}
std_midi_synth.midi_dev = num_midis;
my_dev = num_midis;
midi_devs[num_midis++] = &sb_midi_operations; midi_devs[num_midis++] = &sb_midi_operations;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment