Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

369
sound/oss/CHANGELOG Normal file
View file

@ -0,0 +1,369 @@
Note these changes relate to Hannu's code and don't include the changes
made outside of this for modularising the sound
Changelog for version 3.8o
--------------------------
Since 3.8h
- Included support for OPL3-SA1 and SoftOSS
Since 3.8
- Fixed SNDCTL_DSP_GETOSPACE
- Compatibility fixes for Linux 2.1.47
Since 3.8-beta21
- Fixed all known bugs (I think).
Since 3.8-beta8
- Lot of fixes to audio playback code in dmabuf.c
Since 3.8-beta6
- Fixed the famous Quake delay bug.
Since 3.8-beta5
- Fixed many bugs in audio playback.
Since 3.8-beta4
- Just minor changes.
Since 3.8-beta1
- Major rewrite of audio playback handling.
- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
Since 3.7-beta#
- Passing of ioctl() parameters between soundcard.c and other modules has been
changed so that arg always points to kernel space.
- Some bugfixes.
Since 3.7-beta5
- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
stream of received 0x00 bytes when the MIDI receiver is enabled.
Since 3.5
- Changes almost everywhere.
- Support for OPTi 82C924-based sound cards.
Since 3.5.4-beta8
- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
with GUS.
- Limited minimum fragment size with some audio devices (GUS=512 and
SB=32). These devices require more time to "recover" from processing
of each fragment.
Since 3.5.4-beta6/7
- There seems to be problems in the OPTi 82C930 so cards based on this
chip don't necessarily work yet. There are problems in detecting the
MIDI interface. Also mixer volumes may be seriously wrong on some systems.
You can safely use this driver version with C930 if it looks to work.
However please don't complain if you have problems with it. C930 support
should be fixed in future releases.
- Got initialization of GUS PnP to work. With this version GUS PnP should
work in GUS compatible mode after initialization using isapnptools.
- Fixed a bug in handling of full duplex cards in write only mode. This has
been causing "audio device opening" errors with RealAudio player.
Since 3.5.4.beta5
- Changes to OPTi 82C930 driver.
- Major changes to the Soundscape driver. The driver requires now just one
DMA channel. The extra audio/dsp device (the "Not functional" one) used
for code download in the earlier versions has been eliminated. There is now
just one /dev/dsp# device which is used both for code download and audio.
Since 3.5.4.beta4
- Minor changes.
Since 3.5.4-beta2
- Fixed silent playback with ESS 688/1688.
- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one
is required for 8 and 16 bit modes).
- Added the "lowlevel" subdirectory for additional low level drivers that
are not part of USS core. See lowlevel/README for more info.
- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
miroPCM sound cards. See lowlevel/aci.readme for more info.
- Support for Aztech Washington chipset (AZT2316 ASIC).
Since 3.5.4-beta1
- Reduced clicking with AD1848.
- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback
is sometimes just white noise (occurs randomly).
Since 3.5.2
- Major changes to the SB/Jazz16/ESS driver (most parts rewritten).
The most noticeable new feature is support for multiple SB cards at the same
time.
- Renamed sb16_midi.c to uart401.c. Also modified it to work also with
other MPU401 UART compatible cards than SB16/ESS/Jazz.
- Some changes which reduce clicking in audio playback.
- Copying policy is now GPL.
Since 3.5.1
- TB Maui initialization support
Since 3.5
- Improved handling of playback underrun situations.
Since 3.5-beta10
- Bug fixing
Since 3.5-beta9
- Fixed for compatibility with Linux 1.3.70 and later.
- Changed boot time passing of 16 bit DMA channel number to SB driver.
Since 3.5-beta8
- Minor changes
Since 3.5-beta7
- enhancements to configure program (by Jeff Tranter):
- prompts are in same format as 1.3.x Linux kernel config program
- on-line help for each question
- fixed some compile warnings detected by gcc/g++ -Wall
- minor grammatical changes to prompts
Since 3.5-beta6
- Fixed bugs in mmap() support.
- Minor changes to Maui driver.
Since 3.5-beta5
- Fixed crash after recording with ESS688. It's generally a good
idea to stop inbound DMA transfers before freeing the memory
buffer.
- Fixed handling of AD1845 codec (for example Shuttle Sound System).
- Few other fixes.
Since 3.5-beta4
- Fixed bug in handling of uninitialized instruments with GUS.
Since 3.5-beta3
- Few changes which decrease popping at end/beginning of audio playback.
Since 3.5-beta2
- Removed MAD16+CS4231 hack made in previous version since it didn't
help.
- Fixed the above bug in proper way and in proper place. Many thanks
to James Hightower.
Since 3.5-beta1
- Bug fixes.
- Full duplex audio with MAD16+CS4231 may work now. The driver configures
SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels.
The side effect is that all 8 bit DMA channels (0,1,3) are populated in
duplex mode.
Since 3.5-alpha9
- Bug fixes (mostly in Jazz16 and ESS1688/688 supports).
- Temporarily disabled recording with ESS1688/688 since it causes crash.
- Changed audio buffer partitioning algorithm so that it selects
smaller fragment size than earlier. This improves real time capabilities
of the driver and makes recording to disk to work better. Unfortunately
this change breaks some programs which assume that fragments cannot be
shorter than 4096 bytes.
Since 3.5-alpha8
- Bug fixes
Since 3.5-alpha7
- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable
using command "cd /linux/drivers/sound;make script" and then
just run kernel's make config normally.
- Minor fixes to the SB support. Hopefully the driver works with
all SB models now.
- Added support for ESS ES1688 "AudioDrive" based cards.
Since 3.5-alpha6
- SB Pro and SB16 supports are no longer separately selectable options.
Enabling SB enables them too.
- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified
configure to handle this.
- Removed initialization messages from the
modularized version. They can be enabled by using init_trace=1 in
the insmod command line (insmod sound init_trace=1).
- More AIX stuff.
- Added support for synchronizing dsp/audio devices with /dev/sequencer.
- mmap() support for dsp/audio devices.
Since 3.5-alpha5
- AIX port.
- Changed some xxx_PATCH macros in soundcard.h to work with
big endian machines.
Since 3.5-alpha4
- Removed the 'setfx' stuff from the version distributed with kernel
sources. Running 'setfx' is required again.
Since 3.5-alpha3
- Moved stuff from the 'setfx' program to the AudioTrix Pro driver.
Since 3.5-alpha2
- Modifications to makefile and configure.c. Unnecessary sources
are no longer compiled. Newly created local.h is also copied to
/etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
new local.h which is compatible with current version of the driver.
- Some fixes to the SB16 support.
- Fixed random protection fault in gus_wave.c
Since 3.5-alpha1
- Modified to work with Linux-1.3.33 and later
- Some minor changes
Since 3.0.2
- Support for CS4232 based PnP cards (AcerMagic S23 etc).
- Full duplex support for some CS4231, CS4232 and AD1845 based cards
(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards
having a codec mentioned above).
- Almost fully rewritten loadable modules support.
- Fixed some bugs.
- Huge amount of testing (more testing is still required).
- mmap() support (works with some cards). Requires much more testing.
- Sample/patch/program loading for TB Maui/Tropez. No initialization
since TB doesn't allow me to release that code.
- Using CS4231 compatible codecs as timer for /dev/music.
Since 3.0.1
- Added allocation of I/O ports, DMA channels and interrupts
to the initialization code. This may break modules support since
the driver may not free some resources on unload. Should be fixed soon.
Since 3.0
- Some important bug fixes.
- select() for /dev/dsp and /dev/audio (Linux only).
(To use select() with read, you have to call read() to start
the recording. Calling write() kills recording immediately so
use select() carefully when you are writing a half duplex app.
Full duplex mode is not implemented yet.) Select works also with
/dev/sequencer and /dev/music. Maybe with /dev/midi## too.
Since 3.0-beta2
- Minor fixes.
- Added Readme.cards
Since 3.0-beta1
- Minor fixes to the modules support.
- Eliminated call to sb_free_irq() in ad1848.c
- Rewritten MAD16&Mozart support (not tested with MAD16 Pro).
- Fix to DMA initialization of PSS cards.
- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.)
- Fixed some bugs in the PSS driver which caused I/O errors with
the MSS mode (/dev/dsp).
Since 3.0-950506
- Recording with GUS MAX fixed. It works when the driver is configured
to use two DMA channels with GUS MAX (16 bit ones recommended).
Since 3.0-94xxxx
- Too many changes
Since 3.0-940818
- Fixes for Linux 1.1.4x.
- Disables Disney Sound System with SG NX Pro 16 (less noise).
Since 2.90-2
- Fixes to soundcard.h
- Non blocking mode to /dev/sequencer
- Experimental detection code for Ensoniq Soundscape.
Since 2.90
- Minor and major bug fixes
Since pre-3.0-940712
- 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 sound cards. This version is developed
for the 16 bit daughtercard of GUS. It should work with other cards also
if the following requirements are met:
- The I/O, IRQ and DMA settings are jumper selectable or
the card is initialized by booting DOS before booting Linux (etc.).
- You add the IO, IRQ and DMA settings manually to the local.h.
(Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
the base address bust be the base address of the codec chip not the
card itself. For the GUS16 these are the same but most MSS compatible
cards have the codec located at card_base+4.
- Some minor changes
Since 2.5 (******* MAJOR REWRITE ***********)
This version is based on v2.3. I have tried to maintain two versions
together so that this one should have the same features than v2.5.
Something may still be missing. If you notice such things, please let me
know.
The Readme.v30 contains more details.
- /dev/midi## devices.
- /dev/sequencer2
Since 2.5-beta2
- Some fine tuning to the GUS v3.7 mixer code.
- Fixed speed limits for the plain SB (1.0 to 2.0).
Since 2.5-beta
- Fixed OPL-3 detection with SB. Caused problems with PAS16.
- GUS v3.7 mixer support.
Since 2.4
- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
- Fixed truncated sound on /dev/dsp when the device is closed.
- Linear volume mode for GUS
- Pitch bends larger than +/- 2 octaves.
- MIDI recording for SB and SB Pro. (Untested).
- Some other fixes.
- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
- Implemented better detection for OPL-3. This should be useful if you
have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
Since 2.3b
- Fixed bug which made it impossible to make long recordings to disk.
Recording was not restarted after a buffer overflow situation.
- Limited mixer support for GUS.
- Numerous improvements to the GUS driver by Andrew Robinson. Including
some click removal etc.
Since 2.3
- Fixed some minor bugs in the SB16 driver.
Since 2.2b
- Full SB16 DSP support. 8/16 bit, mono/stereo
- The SCO and FreeBSD versions should be in sync now. There are some
problems with SB16 and GUS in the FreeBSD versions.
The DMA buffer allocation of the SCO version has been polished but
there could still be some problems. At least it hogs memory.
The DMA channel
configuration method used in the SCO/System is a hack.
- Support for the MPU emulation of the SB16.
- Some big arrays are now allocated boot time. This makes the BSS segment
smaller which makes it possible to use the full driver with
NetBSD. These arrays are not allocated if no suitable sound card is available.
- Fixed a bug in the compute_and_set_volume in gus_wave.c
- Fixed the too fast mono playback problem of SB Pro and PAS16.
Since 2.2
- Stereo recording for SB Pro. Somehow it was missing and nobody
had noticed it earlier.
- Minor polishing.
- Interpreting of boot time arguments (sound=) for Linux.
- Breakup of sb_dsp.c. Parts of the code has been moved to
sb_mixer.c and sb_midi.c
Since 2.1
- Preliminary support for SB16.
- The SB16 mixer is supported in its native mode.
- Digitized voice capability up to 44.1 kHz/8 bit/mono
(16 bit and stereo support coming in the next release).
- Fixed some bugs in the digitized voice driver for PAS16.
- Proper initialization of the SB emulation of latest PAS16 models.
- Significantly improved /dev/dsp and /dev/audio support.
- Now supports half duplex mode. It's now possible to record and
playback without closing and reopening the device.
- It's possible to use smaller buffers than earlier. There is a new
ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
This call instructs the driver to use smaller buffers. The default
buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
immediately after opening the device.
Since 2.0
Just cosmetic changes.

533
sound/oss/Kconfig Normal file
View file

@ -0,0 +1,533 @@
# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
# More hacking for modularisation.
#
# Prompt user for primary drivers.
config SOUND_BCM_CS4297A
tristate "Crystal Sound CS4297a (for Swarm)"
depends on SIBYTE_SWARM
help
The BCM91250A has a Crystal CS4297a on synchronous serial
port B (in addition to the DB-9 serial port). Say Y or M
here to enable the sound chip instead of the UART. Also
note that CONFIG_KGDB should not be enabled at the same
time, since it also attempts to use this UART port.
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
depends on (m || !STANDALONE) && ISA
help
Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
Monterey (not for the Pinnacle or Fiji).
See <file:Documentation/sound/oss/MultiSound> for important information
about this driver. Note that it has been discontinued, but the
Voyetra Turtle Beach knowledge base entry for it is still available
at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
comment "Compiled-in MSND Classic support requires firmware during compilation."
depends on SOUND_PRIME && SOUND_MSNDCLAS=y
config MSNDCLAS_HAVE_BOOT
bool
depends on SOUND_MSNDCLAS=y && !STANDALONE
default y
config MSNDCLAS_INIT_FILE
string "Full pathname of MSNDINIT.BIN firmware file"
depends on SOUND_MSNDCLAS
default "/etc/sound/msndinit.bin"
help
The MultiSound cards have two firmware files which are required for
operation, and are not currently included. These files can be
obtained from Turtle Beach. See
<file:Documentation/sound/oss/MultiSound> for information on how to
obtain this.
config MSNDCLAS_PERM_FILE
string "Full pathname of MSNDPERM.BIN firmware file"
depends on SOUND_MSNDCLAS
default "/etc/sound/msndperm.bin"
help
The MultiSound cards have two firmware files which are required for
operation, and are not currently included. These files can be
obtained from Turtle Beach. See
<file:Documentation/sound/oss/MultiSound> for information on how to
obtain this.
config MSNDCLAS_IRQ
int "MSND Classic IRQ 5, 7, 9, 10, 11, 12"
depends on SOUND_MSNDCLAS=y
default "5"
help
Interrupt Request line for the MultiSound Classic and related cards.
config MSNDCLAS_MEM
hex "MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000"
depends on SOUND_MSNDCLAS=y
default "D0000"
help
Memory-mapped I/O base address for the MultiSound Classic and
related cards.
config MSNDCLAS_IO
hex "MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
depends on SOUND_MSNDCLAS=y
default "290"
help
I/O port address for the MultiSound Classic and related cards.
config SOUND_MSNDPIN
tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
depends on (m || !STANDALONE) && ISA
help
Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
See <file:Documentation/sound/oss/MultiSound> for important information
about this driver. Note that it has been discontinued, but the
Voyetra Turtle Beach knowledge base entry for it is still available
at <http://www.turtlebeach.com/site/kb_ftp/600.asp>.
comment "Compiled-in MSND Pinnacle support requires firmware during compilation."
depends on SOUND_PRIME && SOUND_MSNDPIN=y
config MSNDPIN_HAVE_BOOT
bool
depends on SOUND_MSNDPIN=y
default y
config MSNDPIN_INIT_FILE
string "Full pathname of PNDSPINI.BIN firmware file"
depends on SOUND_MSNDPIN
default "/etc/sound/pndspini.bin"
help
The MultiSound cards have two firmware files which are required
for operation, and are not currently included. These files can be
obtained from Turtle Beach. See
<file:Documentation/sound/oss/MultiSound> for information on how to
obtain this.
config MSNDPIN_PERM_FILE
string "Full pathname of PNDSPERM.BIN firmware file"
depends on SOUND_MSNDPIN
default "/etc/sound/pndsperm.bin"
help
The MultiSound cards have two firmware files which are required for
operation, and are not currently included. These files can be
obtained from Turtle Beach. See
<file:Documentation/sound/oss/MultiSound> for information on how to
obtain this.
config MSNDPIN_IRQ
int "MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12"
depends on SOUND_MSNDPIN=y
default "5"
help
Interrupt request line for the primary synthesizer on MultiSound
Pinnacle and Fiji sound cards.
config MSNDPIN_MEM
hex "MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000"
depends on SOUND_MSNDPIN=y
default "D0000"
help
Memory-mapped I/O base address for the primary synthesizer on
MultiSound Pinnacle and Fiji sound cards.
config MSNDPIN_IO
hex "MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
depends on SOUND_MSNDPIN=y
default "290"
help
Memory-mapped I/O base address for the primary synthesizer on
MultiSound Pinnacle and Fiji sound cards.
config MSNDPIN_DIGITAL
bool "MSND Pinnacle has S/PDIF I/O"
depends on SOUND_MSNDPIN=y
help
If you have the S/PDIF daughter board for the Pinnacle or Fiji,
answer Y here; otherwise, say N. If you have this, you will be able
to play and record from the S/PDIF port (digital signal). See
<file:Documentation/sound/oss/MultiSound> for information on how to make
use of this capability.
config MSNDPIN_NONPNP
bool "MSND Pinnacle non-PnP Mode"
depends on SOUND_MSNDPIN=y
help
The Pinnacle and Fiji card resources can be configured either with
PnP, or through a configuration port. Say Y here if your card is NOT
in PnP mode. For the Pinnacle, configuration in non-PnP mode allows
use of the IDE and joystick peripherals on the card as well; these
do not show up when the card is in PnP mode. Specifying zero for any
resource of a device will disable the device. If you are running the
card in PnP mode, you must say N here and use isapnptools to
configure the card's resources.
comment "MSND Pinnacle DSP section will be configured to above parameters."
depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
config MSNDPIN_CFG
hex "MSND Pinnacle config port 250,260,270"
depends on MSNDPIN_NONPNP
default "250"
help
This is the port which the Pinnacle and Fiji uses to configure the
card's resources when not in PnP mode. If your card is in PnP mode,
then be sure to say N to the previous option, "MSND Pinnacle Non-PnP
Mode".
comment "Pinnacle-specific Device Configuration (0 disables)"
depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
config MSNDPIN_MPU_IO
hex "MSND Pinnacle MPU I/O (e.g. 330)"
depends on MSNDPIN_NONPNP
default "0"
help
Memory-mapped I/O base address for the Kurzweil daughterboard
synthesizer on MultiSound Pinnacle and Fiji sound cards.
config MSNDPIN_MPU_IRQ
int "MSND Pinnacle MPU IRQ (e.g. 9)"
depends on MSNDPIN_NONPNP
default "0"
help
Interrupt request number for the Kurzweil daughterboard
synthesizer on MultiSound Pinnacle and Fiji sound cards.
config MSNDPIN_IDE_IO0
hex "MSND Pinnacle IDE I/O 0 (e.g. 170)"
depends on MSNDPIN_NONPNP
default "0"
help
CD-ROM drive 0 memory-mapped I/O base address for the MultiSound
Pinnacle and Fiji sound cards.
config MSNDPIN_IDE_IO1
hex "MSND Pinnacle IDE I/O 1 (e.g. 376)"
depends on MSNDPIN_NONPNP
default "0"
help
CD-ROM drive 1 memory-mapped I/O base address for the MultiSound
Pinnacle and Fiji sound cards.
config MSNDPIN_IDE_IRQ
int "MSND Pinnacle IDE IRQ (e.g. 15)"
depends on MSNDPIN_NONPNP
default "0"
help
Interrupt request number for the IDE CD-ROM interface on the
MultiSound Pinnacle and Fiji sound cards.
config MSNDPIN_JOYSTICK_IO
hex "MSND Pinnacle joystick I/O (e.g. 200)"
depends on MSNDPIN_NONPNP
default "0"
help
Memory-mapped I/O base address for the joystick port on MultiSound
Pinnacle and Fiji sound cards.
config MSND_FIFOSIZE
int "MSND buffer size (kB)"
depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y
default "128"
help
Configures the size of each audio buffer, in kilobytes, for
recording and playing in the MultiSound drivers (both the Classic
and Pinnacle). Larger values reduce the chance of data overruns at
the expense of overall latency. If unsure, use the default.
menuconfig SOUND_OSS
tristate "OSS sound modules"
depends on ISA_DMA_API && VIRT_TO_BUS
depends on !GENERIC_ISA_DMA_SUPPORT_BROKEN
help
OSS is the Open Sound System suite of sound card drivers. They make
sound programming easier since they provide a common API. Say Y or
M here (the module will be called sound) if you haven't found a
driver for your sound card above, then pick your driver from the
list below.
if SOUND_OSS
config SOUND_TRACEINIT
bool "Verbose initialisation"
help
Verbose soundcard initialization -- affects the format of autoprobe
and initialization messages at boot time.
config SOUND_DMAP
bool "Persistent DMA buffers"
---help---
Linux can often have problems allocating DMA buffers for ISA sound
cards on machines with more than 16MB of RAM. This is because ISA
DMA buffers must exist below the 16MB boundary and it is quite
possible that a large enough free block in this region cannot be
found after the machine has been running for a while. If you say Y
here the DMA buffers (64Kb) will be allocated at boot time and kept
until the shutdown. This option is only useful if you said Y to
"OSS sound modules", above. If you said M to "OSS sound modules"
then you can get the persistent DMA buffer functionality by passing
the command-line argument "dmabuf=1" to the sound module.
Say Y unless you have 16MB or more RAM or a PCI sound card.
config SOUND_VMIDI
tristate "Loopback MIDI device support"
help
Support for MIDI loopback on port 1 or 2.
config SOUND_TRIX
tristate "MediaTrix AudioTrix Pro support"
help
Answer Y if you have the AudioTriX Pro sound card manufactured
by MediaTrix.
config TRIX_HAVE_BOOT
bool "Have TRXPRO.HEX firmware file"
depends on SOUND_TRIX=y && !STANDALONE
help
The MediaTrix AudioTrix Pro has an on-board microcontroller which
needs to be initialized by downloading the code from the file
TRXPRO.HEX in the DOS driver directory. If you don't have the
TRXPRO.HEX file handy you may skip this step. However, the SB and
MPU-401 modes of AudioTrix Pro will not work without this file!
config TRIX_BOOT_FILE
string "Full pathname of TRXPRO.HEX firmware file"
depends on TRIX_HAVE_BOOT
default "/etc/sound/trxpro.hex"
help
Enter the full pathname of your TRXPRO.HEX file, starting from /.
config SOUND_MSS
tristate "Microsoft Sound System support"
---help---
Again think carefully before answering Y to this question. It's
safe to answer Y if you have the original Windows Sound System card
made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may
say Y in case your card is NOT among these:
ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16,
Ensoniq SoundScape (and compatibles made by Reveal and Spea),
Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max,
Gravis Ultrasound with 16 bit option, Logitech Sound Man 16,
Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi
82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft
Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid
SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro
Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface,
Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound
Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M
notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM
synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface.
For cards having native support in VoxWare, consult the card
specific instructions in <file:Documentation/sound/oss/README.OSS>.
Some drivers have their own MSS support and saying Y to this option
will cause a conflict.
If you compile the driver into the kernel, you have to add
"ad1848=<io>,<irq>,<dma>,<dma2>[,<type>]" to the kernel command
line.
config SOUND_MPU401
tristate "MPU-401 support (NOT for SB16)"
---help---
Be careful with this question. The MPU401 interface is supported by
all sound cards. However, some natively supported cards have their
own driver for MPU401. Enabling this MPU401 option with these cards
will cause a conflict. Also, enabling MPU401 on a system that
doesn't really have a MPU401 could cause some trouble. If your card
was in the list of supported cards, look at the card specific
instructions in the <file:Documentation/sound/oss/README.OSS> file. It
is safe to answer Y if you have a true MPU401 MIDI interface card.
If you compile the driver into the kernel, you have to add
"mpu401=<io>,<irq>" to the kernel command line.
config SOUND_PAS
tristate "ProAudioSpectrum 16 support"
---help---
Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
16 or Logitech SoundMan 16 sound card. Answer N if you have some
other card made by Media Vision or Logitech since those are not
PAS16 compatible. Please read <file:Documentation/sound/oss/PAS16>.
It is not necessary to add Sound Blaster support separately; it
is included in PAS support.
If you compile the driver into the kernel, you have to add
"pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2>
to the kernel command line.
config PAS_JOYSTICK
bool "Enable PAS16 joystick port"
depends on SOUND_PAS=y
help
Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick
port.
config SOUND_PSS
tristate "PSS (AD1848, ADSP-2115, ESC614) support"
help
Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on
how to compile it into the kernel or as a module see the file
<file:Documentation/sound/oss/PSS>.
If you compile the driver into the kernel, you have to add
"pss=<io>,<mssio>,<mssirq>,<mssdma>,<mpuio>,<mpuirq>" to the kernel
command line.
config PSS_MIXER
bool "Enable PSS mixer (Beethoven ADSP-16 and other compatible)"
depends on SOUND_PSS
help
Answer Y for Beethoven ADSP-16. You may try to say Y also for other
cards if they have master volume, bass, treble, and you can't
control it under Linux. If you answer N for Beethoven ADSP-16, you
can't control master volume, bass, treble and synth volume.
If you said M to "PSS support" above, you may enable or disable this
PSS mixer with the module parameter pss_mixer. For more information
see the file <file:Documentation/sound/oss/PSS>.
config PSS_HAVE_BOOT
bool "Have DSPxxx.LD firmware file"
depends on SOUND_PSS && !STANDALONE
help
If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y
to include this file. Without this file the synth device (OPL) may
not work.
config PSS_BOOT_FILE
string "Full pathname of DSPxxx.LD firmware file"
depends on PSS_HAVE_BOOT
default "/etc/sound/dsp001.ld"
help
Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file,
starting from /.
config SOUND_SB
tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
---help---
Answer Y if you have an original Sound Blaster card made by Creative
Labs or a 100% hardware compatible clone (like the Thunderboard or
SM Games). For an unknown card you may answer Y if the card claims
to be Sound Blaster-compatible.
Please read the file <file:Documentation/sound/oss/Soundblaster>.
You should also say Y here for cards based on the Avance Logic
ALS-007 and ALS-1X0 chips (read <file:Documentation/sound/oss/ALS>) and
for cards based on ESS chips (read
<file:Documentation/sound/oss/ESS1868> and
<file:Documentation/sound/oss/ESS>). If you have an IBM Mwave
card, say Y here and read <file:Documentation/sound/oss/mwave>.
If you compile the driver into the kernel and don't want to use
isapnp, you have to add "sb=<io>,<irq>,<dma>,<dma2>" to the kernel
command line.
You can say M here to compile this driver as a module; the module is
called sb.
config SOUND_YM3812
tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
---help---
Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
Answering Y is usually a safe and recommended choice, however some
cards may have software (TSR) FM emulation. Enabling FM support with
these cards may cause trouble (I don't currently know of any such
cards, however). Please read the file
<file:Documentation/sound/oss/OPL3> if your card has an OPL3 chip.
If you compile the driver into the kernel, you have to add
"opl3=<io>" to the kernel command line.
If unsure, say Y.
config SOUND_UART6850
tristate "6850 UART support"
help
This option enables support for MIDI interfaces based on the 6850
UART chip. This interface is rarely found on sound cards. It's safe
to answer N to this question.
If you compile the driver into the kernel, you have to add
"uart6850=<io>,<irq>" to the kernel command line.
config SOUND_AEDSP16
tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
---help---
Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
driver supports Audio Excel DSP 16 but not the III nor PnP versions
of this card.
The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or
a Microsoft Sound System card, so you should have said Y to either
"100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
or "Microsoft Sound System support", above, and you need to answer
the "MSS emulation" and "SBPro emulation" questions below
accordingly. You should say Y to one and only one of these two
questions.
Read the <file:Documentation/sound/oss/README.OSS> file and the head of
<file:sound/oss/aedsp16.c> as well as
<file:Documentation/sound/oss/AudioExcelDSP16> to get more information
about this driver and its configuration.
config SC6600
bool "SC-6600 based audio cards (new Audio Excel DSP 16)"
depends on SOUND_AEDSP16
help
The SC6600 is the new version of DSP mounted on the Audio Excel DSP
16 cards. Find in the manual the FCC ID of your audio card and
answer Y if you have an SC6600 DSP.
config SC6600_JOY
bool "Activate SC-6600 Joystick Interface"
depends on SC6600
help
Say Y here in order to use the joystick interface of the Audio Excel
DSP 16 card.
config SC6600_CDROM
int "SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)"
depends on SC6600
default "4"
help
This is used to activate the CD-ROM interface of the Audio Excel
DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no
CD-ROM present.
config SC6600_CDROMBASE
hex "SC-6600 CDROM Interface I/O Address"
depends on SC6600
default "0"
help
Base I/O port address for the CD-ROM interface of the Audio Excel
DSP 16 card.
config SOUND_VIDC
tristate "VIDC 16-bit sound"
depends on ARM && ARCH_ACORN
help
16-bit support for the VIDC onboard sound hardware found on Acorn
machines.
config SOUND_WAVEARTIST
tristate "Netwinder WaveArtist"
depends on ARM && ARCH_NETWINDER
help
Say Y here to include support for the Rockwell WaveArtist sound
system. This driver is mainly for the NetWinder.
config SOUND_KAHLUA
tristate "XpressAudio Sound Blaster emulation"
depends on SOUND_SB
endif # SOUND_OSS

107
sound/oss/Makefile Normal file
View file

@ -0,0 +1,107 @@
# Makefile for the Linux sound card driver
#
# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
# Rewritten to use lists instead of if-statements.
# Each configuration option enables a list of files.
obj-$(CONFIG_SOUND_OSS) += sound.o
# Please leave it as is, cause the link order is significant !
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_MSS) += ad1848.o
obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o
obj-$(CONFIG_SOUND_MPU401) += mpu401.o
obj-$(CONFIG_SOUND_UART6850) += uart6850.o
obj-$(CONFIG_SOUND_YM3812) += opl3.o
obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_DMASOUND) += dmasound/
# Declare multi-part drivers.
sound-objs := \
dev_table.o soundcard.o \
audio.o dmabuf.o \
midi_synth.o midibuf.o \
sequencer.o sound_timer.o sys_timer.o
pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
sb-objs := sb_card.o
sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
vidc_mod-objs := vidc.o vidc_fill.o
hostprogs-y := bin2hex hex2hex
# Files generated that shall be removed upon make clean
clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \
pss_boot.h trix_boot.h
# Firmware files that need translation
#
# The translated files are protected by a file that keeps track
# of what name was used to build them. If the name changes, they
# will be forced to be remade.
#
# Turtle Beach MultiSound
ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
$(obj)/msnd_classic.o: $(obj)/msndperm.c $(obj)/msndinit.c
$(obj)/msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) $(obj)/bin2hex
$(obj)/bin2hex msndperm < $< > $@
$(obj)/msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex msndinit < $< > $@
endif
ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
$(obj)/msnd_pinnacle.o: $(obj)/pndsperm.c $(obj)/pndspini.c
$(obj)/pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) $(obj)/bin2hex
$(obj)/bin2hex pndsperm < $< > $@
$(obj)/pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex pndspini < $< > $@
endif
# PSS (ECHO-ADI2111)
$(obj)/pss.o: $(obj)/pss_boot.h
ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
$(obj)/pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex pss_synth < $< > $@
else
$(obj)/pss_boot.h:
$(Q)( \
echo 'static unsigned char * pss_synth = NULL;'; \
echo 'static int pss_synthLen = 0;'; \
) > $@
endif
# MediaTrix AudioTrix Pro
$(obj)/trix.o: $(obj)/trix_boot.h
ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
$(obj)/trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) $(obj)/hex2hex
$(obj)/hex2hex -i trix_boot < $< > $@
else
$(obj)/trix_boot.h:
$(Q)( \
echo 'static unsigned char * trix_boot = NULL;'; \
echo 'static int trix_boot_len = 0;'; \
) > $@
endif

6
sound/oss/README.FIRST Normal file
View file

@ -0,0 +1,6 @@
The modular sound driver patches were funded by Red Hat Software
(www.redhat.com). The sound driver here is thus a modified version of
Hannu's code. Please bear that in mind when considering the appropriate
forums for bug reporting.
Alan Cox

3065
sound/oss/ad1848.c Normal file

File diff suppressed because it is too large Load diff

24
sound/oss/ad1848.h Normal file
View file

@ -0,0 +1,24 @@
#include <linux/interrupt.h>
#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */
#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */
#define AD1848_SET_XTAL 1
#define AD1848_MIXER_REROUTE 2
#define AD1848_REROUTE(oldctl, newctl) \
ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback,
int dma_capture, int share_dma, int *osp, struct module *owner);
void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
int ad1848_detect (struct resource *ports, int *flags, int *osp);
int ad1848_control(int cmd, int arg);
void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner);
int probe_ms_sound(struct address_info *hw_config, struct resource *ports);
void unload_ms_sound(struct address_info *hw_info);

253
sound/oss/ad1848_mixer.h Normal file
View file

@ -0,0 +1,253 @@
/*
* sound/oss/ad1848_mixer.h
*
* Definitions for the mixer of AD1848 and compatible codecs.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
* Sound card manufacturers have connected actual inputs (CD, synth, line,
* etc) to these inputs in different order. Therefore it's difficult
* to assign mixer channels to these inputs correctly. The following
* contains two alternative mappings. The first one is for GUS MAX and
* the second is just a generic one (line1, line2 and line3).
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
SOUND_MASK_LINE1 | SOUND_MASK_IMIX)
#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_LINE1)
#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
SOUND_MASK_MIC | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME)
/* OPTi 82C930 has no IMIX level control, but it can still be selected as an
* input
*/
#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
SOUND_MASK_MIC | SOUND_MASK_VOLUME | \
SOUND_MASK_LINE3 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM)
#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_SYNTH | \
SOUND_MASK_CD | SOUND_MASK_MIC | \
SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \
SOUND_MASK_OGAIN)
struct mixer_def {
unsigned int regno:6; /* register number for volume */
unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */
unsigned int bitpos:3; /* position of bits in register for volume */
unsigned int nbits:3; /* number of bits in register for volume */
unsigned int mutereg:6; /* register number for mute bit */
unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */
unsigned int mutepos:4; /* position of mute bit in register */
unsigned int recreg:6; /* register number for recording bit */
unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */
unsigned int recpos:4; /* position of recording bit in register */
};
static char mix_cvt[101] = {
0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
100
};
typedef struct mixer_def mixer_ent;
typedef mixer_ent mixer_ents[2];
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
*
* The channel numbering used by individual sound cards is not fixed. Some
* cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
* The current version doesn't try to compensate this.
*/
#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \
[name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \
{reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}}
#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
rec_reg_l, rec_pola_l, rec_pos_l, \
reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
rec_reg_r, rec_pola_r, rec_pos_r) \
[name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
rec_reg_l, rec_pola_l, rec_pos_l}, \
{reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
rec_reg_r, rec_pola_r, rec_pos_r}}
static mixer_ents ad1848_mix_devices[32] = {
MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
};
static mixer_ents iwave_mix_devices[32] = {
MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
};
static mixer_ents cs42xb_mix_devices[32] = {
/* Digital master volume actually has seven bits, but we only use
six to avoid the discontinuity when the analog gain kicks in. */
MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
/* For the IMIX entry, it was not possible to use the MIX_ENT macro
because the mute bit is in different positions for the two
channels and requires reverse polarity. */
[SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},
{42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7)
};
/* OPTi 82C930 has somewhat different port addresses.
* Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1
* VOLUME, SYNTH, LINE, CD are not enabled above.
* MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
*/
static mixer_ents c930_mix_devices[32] = {
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7)
};
static mixer_ents spro_mix_devices[32] = {
MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8),
MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8,
5, 1, 1, 4, 23, 0, 3, 0, 0, 8),
MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8),
MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8),
MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2,
20, 0, 0, 4, 17, 1, 3, 16, 0, 1),
MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4,
21, 0, 0, 4, 17, 1, 1, 16, 0, 3),
MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8),
/* This is external wavetable */
MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4,
22, 0, 0, 4, 23, 1, 0, 23, 0, 5),
};
static int default_mixer_levels[32] =
{
0x3232, /* Master Volume */
0x3232, /* Bass */
0x3232, /* Treble */
0x4b4b, /* FM */
0x3232, /* PCM */
0x1515, /* PC Speaker */
0x2020, /* Ext Line */
0x1010, /* Mic */
0x4b4b, /* CD */
0x0000, /* Recording monitor */
0x4b4b, /* Second PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b, /* Output gain */
0x2020, /* Line1 */
0x2020, /* Line2 */
0x1515 /* Line3 (usually line in)*/
};
#define LEFT_CHN 0
#define RIGHT_CHN 1
/*
* Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1)
*/
#ifndef AUDIO_SPEAKER
#define AUDIO_SPEAKER 0x01 /* Enable mono output */
#define AUDIO_HEADPHONE 0x02 /* Sparc only */
#define AUDIO_LINE_OUT 0x04 /* Sparc only */
#endif

1373
sound/oss/aedsp16.c Normal file

File diff suppressed because it is too large Load diff

985
sound/oss/audio.c Normal file
View file

@ -0,0 +1,985 @@
/*
* sound/oss/audio.c
*
* Device file manager for /dev/audio
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Thomas Sailer : moved several static variables into struct audio_operations
* (which is grossly misnamed btw.) because they have the same
* lifetime as the rest in there and dynamic allocation saves
* 12k or so
* Thomas Sailer : use more logical O_NONBLOCK semantics
* Daniel Rodriksson: reworked the use of the device specific copy_user
* still generic
* Horst von Brand: Add missing #include <linux/string.h>
* Chris Rankin : Update the module-usage counter for the coprocessor,
* and decrement the counters again if we cannot open
* the audio device.
*/
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/kmod.h>
#include "sound_config.h"
#include "ulaw.h"
#include "coproc.h"
#define NEUTRAL8 0x80
#define NEUTRAL16 0x00
static int dma_ioctl(int dev, unsigned int cmd, void __user *arg);
static int set_format(int dev, int fmt)
{
if (fmt != AFMT_QUERY)
{
audio_devs[dev]->local_conversion = 0;
if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
{
if (fmt == AFMT_MU_LAW)
{
fmt = AFMT_U8;
audio_devs[dev]->local_conversion = CNV_MU_LAW;
}
else
fmt = AFMT_U8; /* This is always supported */
}
audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
audio_devs[dev]->local_format = fmt;
}
else
return audio_devs[dev]->local_format;
if (audio_devs[dev]->local_conversion)
return audio_devs[dev]->local_conversion;
else
return audio_devs[dev]->local_format;
}
int audio_open(int dev, struct file *file)
{
int ret;
int bits;
int dev_type = dev & 0x0f;
int mode = translate_mode(file);
const struct audio_driver *driver;
const struct coproc_operations *coprocessor;
dev = dev >> 4;
if (dev_type == SND_DEV_DSP16)
bits = 16;
else
bits = 8;
if (dev < 0 || dev >= num_audiodevs)
return -ENXIO;
driver = audio_devs[dev]->d;
if (!try_module_get(driver->owner))
return -ENODEV;
if ((ret = DMAbuf_open(dev, mode)) < 0)
goto error_1;
if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
if (!try_module_get(coprocessor->owner))
goto error_2;
if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
goto error_3;
}
}
audio_devs[dev]->local_conversion = 0;
if (dev_type == SND_DEV_AUDIO)
set_format(dev, AFMT_MU_LAW);
else
set_format(dev, bits);
audio_devs[dev]->audio_mode = AM_NONE;
return 0;
/*
* Clean-up stack: this is what needs (un)doing if
* we can't open the audio device ...
*/
error_3:
module_put(coprocessor->owner);
error_2:
DMAbuf_release(dev, mode);
error_1:
module_put(driver->owner);
return ret;
}
static void sync_output(int dev)
{
int p, i;
int l;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
if (dmap->fragment_size <= 0)
return;
dmap->flags |= DMA_POST;
/* Align the write pointer with fragment boundaries */
if ((l = dmap->user_counter % dmap->fragment_size) > 0)
{
int len;
unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
len = dmap->fragment_size - l;
memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
DMAbuf_move_wrpointer(dev, len);
}
/*
* Clean all unused buffer fragments.
*/
p = dmap->qtail;
dmap->flags |= DMA_POST;
for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
{
p = (p + 1) % dmap->nbufs;
if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
(dmap->raw_buf + dmap->buffsize))
printk(KERN_ERR "audio: Buffer error 2\n");
memset(dmap->raw_buf + p * dmap->fragment_size,
dmap->neutral_byte,
dmap->fragment_size);
}
dmap->flags |= DMA_DIRTY;
}
void audio_release(int dev, struct file *file)
{
const struct coproc_operations *coprocessor;
int mode = translate_mode(file);
dev = dev >> 4;
/*
* We do this in DMAbuf_release(). Why are we doing it
* here? Why don't we test the file mode before setting
* both flags? DMAbuf_release() does.
* ...pester...pester...pester...
*/
audio_devs[dev]->dmap_out->closing = 1;
audio_devs[dev]->dmap_in->closing = 1;
/*
* We need to make sure we allocated the dmap_out buffer
* before we go mucking around with it in sync_output().
*/
if (mode & OPEN_WRITE)
sync_output(dev);
if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
coprocessor->close(coprocessor->devc, COPR_PCM);
module_put(coprocessor->owner);
}
DMAbuf_release(dev, mode);
module_put(audio_devs[dev]->d->owner);
}
static void translate_bytes(const unsigned char *table, unsigned char *buff, int n)
{
unsigned long i;
if (n <= 0)
return;
for (i = 0; i < n; ++i)
buff[i] = table[buff[i]];
}
int audio_write(int dev, struct file *file, const char __user *buf, int count)
{
int c, p, l, buf_size, used, returned;
int err;
char *dma_buf;
dev = dev >> 4;
p = 0;
c = count;
if(count < 0)
return -EINVAL;
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EPERM;
if (audio_devs[dev]->flags & DMA_DUPLEX)
audio_devs[dev]->audio_mode |= AM_WRITE;
else
audio_devs[dev]->audio_mode = AM_WRITE;
if (!count) /* Flush output */
{
sync_output(dev);
return 0;
}
while (c)
{
if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
{
/* Handle nonblocking mode */
if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */
return err;
}
l = c;
if (l > buf_size)
l = buf_size;
returned = l;
used = l;
if (!audio_devs[dev]->d->copy_user)
{
if ((dma_buf + l) >
(audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
{
printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
return -EDOM;
}
if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
{
printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
return -EDOM;
}
if(copy_from_user(dma_buf, &(buf)[p], l))
return -EFAULT;
}
else audio_devs[dev]->d->copy_user (dev,
dma_buf, 0,
buf, p,
c, buf_size,
&used, &returned,
l);
l = returned;
if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
{
translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
}
c -= used;
p += used;
DMAbuf_move_wrpointer(dev, l);
}
return count;
}
int audio_read(int dev, struct file *file, char __user *buf, int count)
{
int c, p, l;
char *dmabuf;
int buf_no;
dev = dev >> 4;
p = 0;
c = count;
if (!(audio_devs[dev]->open_mode & OPEN_READ))
return -EPERM;
if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
sync_output(dev);
if (audio_devs[dev]->flags & DMA_DUPLEX)
audio_devs[dev]->audio_mode |= AM_READ;
else
audio_devs[dev]->audio_mode = AM_READ;
while(c)
{
if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
{
/*
* Nonblocking mode handling. Return current # of bytes
*/
if (p > 0) /* Avoid throwing away data */
return p; /* Return it instead */
if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN)
return -EAGAIN;
return buf_no;
}
if (l > c)
l = c;
/*
* Insert any local processing here.
*/
if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
{
translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
}
{
char *fixit = dmabuf;
if(copy_to_user(&(buf)[p], fixit, l))
return -EFAULT;
}
DMAbuf_rmchars(dev, buf_no, l);
p += l;
c -= l;
}
return count - c;
}
int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
{
int val, count;
unsigned long flags;
struct dma_buffparms *dmap;
int __user *p = arg;
dev = dev >> 4;
if (_IOC_TYPE(cmd) == 'C') {
if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
/* else
printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
return -ENXIO;
}
else switch (cmd)
{
case SNDCTL_DSP_SYNC:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return 0;
if (audio_devs[dev]->dmap_out->fragment_size == 0)
return 0;
sync_output(dev);
DMAbuf_sync(dev);
DMAbuf_reset(dev);
return 0;
case SNDCTL_DSP_POST:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return 0;
if (audio_devs[dev]->dmap_out->fragment_size == 0)
return 0;
audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
sync_output(dev);
dma_ioctl(dev, SNDCTL_DSP_POST, NULL);
return 0;
case SNDCTL_DSP_RESET:
audio_devs[dev]->audio_mode = AM_NONE;
DMAbuf_reset(dev);
return 0;
case SNDCTL_DSP_GETFMTS:
val = audio_devs[dev]->format_mask | AFMT_MU_LAW;
break;
case SNDCTL_DSP_SETFMT:
if (get_user(val, p))
return -EFAULT;
val = set_format(dev, val);
break;
case SNDCTL_DSP_GETISPACE:
if (!(audio_devs[dev]->open_mode & OPEN_READ))
return 0;
if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
return -EBUSY;
return dma_ioctl(dev, cmd, arg);
case SNDCTL_DSP_GETOSPACE:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EPERM;
if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
return -EBUSY;
return dma_ioctl(dev, cmd, arg);
case SNDCTL_DSP_NONBLOCK:
spin_lock(&file->f_lock);
file->f_flags |= O_NONBLOCK;
spin_unlock(&file->f_lock);
return 0;
case SNDCTL_DSP_GETCAPS:
val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */
if (audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode == OPEN_READWRITE)
val |= DSP_CAP_DUPLEX;
if (audio_devs[dev]->coproc)
val |= DSP_CAP_COPROC;
if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
val |= DSP_CAP_BATCH;
if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */
val |= DSP_CAP_TRIGGER;
break;
case SOUND_PCM_WRITE_RATE:
if (get_user(val, p))
return -EFAULT;
val = audio_devs[dev]->d->set_speed(dev, val);
break;
case SOUND_PCM_READ_RATE:
val = audio_devs[dev]->d->set_speed(dev, 0);
break;
case SNDCTL_DSP_STEREO:
if (get_user(val, p))
return -EFAULT;
if (val > 1 || val < 0)
return -EINVAL;
val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
break;
case SOUND_PCM_WRITE_CHANNELS:
if (get_user(val, p))
return -EFAULT;
val = audio_devs[dev]->d->set_channels(dev, val);
break;
case SOUND_PCM_READ_CHANNELS:
val = audio_devs[dev]->d->set_channels(dev, 0);
break;
case SOUND_PCM_READ_BITS:
val = audio_devs[dev]->d->set_bits(dev, 0);
break;
case SNDCTL_DSP_SETDUPLEX:
if (audio_devs[dev]->open_mode != OPEN_READWRITE)
return -EPERM;
return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
case SNDCTL_DSP_PROFILE:
if (get_user(val, p))
return -EFAULT;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
audio_devs[dev]->dmap_out->applic_profile = val;
if (audio_devs[dev]->open_mode & OPEN_READ)
audio_devs[dev]->dmap_in->applic_profile = val;
return 0;
case SNDCTL_DSP_GETODELAY:
dmap = audio_devs[dev]->dmap_out;
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
if (!(dmap->flags & DMA_ALLOC_DONE))
{
val=0;
break;
}
spin_lock_irqsave(&dmap->lock,flags);
/* Compute number of bytes that have been played */
count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
if (count < dmap->fragment_size && dmap->qhead != 0)
count += dmap->bytes_in_use; /* Pointer wrap not handled yet */
count += dmap->byte_counter;
/* Subtract current count from the number of bytes written by app */
count = dmap->user_counter - count;
if (count < 0)
count = 0;
spin_unlock_irqrestore(&dmap->lock,flags);
val = count;
break;
default:
return dma_ioctl(dev, cmd, arg);
}
return put_user(val, p);
}
void audio_init_devices(void)
{
/*
* NOTE! This routine could be called several times during boot.
*/
}
void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
{
/*
* This routine breaks the physical device buffers to logical ones.
*/
struct audio_operations *dsp_dev = audio_devs[dev];
unsigned i, n;
unsigned sr, nc, sz, bsz;
sr = dsp_dev->d->set_speed(dev, 0);
nc = dsp_dev->d->set_channels(dev, 0);
sz = dsp_dev->d->set_bits(dev, 0);
if (sz == 8)
dmap->neutral_byte = NEUTRAL8;
else
dmap->neutral_byte = NEUTRAL16;
if (sr < 1 || nc < 1 || sz < 1)
{
/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
sr = DSP_DEFAULT_SPEED;
nc = 1;
sz = 8;
}
sz = sr * nc * sz;
sz /= 8; /* #bits -> #bytes */
dmap->data_rate = sz;
if (!dmap->needs_reorg)
return;
dmap->needs_reorg = 0;
if (dmap->fragment_size == 0)
{
/* Compute the fragment size using the default algorithm */
/*
* Compute a buffer size for time not exceeding 1 second.
* Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
* of sound (using the current speed, sample size and #channels).
*/
bsz = dmap->buffsize;
while (bsz > sz)
bsz /= 2;
if (bsz == dmap->buffsize)
bsz /= 2; /* Needs at least 2 buffers */
/*
* Split the computed fragment to smaller parts. After 3.5a9
* the default subdivision is 4 which should give better
* results when recording.
*/
if (dmap->subdivision == 0) /* Not already set */
{
dmap->subdivision = 4; /* Init to the default value */
if ((bsz / dmap->subdivision) > 4096)
dmap->subdivision *= 2;
if ((bsz / dmap->subdivision) < 4096)
dmap->subdivision = 1;
}
bsz /= dmap->subdivision;
if (bsz < 16)
bsz = 16; /* Just a sanity check */
dmap->fragment_size = bsz;
}
else
{
/*
* The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
* the buffer size computation has already been done.
*/
if (dmap->fragment_size > (dmap->buffsize / 2))
dmap->fragment_size = (dmap->buffsize / 2);
bsz = dmap->fragment_size;
}
if (audio_devs[dev]->min_fragment)
if (bsz < (1 << audio_devs[dev]->min_fragment))
bsz = 1 << audio_devs[dev]->min_fragment;
if (audio_devs[dev]->max_fragment)
if (bsz > (1 << audio_devs[dev]->max_fragment))
bsz = 1 << audio_devs[dev]->max_fragment;
bsz &= ~0x07; /* Force size which is multiple of 8 bytes */
#ifdef OS_DMA_ALIGN_CHECK
OS_DMA_ALIGN_CHECK(bsz);
#endif
n = dmap->buffsize / bsz;
if (n > MAX_SUB_BUFFERS)
n = MAX_SUB_BUFFERS;
if (n > dmap->max_fragments)
n = dmap->max_fragments;
if (n < 2)
{
n = 2;
bsz /= 2;
}
dmap->nbufs = n;
dmap->bytes_in_use = n * bsz;
dmap->fragment_size = bsz;
dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
dmap->bytes_in_use; /* Approximately one hour */
if (dmap->raw_buf)
{
memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use);
}
for (i = 0; i < dmap->nbufs; i++)
{
dmap->counts[i] = 0;
}
dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
}
static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
{
if (fact == 0)
{
fact = dmap->subdivision;
if (fact == 0)
fact = 1;
return fact;
}
if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */
return -EINVAL;
if (fact > MAX_REALTIME_FACTOR)
return -EINVAL;
if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
return -EINVAL;
dmap->subdivision = fact;
return fact;
}
static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
{
int bytes, count;
if (fact == 0)
return -EIO;
if (dmap->subdivision != 0 ||
dmap->fragment_size) /* Too late to change */
return -EINVAL;
bytes = fact & 0xffff;
count = (fact >> 16) & 0x7fff;
if (count == 0)
count = MAX_SUB_BUFFERS;
else if (count < MAX_SUB_BUFFERS)
count++;
if (bytes < 4 || bytes > 17) /* <16 || > 512k */
return -EINVAL;
if (count < 2)
return -EINVAL;
if (audio_devs[dev]->min_fragment > 0)
if (bytes < audio_devs[dev]->min_fragment)
bytes = audio_devs[dev]->min_fragment;
if (audio_devs[dev]->max_fragment > 0)
if (bytes > audio_devs[dev]->max_fragment)
bytes = audio_devs[dev]->max_fragment;
#ifdef OS_DMA_MINBITS
if (bytes < OS_DMA_MINBITS)
bytes = OS_DMA_MINBITS;
#endif
dmap->fragment_size = (1 << bytes);
dmap->max_fragments = count;
if (dmap->fragment_size > dmap->buffsize)
dmap->fragment_size = dmap->buffsize;
if (dmap->fragment_size == dmap->buffsize &&
audio_devs[dev]->flags & DMA_AUTOMODE)
dmap->fragment_size /= 2; /* Needs at least 2 buffers */
dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
return bytes | ((count - 1) << 16);
}
static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
{
struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
struct dma_buffparms *dmap;
audio_buf_info info;
count_info cinfo;
int fact, ret, changed, bits, count, err;
unsigned long flags;
switch (cmd)
{
case SNDCTL_DSP_SUBDIVIDE:
ret = 0;
if (get_user(fact, (int __user *)arg))
return -EFAULT;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
ret = dma_subdivide(dev, dmap_out, fact);
if (ret < 0)
return ret;
if (audio_devs[dev]->open_mode != OPEN_WRITE ||
(audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ))
ret = dma_subdivide(dev, dmap_in, fact);
if (ret < 0)
return ret;
break;
case SNDCTL_DSP_GETISPACE:
case SNDCTL_DSP_GETOSPACE:
dmap = dmap_out;
if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
return -EINVAL;
if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
dmap = dmap_in;
if (dmap->mapping_flags & DMA_MAP_MAPPED)
return -EINVAL;
if (!(dmap->flags & DMA_ALLOC_DONE))
reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
info.fragstotal = dmap->nbufs;
if (cmd == SNDCTL_DSP_GETISPACE)
info.fragments = dmap->qlen;
else
{
if (!DMAbuf_space_in_queue(dev))
info.fragments = 0;
else
{
info.fragments = DMAbuf_space_in_queue(dev);
if (audio_devs[dev]->d->local_qlen)
{
int tmp = audio_devs[dev]->d->local_qlen(dev);
if (tmp && info.fragments)
tmp--; /*
* This buffer has been counted twice
*/
info.fragments -= tmp;
}
}
}
if (info.fragments < 0)
info.fragments = 0;
else if (info.fragments > dmap->nbufs)
info.fragments = dmap->nbufs;
info.fragsize = dmap->fragment_size;
info.bytes = info.fragments * dmap->fragment_size;
if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
info.bytes -= dmap->counts[dmap->qhead];
else
{
info.fragments = info.bytes / dmap->fragment_size;
info.bytes -= dmap->user_counter % dmap->fragment_size;
}
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
case SNDCTL_DSP_SETTRIGGER:
if (get_user(bits, (int __user *)arg))
return -EFAULT;
bits &= audio_devs[dev]->open_mode;
if (audio_devs[dev]->d->trigger == NULL)
return -EINVAL;
if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
(bits & PCM_ENABLE_OUTPUT))
return -EINVAL;
if (bits & PCM_ENABLE_INPUT)
{
spin_lock_irqsave(&dmap_in->lock,flags);
changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT;
if (changed && audio_devs[dev]->go)
{
reorganize_buffers(dev, dmap_in, 1);
if ((err = audio_devs[dev]->d->prepare_for_input(dev,
dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
spin_unlock_irqrestore(&dmap_in->lock,flags);
return err;
}
dmap_in->dma_mode = DMODE_INPUT;
audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
DMAbuf_activate_recording(dev, dmap_in);
} else
audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT;
spin_unlock_irqrestore(&dmap_in->lock,flags);
}
if (bits & PCM_ENABLE_OUTPUT)
{
spin_lock_irqsave(&dmap_out->lock,flags);
changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT;
if (changed &&
(dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
audio_devs[dev]->go)
{
if (!(dmap_out->flags & DMA_ALLOC_DONE))
reorganize_buffers(dev, dmap_out, 0);
dmap_out->dma_mode = DMODE_OUTPUT;
audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT;
dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
DMAbuf_launch_output(dev, dmap_out);
} else
audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT;
spin_unlock_irqrestore(&dmap_out->lock,flags);
}
#if 0
if (changed && audio_devs[dev]->d->trigger)
audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
#endif
/* Falls through... */
case SNDCTL_DSP_GETTRIGGER:
ret = audio_devs[dev]->enable_bits;
break;
case SNDCTL_DSP_SETSYNCRO:
if (!audio_devs[dev]->d->trigger)
return -EINVAL;
audio_devs[dev]->d->trigger(dev, 0);
audio_devs[dev]->go = 0;
return 0;
case SNDCTL_DSP_GETIPTR:
if (!(audio_devs[dev]->open_mode & OPEN_READ))
return -EINVAL;
spin_lock_irqsave(&dmap_in->lock,flags);
cinfo.bytes = dmap_in->byte_counter;
cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */
cinfo.blocks = dmap_in->qlen;
cinfo.bytes += cinfo.ptr;
if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
dmap_in->qlen = 0; /* Reset interrupt counter */
spin_unlock_irqrestore(&dmap_in->lock,flags);
if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETOPTR:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
spin_lock_irqsave(&dmap_out->lock,flags);
cinfo.bytes = dmap_out->byte_counter;
cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
cinfo.blocks = dmap_out->qlen;
cinfo.bytes += cinfo.ptr;
if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
dmap_out->qlen = 0; /* Reset interrupt counter */
spin_unlock_irqrestore(&dmap_out->lock,flags);
if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETODELAY:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
if (!(dmap_out->flags & DMA_ALLOC_DONE))
{
ret=0;
break;
}
spin_lock_irqsave(&dmap_out->lock,flags);
/* Compute number of bytes that have been played */
count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
count += dmap_out->byte_counter;
/* Subtract current count from the number of bytes written by app */
count = dmap_out->user_counter - count;
if (count < 0)
count = 0;
spin_unlock_irqrestore(&dmap_out->lock,flags);
ret = count;
break;
case SNDCTL_DSP_POST:
if (audio_devs[dev]->dmap_out->qlen > 0)
if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
return 0;
case SNDCTL_DSP_GETBLKSIZE:
dmap = dmap_out;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
if (audio_devs[dev]->open_mode == OPEN_READ ||
(audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ))
reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
if (audio_devs[dev]->open_mode == OPEN_READ)
dmap = dmap_in;
ret = dmap->fragment_size;
break;
case SNDCTL_DSP_SETFRAGMENT:
ret = 0;
if (get_user(fact, (int __user *)arg))
return -EFAULT;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
ret = dma_set_fragment(dev, dmap_out, fact);
if (ret < 0)
return ret;
if (audio_devs[dev]->open_mode == OPEN_READ ||
(audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ))
ret = dma_set_fragment(dev, dmap_in, fact);
if (ret < 0)
return ret;
if (!arg) /* don't know what this is good for, but preserve old semantics */
return 0;
break;
default:
if (!audio_devs[dev]->d->ioctl)
return -EINVAL;
return audio_devs[dev]->d->ioctl(dev, cmd, arg);
}
return put_user(ret, (int __user *)arg);
}

39
sound/oss/bin2hex.c Normal file
View file

@ -0,0 +1,39 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main( int argc, const char * argv [] )
{
const char * varname;
int i = 0;
int c;
int id = 0;
if(argv[1] && strcmp(argv[1],"-i")==0)
{
argv++;
argc--;
id=1;
}
if(argc==1)
{
fprintf(stderr, "bin2hex: [-i] firmware\n");
exit(1);
}
varname = argv[1];
printf( "/* automatically generated by bin2hex */\n" );
printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
while ( ( c = getchar( ) ) != EOF )
{
if ( i != 0 && i % 10 == 0 )
printf( "\n" );
printf( "0x%02lx,", c & 0xFFl );
i++;
}
printf( "};\nstatic int %sLen = %d;\n", varname, i );
return 0;
}

12
sound/oss/coproc.h Normal file
View file

@ -0,0 +1,12 @@
/*
* Definitions for various on board processors on the sound cards. For
* example DSP processors.
*/
/*
* Coprocessor access types
*/
#define COPR_CUSTOM 0x0001 /* Custom applications */
#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */
#define COPR_PCM 0x0004 /* Digitized voice applications */
#define COPR_SYNTH 0x0008 /* Music synthesis */

256
sound/oss/dev_table.c Normal file
View file

@ -0,0 +1,256 @@
/*
* sound/oss/dev_table.c
*
* Device call tables.
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
#include <linux/init.h>
#include "sound_config.h"
struct audio_operations *audio_devs[MAX_AUDIO_DEV];
EXPORT_SYMBOL(audio_devs);
int num_audiodevs;
EXPORT_SYMBOL(num_audiodevs);
struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
EXPORT_SYMBOL(mixer_devs);
int num_mixers;
EXPORT_SYMBOL(num_mixers);
struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
EXPORT_SYMBOL(synth_devs);
int num_synths;
struct midi_operations *midi_devs[MAX_MIDI_DEV];
EXPORT_SYMBOL(midi_devs);
int num_midis;
EXPORT_SYMBOL(num_midis);
struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
&default_sound_timer, NULL
};
EXPORT_SYMBOL(sound_timer_devs);
int num_sound_timers = 1;
static int sound_alloc_audiodev(void);
int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
int driver_size, int flags, unsigned int format_mask,
void *devc, int dma1, int dma2)
{
struct audio_driver *d;
struct audio_operations *op;
int num;
if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
return -(EINVAL);
}
num = sound_alloc_audiodev();
if (num == -1) {
printk(KERN_ERR "sound: Too many audio drivers\n");
return -(EBUSY);
}
d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
sound_nblocks++;
if (sound_nblocks >= MAX_MEM_BLOCKS)
sound_nblocks = MAX_MEM_BLOCKS - 1;
op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct audio_operations)));
sound_nblocks++;
if (sound_nblocks >= MAX_MEM_BLOCKS)
sound_nblocks = MAX_MEM_BLOCKS - 1;
if (d == NULL || op == NULL) {
printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
sound_unload_audiodev(num);
return -(ENOMEM);
}
init_waitqueue_head(&op->in_sleeper);
init_waitqueue_head(&op->out_sleeper);
init_waitqueue_head(&op->poll_sleeper);
if (driver_size < sizeof(struct audio_driver))
memset((char *) d, 0, sizeof(struct audio_driver));
memcpy((char *) d, (char *) driver, driver_size);
op->d = d;
strlcpy(op->name, name, sizeof(op->name));
op->flags = flags;
op->format_mask = format_mask;
op->devc = devc;
/*
* Hardcoded defaults
*/
audio_devs[num] = op;
DMAbuf_init(num, dma1, dma2);
audio_init_devices();
return num;
}
EXPORT_SYMBOL(sound_install_audiodrv);
int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
int driver_size, void *devc)
{
struct mixer_operations *op;
int n = sound_alloc_mixerdev();
if (n == -1) {
printk(KERN_ERR "Sound: Too many mixer drivers\n");
return -EBUSY;
}
if (vers != MIXER_DRIVER_VERSION ||
driver_size > sizeof(struct mixer_operations)) {
printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
return -EINVAL;
}
/* FIXME: This leaks a mixer_operations struct every time its called
until you unload sound! */
op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct mixer_operations)));
sound_nblocks++;
if (sound_nblocks >= MAX_MEM_BLOCKS)
sound_nblocks = MAX_MEM_BLOCKS - 1;
if (op == NULL) {
printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
return -ENOMEM;
}
memcpy((char *) op, (char *) driver, driver_size);
strlcpy(op->name, name, sizeof(op->name));
op->devc = devc;
mixer_devs[n] = op;
return n;
}
EXPORT_SYMBOL(sound_install_mixer);
void sound_unload_audiodev(int dev)
{
if (dev != -1) {
DMAbuf_deinit(dev);
audio_devs[dev] = NULL;
unregister_sound_dsp((dev<<4)+3);
}
}
EXPORT_SYMBOL(sound_unload_audiodev);
static int sound_alloc_audiodev(void)
{
int i = register_sound_dsp(&oss_sound_fops, -1);
if(i==-1)
return i;
i>>=4;
if(i>=num_audiodevs)
num_audiodevs = i + 1;
return i;
}
int sound_alloc_mididev(void)
{
int i = register_sound_midi(&oss_sound_fops, -1);
if(i==-1)
return i;
i>>=4;
if(i>=num_midis)
num_midis = i + 1;
return i;
}
EXPORT_SYMBOL(sound_alloc_mididev);
int sound_alloc_synthdev(void)
{
int i;
for (i = 0; i < MAX_SYNTH_DEV; i++) {
if (synth_devs[i] == NULL) {
if (i >= num_synths)
num_synths++;
return i;
}
}
return -1;
}
EXPORT_SYMBOL(sound_alloc_synthdev);
int sound_alloc_mixerdev(void)
{
int i = register_sound_mixer(&oss_sound_fops, -1);
if(i==-1)
return -1;
i>>=4;
if(i>=num_mixers)
num_mixers = i + 1;
return i;
}
EXPORT_SYMBOL(sound_alloc_mixerdev);
int sound_alloc_timerdev(void)
{
int i;
for (i = 0; i < MAX_TIMER_DEV; i++) {
if (sound_timer_devs[i] == NULL) {
if (i >= num_sound_timers)
num_sound_timers++;
return i;
}
}
return -1;
}
EXPORT_SYMBOL(sound_alloc_timerdev);
void sound_unload_mixerdev(int dev)
{
if (dev != -1) {
mixer_devs[dev] = NULL;
unregister_sound_mixer(dev<<4);
num_mixers--;
}
}
EXPORT_SYMBOL(sound_unload_mixerdev);
void sound_unload_mididev(int dev)
{
if (dev != -1) {
midi_devs[dev] = NULL;
unregister_sound_midi((dev<<4)+2);
}
}
EXPORT_SYMBOL(sound_unload_mididev);
void sound_unload_synthdev(int dev)
{
if (dev != -1)
synth_devs[dev] = NULL;
}
EXPORT_SYMBOL(sound_unload_synthdev);
void sound_unload_timerdev(int dev)
{
if (dev != -1)
sound_timer_devs[dev] = NULL;
}
EXPORT_SYMBOL(sound_unload_timerdev);

390
sound/oss/dev_table.h Normal file
View file

@ -0,0 +1,390 @@
/*
* dev_table.h
*
* Global definitions for device call tables
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
#ifndef _DEV_TABLE_H_
#define _DEV_TABLE_H_
#include <linux/spinlock.h>
/*
* Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
* Numbers 1000 to N are reserved for driver's internal use.
*/
#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */
#define SNDCARD_VIDC 28 /* ARMs VIDC */
#define SNDCARD_SBPNP 29
#define SNDCARD_SOFTOSS 36
#define SNDCARD_VMIDI 37
#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */
#define SNDCARD_OPL3SA1_SB 39
#define SNDCARD_OPL3SA1_MPU 40
#define SNDCARD_WAVEFRONT 41
#define SNDCARD_OPL3SA2 42
#define SNDCARD_OPL3SA2_MPU 43
#define SNDCARD_WAVEARTIST 44 /* Waveartist */
#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */
#define SNDCARD_AD1816 88
/*
* NOTE! NOTE! NOTE! NOTE!
*
* If you modify this file, please check the dev_table.c also.
*
* NOTE! NOTE! NOTE! NOTE!
*/
struct driver_info
{
char *driver_id;
int card_subtype; /* Driver specific. Usually 0 */
int card_type; /* From soundcard.h */
char *name;
void (*attach) (struct address_info *hw_config);
int (*probe) (struct address_info *hw_config);
void (*unload) (struct address_info *hw_config);
};
struct card_info
{
int card_type; /* Link (search key) to the driver list */
struct address_info config;
int enabled;
void *for_driver_use;
};
/*
* Device specific parameters (used only by dmabuf.c)
*/
#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
#define DMODE_NONE 0
#define DMODE_OUTPUT PCM_ENABLE_OUTPUT
#define DMODE_INPUT PCM_ENABLE_INPUT
struct dma_buffparms
{
int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
int closing;
/*
* Pointers to raw buffers
*/
char *raw_buf;
unsigned long raw_buf_phys;
int buffsize;
/*
* Device state tables
*/
unsigned long flags;
#define DMA_BUSY 0x00000001
#define DMA_RESTART 0x00000002
#define DMA_ACTIVE 0x00000004
#define DMA_STARTED 0x00000008
#define DMA_EMPTY 0x00000010
#define DMA_ALLOC_DONE 0x00000020
#define DMA_SYNCING 0x00000040
#define DMA_DIRTY 0x00000080
#define DMA_POST 0x00000100
#define DMA_NODMA 0x00000200
#define DMA_NOTIMEOUT 0x00000400
int open_mode;
/*
* Queue parameters.
*/
int qlen;
int qhead;
int qtail;
spinlock_t lock;
int cfrag; /* Current incomplete fragment (write) */
int nbufs;
int counts[MAX_SUB_BUFFERS];
int subdivision;
int fragment_size;
int needs_reorg;
int max_fragments;
int bytes_in_use;
int underrun_count;
unsigned long byte_counter;
unsigned long user_counter;
unsigned long max_byte_counter;
int data_rate; /* Bytes/second */
int mapping_flags;
#define DMA_MAP_MAPPED 0x00000001
char neutral_byte;
int dma; /* DMA channel */
int applic_profile; /* Application profile (APF_*) */
/* Interrupt callback stuff */
void (*audio_callback) (int dev, int parm);
int callback_parm;
int buf_flags[MAX_SUB_BUFFERS];
#define BUFF_EOF 0x00000001 /* Increment eof count */
#define BUFF_DIRTY 0x00000002 /* Buffer written */
};
/*
* Structure for use with various microcontrollers and DSP processors
* in the recent sound cards.
*/
typedef struct coproc_operations
{
char name[64];
struct module *owner;
int (*open) (void *devc, int sub_device);
void (*close) (void *devc, int sub_device);
int (*ioctl) (void *devc, unsigned int cmd, void __user * arg, int local);
void (*reset) (void *devc);
void *devc; /* Driver specific info */
} coproc_operations;
struct audio_driver
{
struct module *owner;
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, void __user * arg);
int (*prepare_for_input) (int dev, int bufsize, int nbufs);
int (*prepare_for_output) (int dev, int bufsize, int nbufs);
void (*halt_io) (int dev);
int (*local_qlen)(int dev);
void (*copy_user) (int dev,
char *localbuf, int localoffs,
const char __user *userbuf, int useroffs,
int max_in, int max_out,
int *used, int *returned,
int len);
void (*halt_input) (int dev);
void (*halt_output) (int dev);
void (*trigger) (int dev, int bits);
int (*set_speed)(int dev, int speed);
unsigned int (*set_bits)(int dev, unsigned int bits);
short (*set_channels)(int dev, short channels);
void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */
void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */
void (*mmap)(int dev);
};
struct audio_operations
{
char name[128];
int flags;
#define NOTHING_SPECIAL 0x00
#define NEEDS_RESTART 0x01
#define DMA_AUTOMODE 0x02
#define DMA_DUPLEX 0x04
#define DMA_PSEUDO_AUTOMODE 0x08
#define DMA_HARDSTOP 0x10
#define DMA_EXACT 0x40
#define DMA_NORESET 0x80
int format_mask; /* Bitmask for supported audio formats */
void *devc; /* Driver specific info */
struct audio_driver *d;
void *portc; /* Driver specific info */
struct dma_buffparms *dmap_in, *dmap_out;
struct coproc_operations *coproc;
int mixer_dev;
int enable_bits;
int open_mode;
int go;
int min_fragment; /* 0 == unlimited */
int max_fragment; /* 0 == unlimited */
int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
/* fields formerly in dmabuf.c */
wait_queue_head_t in_sleeper;
wait_queue_head_t out_sleeper;
wait_queue_head_t poll_sleeper;
/* fields formerly in audio.c */
int audio_mode;
#define AM_NONE 0
#define AM_WRITE OPEN_WRITE
#define AM_READ OPEN_READ
int local_format;
int audio_format;
int local_conversion;
#define CNV_MU_LAW 0x00000001
/* large structures at the end to keep offsets small */
struct dma_buffparms dmaps[2];
};
int *load_mixer_volumes(char *name, int *levels, int present);
struct mixer_operations
{
struct module *owner;
char id[16];
char name[64];
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
void *devc;
int modify_counter;
};
struct synth_operations
{
struct module *owner;
char *id; /* Unique identifier (ASCII) max 29 char */
struct synth_info *info;
int midi_dev;
int synth_type;
int synth_subtype;
int (*open) (int dev, int mode);
void (*close) (int dev);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
int (*kill_note) (int dev, int voice, int note, int velocity);
int (*start_note) (int dev, int voice, int note, int velocity);
int (*set_instr) (int dev, int voice, int instr);
void (*reset) (int dev);
void (*hw_control) (int dev, unsigned char *event);
int (*load_patch) (int dev, int format, const char __user *addr,
int count, int pmgr_flag);
void (*aftertouch) (int dev, int voice, int pressure);
void (*controller) (int dev, int voice, int ctrl_num, int value);
void (*panning) (int dev, int voice, int value);
void (*volume_method) (int dev, int mode);
void (*bender) (int dev, int chn, int value);
int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
void (*setup_voice) (int dev, int voice, int chn);
int (*send_sysex)(int dev, unsigned char *bytes, int len);
struct voice_alloc_info alloc;
struct channel_info chn_info[16];
int emulation;
#define EMU_GM 1 /* General MIDI */
#define EMU_XG 2 /* Yamaha XG */
#define MAX_SYSEX_BUF 64
unsigned char sysex_buf[MAX_SYSEX_BUF];
int sysex_ptr;
};
struct midi_input_info
{
/* MIDI input scanner variables */
#define MI_MAX 10
volatile int m_busy;
unsigned char m_buf[MI_MAX];
unsigned char m_prev_status; /* For running status */
int m_ptr;
#define MST_INIT 0
#define MST_DATA 1
#define MST_SYSEX 2
int m_state;
int m_left;
};
struct midi_operations
{
struct module *owner;
struct midi_info info;
struct synth_operations *converter;
struct midi_input_info in_info;
int (*open) (int dev, int mode,
void (*inputintr)(int dev, unsigned char data),
void (*outputintr)(int dev)
);
void (*close) (int dev);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
int (*outputc) (int dev, unsigned char data);
int (*start_read) (int dev);
int (*end_read) (int dev);
void (*kick)(int dev);
int (*command) (int dev, unsigned char *data);
int (*buffer_status) (int dev);
int (*prefix_cmd) (int dev, unsigned char status);
struct coproc_operations *coproc;
void *devc;
};
struct sound_lowlev_timer
{
int dev;
int priority;
unsigned int (*tmr_start)(int dev, unsigned int usecs);
void (*tmr_disable)(int dev);
void (*tmr_restart)(int dev);
};
struct sound_timer_operations
{
struct module *owner;
struct sound_timer_info info;
int priority;
int devlink;
int (*open)(int dev, int mode);
void (*close)(int dev);
int (*event)(int dev, unsigned char *ev);
unsigned long (*get_time)(int dev);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
void (*arm_timer)(int dev, long time);
};
extern struct sound_timer_operations default_sound_timer;
extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
extern int num_audiodevs;
extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
extern int num_mixers;
extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
extern int num_synths;
extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
extern int num_midis;
extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
extern int num_sound_timers;
extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
void sound_timer_init (struct sound_lowlev_timer *t, char *name);
void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
#define AUDIO_DRIVER_VERSION 2
#define MIXER_DRIVER_VERSION 2
int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
int driver_size, int flags, unsigned int format_mask,
void *devc, int dma1, int dma2);
int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
int driver_size, void *devc);
void sound_unload_audiodev(int dev);
void sound_unload_mixerdev(int dev);
void sound_unload_mididev(int dev);
void sound_unload_synthdev(int dev);
void sound_unload_timerdev(int dev);
int sound_alloc_mixerdev(void);
int sound_alloc_timerdev(void);
int sound_alloc_synthdev(void);
int sound_alloc_mididev(void);
#endif /* _DEV_TABLE_H_ */

1266
sound/oss/dmabuf.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
config DMASOUND_ATARI
tristate "Atari DMA sound support"
depends on ATARI && SOUND
select DMASOUND
help
If you want to use the internal audio of your Atari in Linux, answer
Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND_PAULA
tristate "Amiga DMA sound support"
depends on AMIGA && SOUND
select DMASOUND
help
If you want to use the internal audio of your Amiga in Linux, answer
Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND_Q40
tristate "Q40 sound support"
depends on Q40 && SOUND
select DMASOUND
help
If you want to use the internal audio of your Q40 in Linux, answer
Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND
tristate
select SOUND_OSS_CORE

View file

@ -0,0 +1,7 @@
#
# Makefile for the DMA sound driver
#
obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o
obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o
obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o

View file

@ -0,0 +1,261 @@
#ifndef _dmasound_h_
/*
* linux/sound/oss/dmasound/dmasound.h
*
*
* Minor numbers for the sound driver.
*
* Unfortunately Creative called the codec chip of SB as a DSP. For this
* reason the /dev/dsp is reserved for digitized audio use. There is a
* device for true DSP processors but it will be called something else.
* In v3.0 it's /dev/sndproc but this could be a temporary solution.
*/
#define _dmasound_h_
#include <linux/types.h>
#define SND_NDEVS 256 /* Number of supported devices */
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
synthesizer and MIDI output) */
#define SND_DEV_MIDIN 2 /* Raw midi access */
#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
#define SND_DEV_STATUS 6 /* /dev/sndstat */
/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
#define SND_DEV_PSS SND_DEV_SNDPROC
/* switch on various prinks */
#define DEBUG_DMASOUND 1
#define MAX_AUDIO_DEV 5
#define MAX_MIXER_DEV 4
#define MAX_SYNTH_DEV 3
#define MAX_MIDI_DEV 6
#define MAX_TIMER_DEV 3
#define MAX_CATCH_RADIUS 10
#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
#define IOCTL_IN(arg, ret) \
do { int error = get_user(ret, (int __user *)(arg)); \
if (error) return error; \
} while (0)
#define IOCTL_OUT(arg, ret) ioctl_return((int __user *)(arg), ret)
static inline int ioctl_return(int __user *addr, int value)
{
return value < 0 ? value : put_user(value, addr);
}
/*
* Configuration
*/
#undef HAS_8BIT_TABLES
#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
#define HAS_8BIT_TABLES
#define MIN_BUFFERS 4
#define MIN_BUFSIZE (1<<12) /* in bytes (- where does this come from ?) */
#define MIN_FRAG_SIZE 8 /* not 100% sure about this */
#define MAX_BUFSIZE (1<<17) /* Limit for Amiga is 128 kb */
#define MAX_FRAG_SIZE 15 /* allow *4 for mono-8 => stereo-16 (for multi) */
#else /* is pmac and multi is off */
#define MIN_BUFFERS 2
#define MIN_BUFSIZE (1<<8) /* in bytes */
#define MIN_FRAG_SIZE 8
#define MAX_BUFSIZE (1<<18) /* this is somewhat arbitrary for pmac */
#define MAX_FRAG_SIZE 16 /* need to allow *4 for mono-8 => stereo-16 */
#endif
#define DEFAULT_N_BUFFERS 4
#define DEFAULT_BUFF_SIZE (1<<15)
/*
* Initialization
*/
extern int dmasound_init(void);
#ifdef MODULE
extern void dmasound_deinit(void);
#else
#define dmasound_deinit() do { } while (0)
#endif
/* description of the set-up applies to either hard or soft settings */
typedef struct {
int format; /* AFMT_* */
int stereo; /* 0 = mono, 1 = stereo */
int size; /* 8/16 bit*/
int speed; /* speed */
} SETTINGS;
/*
* Machine definitions
*/
typedef struct {
const char *name;
const char *name2;
struct module *owner;
void *(*dma_alloc)(unsigned int, gfp_t);
void (*dma_free)(void *, unsigned int);
int (*irqinit)(void);
#ifdef MODULE
void (*irqcleanup)(void);
#endif
void (*init)(void);
void (*silence)(void);
int (*setFormat)(int);
int (*setVolume)(int);
int (*setBass)(int);
int (*setTreble)(int);
int (*setGain)(int);
void (*play)(void);
void (*record)(void); /* optional */
void (*mixer_init)(void); /* optional */
int (*mixer_ioctl)(u_int, u_long); /* optional */
int (*write_sq_setup)(void); /* optional */
int (*read_sq_setup)(void); /* optional */
int (*sq_open)(fmode_t); /* optional */
int (*state_info)(char *, size_t); /* optional */
void (*abort_read)(void); /* optional */
int min_dsp_speed;
int max_dsp_speed;
int version ;
int hardware_afmts ; /* OSS says we only return h'ware info */
/* when queried via SNDCTL_DSP_GETFMTS */
int capabilities ; /* low-level reply to SNDCTL_DSP_GETCAPS */
SETTINGS default_hard ; /* open() or init() should set something valid */
SETTINGS default_soft ; /* you can make it look like old OSS, if you want to */
} MACHINE;
/*
* Low level stuff
*/
typedef struct {
ssize_t (*ct_ulaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_alaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_s8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_u8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_s16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_u16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_s16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_u16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
} TRANS;
struct sound_settings {
MACHINE mach; /* machine dependent things */
SETTINGS hard; /* hardware settings */
SETTINGS soft; /* software settings */
SETTINGS dsp; /* /dev/dsp default settings */
TRANS *trans_write; /* supported translations */
int volume_left; /* volume (range is machine dependent) */
int volume_right;
int bass; /* tone (range is machine dependent) */
int treble;
int gain;
int minDev; /* minor device number currently open */
spinlock_t lock;
};
extern struct sound_settings dmasound;
#ifdef HAS_8BIT_TABLES
extern char dmasound_ulaw2dma8[];
extern char dmasound_alaw2dma8[];
#endif
/*
* Mid level stuff
*/
static inline int dmasound_set_volume(int volume)
{
return dmasound.mach.setVolume(volume);
}
static inline int dmasound_set_bass(int bass)
{
return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50;
}
static inline int dmasound_set_treble(int treble)
{
return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50;
}
static inline int dmasound_set_gain(int gain)
{
return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100;
}
/*
* Sound queue stuff, the heart of the driver
*/
struct sound_queue {
/* buffers allocated for this queue */
int numBufs; /* real limits on what the user can have */
int bufSize; /* in bytes */
char **buffers;
/* current parameters */
int locked ; /* params cannot be modified when != 0 */
int user_frags ; /* user requests this many */
int user_frag_size ; /* of this size */
int max_count; /* actual # fragments <= numBufs */
int block_size; /* internal block size in bytes */
int max_active; /* in-use fragments <= max_count */
/* it shouldn't be necessary to declare any of these volatile */
int front, rear, count;
int rear_size;
/*
* The use of the playing field depends on the hardware
*
* Atari, PMac: The number of frames that are loaded/playing
*
* Amiga: Bit 0 is set: a frame is loaded
* Bit 1 is set: a frame is playing
*/
int active;
wait_queue_head_t action_queue, open_queue, sync_queue;
int non_blocking;
int busy, syncing, xruns, died;
};
#define WAKE_UP(queue) (wake_up_interruptible(&queue))
extern struct sound_queue dmasound_write_sq;
#define write_sq dmasound_write_sq
extern int dmasound_catchRadius;
#define catchRadius dmasound_catchRadius
/* define the value to be put in the byte-swap reg in mac-io
when we want it to swap for us.
*/
#define BS_VAL 1
#define SW_INPUT_VOLUME_SCALE 4
#define SW_INPUT_VOLUME_DEFAULT (128 / SW_INPUT_VOLUME_SCALE)
extern int expand_read_bal; /* Balance factor for reading */
extern uint software_input_volume; /* software implemented recording volume! */
#endif /* _dmasound_h_ */

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,739 @@
/*
* linux/sound/oss/dmasound/dmasound_paula.c
*
* Amiga `Paula' DMA Sound Driver
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
* prior to 28/01/2001
*
* 28/01/2001 [0.1] Iain Sandoe
* - added versioning
* - put in and populated the hardware_afmts field.
* [0.2] - put in SNDCTL_DSP_GETCAPS value.
* [0.3] - put in constraint on state buffer usage.
* [0.4] - put in default hard/soft settings
*/
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/soundcard.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include <asm/machdep.h>
#include "dmasound.h"
#define DMASOUND_PAULA_REVISION 0
#define DMASOUND_PAULA_EDITION 4
#define custom amiga_custom
/*
* The minimum period for audio depends on htotal (for OCS/ECS/AGA)
* (Imported from arch/m68k/amiga/amisound.c)
*/
extern volatile u_short amiga_audio_min_period;
/*
* amiga_mksound() should be able to restore the period after beeping
* (Imported from arch/m68k/amiga/amisound.c)
*/
extern u_short amiga_audio_period;
/*
* Audio DMA masks
*/
#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
/*
* Helper pointers for 16(14)-bit sound
*/
static int write_sq_block_size_half, write_sq_block_size_quarter;
/*** Low level stuff *********************************************************/
static void *AmiAlloc(unsigned int size, gfp_t flags);
static void AmiFree(void *obj, unsigned int size);
static int AmiIrqInit(void);
#ifdef MODULE
static void AmiIrqCleanUp(void);
#endif
static void AmiSilence(void);
static void AmiInit(void);
static int AmiSetFormat(int format);
static int AmiSetVolume(int volume);
static int AmiSetTreble(int treble);
static void AmiPlayNextFrame(int index);
static void AmiPlay(void);
static irqreturn_t AmiInterrupt(int irq, void *dummy);
#ifdef CONFIG_HEARTBEAT
/*
* Heartbeat interferes with sound since the 7 kHz low-pass filter and the
* power LED are controlled by the same line.
*/
static void (*saved_heartbeat)(int) = NULL;
static inline void disable_heartbeat(void)
{
if (mach_heartbeat) {
saved_heartbeat = mach_heartbeat;
mach_heartbeat = NULL;
}
AmiSetTreble(dmasound.treble);
}
static inline void enable_heartbeat(void)
{
if (saved_heartbeat)
mach_heartbeat = saved_heartbeat;
}
#else /* !CONFIG_HEARTBEAT */
#define disable_heartbeat() do { } while (0)
#define enable_heartbeat() do { } while (0)
#endif /* !CONFIG_HEARTBEAT */
/*** Mid level stuff *********************************************************/
static void AmiMixerInit(void);
static int AmiMixerIoctl(u_int cmd, u_long arg);
static int AmiWriteSqSetup(void);
static int AmiStateInfo(char *buffer, size_t space);
/*** Translations ************************************************************/
/* ++TeSche: radically changed for new expanding purposes...
*
* These two routines now deal with copying/expanding/translating the samples
* from user space into our buffer at the right frequency. They take care about
* how much data there's actually to read, how much buffer space there is and
* to convert samples into the right frequency/encoding. They will only work on
* complete samples so it may happen they leave some bytes in the input stream
* if the user didn't write a multiple of the current sample size. They both
* return the number of bytes they've used from both streams so you may detect
* such a situation. Luckily all programs should be able to cope with that.
*
* I think I've optimized anything as far as one can do in plain C, all
* variables should fit in registers and the loops are really short. There's
* one loop for every possible situation. Writing a more generalized and thus
* parameterized loop would only produce slower code. Feel free to optimize
* this in assembler if you like. :)
*
* I think these routines belong here because they're not yet really hardware
* independent, especially the fact that the Falcon can play 16bit samples
* only in stereo is hardcoded in both of them!
*
* ++geert: split in even more functions (one per format)
*/
/*
* Native format
*/
static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
{
ssize_t count, used;
if (!dmasound.soft.stereo) {
void *p = &frame[*frameUsed];
count = min_t(unsigned long, userCount, frameLeft) & ~1;
used = count;
if (copy_from_user(p, userPtr, count))
return -EFAULT;
} else {
u_char *left = &frame[*frameUsed>>1];
u_char *right = left+write_sq_block_size_half;
count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
used = count*2;
while (count > 0) {
if (get_user(*left++, userPtr++)
|| get_user(*right++, userPtr++))
return -EFAULT;
count--;
}
}
*frameUsed += used;
return used;
}
/*
* Copy and convert 8 bit data
*/
#define GENERATE_AMI_CT8(funcname, convsample) \
static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \
u_char frame[], ssize_t *frameUsed, \
ssize_t frameLeft) \
{ \
ssize_t count, used; \
\
if (!dmasound.soft.stereo) { \
u_char *p = &frame[*frameUsed]; \
count = min_t(size_t, userCount, frameLeft) & ~1; \
used = count; \
while (count > 0) { \
u_char data; \
if (get_user(data, userPtr++)) \
return -EFAULT; \
*p++ = convsample(data); \
count--; \
} \
} else { \
u_char *left = &frame[*frameUsed>>1]; \
u_char *right = left+write_sq_block_size_half; \
count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
used = count*2; \
while (count > 0) { \
u_char data; \
if (get_user(data, userPtr++)) \
return -EFAULT; \
*left++ = convsample(data); \
if (get_user(data, userPtr++)) \
return -EFAULT; \
*right++ = convsample(data); \
count--; \
} \
} \
*frameUsed += used; \
return used; \
}
#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)])
#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)])
#define AMI_CT_U8(x) ((x) ^ 0x80)
GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
/*
* Copy and convert 16 bit data
*/
#define GENERATE_AMI_CT_16(funcname, convsample) \
static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \
u_char frame[], ssize_t *frameUsed, \
ssize_t frameLeft) \
{ \
const u_short __user *ptr = (const u_short __user *)userPtr; \
ssize_t count, used; \
u_short data; \
\
if (!dmasound.soft.stereo) { \
u_char *high = &frame[*frameUsed>>1]; \
u_char *low = high+write_sq_block_size_half; \
count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
used = count*2; \
while (count > 0) { \
if (get_user(data, ptr++)) \
return -EFAULT; \
data = convsample(data); \
*high++ = data>>8; \
*low++ = (data>>2) & 0x3f; \
count--; \
} \
} else { \
u_char *lefth = &frame[*frameUsed>>2]; \
u_char *leftl = lefth+write_sq_block_size_quarter; \
u_char *righth = lefth+write_sq_block_size_half; \
u_char *rightl = righth+write_sq_block_size_quarter; \
count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \
used = count*4; \
while (count > 0) { \
if (get_user(data, ptr++)) \
return -EFAULT; \
data = convsample(data); \
*lefth++ = data>>8; \
*leftl++ = (data>>2) & 0x3f; \
if (get_user(data, ptr++)) \
return -EFAULT; \
data = convsample(data); \
*righth++ = data>>8; \
*rightl++ = (data>>2) & 0x3f; \
count--; \
} \
} \
*frameUsed += used; \
return used; \
}
#define AMI_CT_S16BE(x) (x)
#define AMI_CT_U16BE(x) ((x) ^ 0x8000)
#define AMI_CT_S16LE(x) (le2be16((x)))
#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000)
GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
static TRANS transAmiga = {
.ct_ulaw = ami_ct_ulaw,
.ct_alaw = ami_ct_alaw,
.ct_s8 = ami_ct_s8,
.ct_u8 = ami_ct_u8,
.ct_s16be = ami_ct_s16be,
.ct_u16be = ami_ct_u16be,
.ct_s16le = ami_ct_s16le,
.ct_u16le = ami_ct_u16le,
};
/*** Low level stuff *********************************************************/
static inline void StopDMA(void)
{
custom.aud[0].audvol = custom.aud[1].audvol = 0;
custom.aud[2].audvol = custom.aud[3].audvol = 0;
custom.dmacon = AMI_AUDIO_OFF;
enable_heartbeat();
}
static void *AmiAlloc(unsigned int size, gfp_t flags)
{
return amiga_chip_alloc((long)size, "dmasound [Paula]");
}
static void AmiFree(void *obj, unsigned int size)
{
amiga_chip_free (obj);
}
static int __init AmiIrqInit(void)
{
/* turn off DMA for audio channels */
StopDMA();
/* Register interrupt handler. */
if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
AmiInterrupt))
return 0;
return 1;
}
#ifdef MODULE
static void AmiIrqCleanUp(void)
{
/* turn off DMA for audio channels */
StopDMA();
/* release the interrupt */
free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
}
#endif /* MODULE */
static void AmiSilence(void)
{
/* turn off DMA for audio channels */
StopDMA();
}
static void AmiInit(void)
{
int period, i;
AmiSilence();
if (dmasound.soft.speed)
period = amiga_colorclock/dmasound.soft.speed-1;
else
period = amiga_audio_min_period;
dmasound.hard = dmasound.soft;
dmasound.trans_write = &transAmiga;
if (period < amiga_audio_min_period) {
/* we would need to squeeze the sound, but we won't do that */
period = amiga_audio_min_period;
} else if (period > 65535) {
period = 65535;
}
dmasound.hard.speed = amiga_colorclock/(period+1);
for (i = 0; i < 4; i++)
custom.aud[i].audper = period;
amiga_audio_period = period;
}
static int AmiSetFormat(int format)
{
int size;
/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
switch (format) {
case AFMT_QUERY:
return dmasound.soft.format;
case AFMT_MU_LAW:
case AFMT_A_LAW:
case AFMT_U8:
case AFMT_S8:
size = 8;
break;
case AFMT_S16_BE:
case AFMT_U16_BE:
case AFMT_S16_LE:
case AFMT_U16_LE:
size = 16;
break;
default: /* :-) */
size = 8;
format = AFMT_S8;
}
dmasound.soft.format = format;
dmasound.soft.size = size;
if (dmasound.minDev == SND_DEV_DSP) {
dmasound.dsp.format = format;
dmasound.dsp.size = dmasound.soft.size;
}
AmiInit();
return format;
}
#define VOLUME_VOXWARE_TO_AMI(v) \
(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
static int AmiSetVolume(int volume)
{
dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
custom.aud[0].audvol = dmasound.volume_left;
dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
custom.aud[1].audvol = dmasound.volume_right;
if (dmasound.hard.size == 16) {
if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
custom.aud[2].audvol = 1;
custom.aud[3].audvol = 1;
} else {
custom.aud[2].audvol = 0;
custom.aud[3].audvol = 0;
}
}
return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
(VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
}
static int AmiSetTreble(int treble)
{
dmasound.treble = treble;
if (treble < 50)
ciaa.pra &= ~0x02;
else
ciaa.pra |= 0x02;
return treble;
}
#define AMI_PLAY_LOADED 1
#define AMI_PLAY_PLAYING 2
#define AMI_PLAY_MASK 3
static void AmiPlayNextFrame(int index)
{
u_char *start, *ch0, *ch1, *ch2, *ch3;
u_long size;
/* used by AmiPlay() if all doubts whether there really is something
* to be played are already wiped out.
*/
start = write_sq.buffers[write_sq.front];
size = (write_sq.count == index ? write_sq.rear_size
: write_sq.block_size)>>1;
if (dmasound.hard.stereo) {
ch0 = start;
ch1 = start+write_sq_block_size_half;
size >>= 1;
} else {
ch0 = start;
ch1 = start;
}
disable_heartbeat();
custom.aud[0].audvol = dmasound.volume_left;
custom.aud[1].audvol = dmasound.volume_right;
if (dmasound.hard.size == 8) {
custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
custom.aud[0].audlen = size;
custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
custom.aud[1].audlen = size;
custom.dmacon = AMI_AUDIO_8;
} else {
size >>= 1;
custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
custom.aud[0].audlen = size;
custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
custom.aud[1].audlen = size;
if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
/* We can play pseudo 14-bit only with the maximum volume */
ch3 = ch0+write_sq_block_size_quarter;
ch2 = ch1+write_sq_block_size_quarter;
custom.aud[2].audvol = 1; /* we are being affected by the beeps */
custom.aud[3].audvol = 1; /* restoring volume here helps a bit */
custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
custom.aud[2].audlen = size;
custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
custom.aud[3].audlen = size;
custom.dmacon = AMI_AUDIO_14;
} else {
custom.aud[2].audvol = 0;
custom.aud[3].audvol = 0;
custom.dmacon = AMI_AUDIO_8;
}
}
write_sq.front = (write_sq.front+1) % write_sq.max_count;
write_sq.active |= AMI_PLAY_LOADED;
}
static void AmiPlay(void)
{
int minframes = 1;
custom.intena = IF_AUD0;
if (write_sq.active & AMI_PLAY_LOADED) {
/* There's already a frame loaded */
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
if (write_sq.active & AMI_PLAY_PLAYING)
/* Increase threshold: frame 1 is already being played */
minframes = 2;
if (write_sq.count < minframes) {
/* Nothing to do */
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
if (write_sq.count <= minframes &&
write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
/* hmmm, the only existing frame is not
* yet filled and we're not syncing?
*/
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
AmiPlayNextFrame(minframes);
custom.intena = IF_SETCLR | IF_AUD0;
}
static irqreturn_t AmiInterrupt(int irq, void *dummy)
{
int minframes = 1;
custom.intena = IF_AUD0;
if (!write_sq.active) {
/* Playing was interrupted and sq_reset() has already cleared
* the sq variables, so better don't do anything here.
*/
WAKE_UP(write_sq.sync_queue);
return IRQ_HANDLED;
}
if (write_sq.active & AMI_PLAY_PLAYING) {
/* We've just finished a frame */
write_sq.count--;
WAKE_UP(write_sq.action_queue);
}
if (write_sq.active & AMI_PLAY_LOADED)
/* Increase threshold: frame 1 is already being played */
minframes = 2;
/* Shift the flags */
write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
if (!write_sq.active)
/* No frame is playing, disable audio DMA */
StopDMA();
custom.intena = IF_SETCLR | IF_AUD0;
if (write_sq.count >= minframes)
/* Try to play the next frame */
AmiPlay();
if (!write_sq.active)
/* Nothing to play anymore.
Wake up a process waiting for audio output to drain. */
WAKE_UP(write_sq.sync_queue);
return IRQ_HANDLED;
}
/*** Mid level stuff *********************************************************/
/*
* /dev/mixer abstraction
*/
static void __init AmiMixerInit(void)
{
dmasound.volume_left = 64;
dmasound.volume_right = 64;
custom.aud[0].audvol = dmasound.volume_left;
custom.aud[3].audvol = 1; /* For pseudo 14bit */
custom.aud[1].audvol = dmasound.volume_right;
custom.aud[2].audvol = 1; /* For pseudo 14bit */
dmasound.treble = 50;
}
static int AmiMixerIoctl(u_int cmd, u_long arg)
{
int data;
switch (cmd) {
case SOUND_MIXER_READ_DEVMASK:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
case SOUND_MIXER_READ_RECMASK:
return IOCTL_OUT(arg, 0);
case SOUND_MIXER_READ_STEREODEVS:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg,
VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
return IOCTL_OUT(arg, dmasound_set_volume(data));
case SOUND_MIXER_READ_TREBLE:
return IOCTL_OUT(arg, dmasound.treble);
case SOUND_MIXER_WRITE_TREBLE:
IOCTL_IN(arg, data);
return IOCTL_OUT(arg, dmasound_set_treble(data));
}
return -EINVAL;
}
static int AmiWriteSqSetup(void)
{
write_sq_block_size_half = write_sq.block_size>>1;
write_sq_block_size_quarter = write_sq_block_size_half>>1;
return 0;
}
static int AmiStateInfo(char *buffer, size_t space)
{
int len = 0;
len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
dmasound.volume_left);
len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
dmasound.volume_right);
if (len >= space) {
printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
len = space ;
}
return len;
}
/*** Machine definitions *****************************************************/
static SETTINGS def_hard = {
.format = AFMT_S8,
.stereo = 0,
.size = 8,
.speed = 8000
} ;
static SETTINGS def_soft = {
.format = AFMT_U8,
.stereo = 0,
.size = 8,
.speed = 8000
} ;
static MACHINE machAmiga = {
.name = "Amiga",
.name2 = "AMIGA",
.owner = THIS_MODULE,
.dma_alloc = AmiAlloc,
.dma_free = AmiFree,
.irqinit = AmiIrqInit,
#ifdef MODULE
.irqcleanup = AmiIrqCleanUp,
#endif /* MODULE */
.init = AmiInit,
.silence = AmiSilence,
.setFormat = AmiSetFormat,
.setVolume = AmiSetVolume,
.setTreble = AmiSetTreble,
.play = AmiPlay,
.mixer_init = AmiMixerInit,
.mixer_ioctl = AmiMixerIoctl,
.write_sq_setup = AmiWriteSqSetup,
.state_info = AmiStateInfo,
.min_dsp_speed = 8000,
.version = ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
.hardware_afmts = (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
.capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
};
/*** Config & Setup **********************************************************/
static int __init amiga_audio_probe(struct platform_device *pdev)
{
dmasound.mach = machAmiga;
dmasound.mach.default_hard = def_hard ;
dmasound.mach.default_soft = def_soft ;
return dmasound_init();
}
static int __exit amiga_audio_remove(struct platform_device *pdev)
{
dmasound_deinit();
return 0;
}
static struct platform_driver amiga_audio_driver = {
.remove = __exit_p(amiga_audio_remove),
.driver = {
.name = "amiga-audio",
.owner = THIS_MODULE,
},
};
module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:amiga-audio");

View file

@ -0,0 +1,638 @@
/*
* linux/sound/oss/dmasound/dmasound_q40.c
*
* Q40 DMA Sound Driver
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
* prior to 28/01/2001
*
* 28/01/2001 [0.1] Iain Sandoe
* - added versioning
* - put in and populated the hardware_afmts field.
* [0.2] - put in SNDCTL_DSP_GETCAPS value.
* [0.3] - put in default hard/soft settings.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/q40ints.h>
#include <asm/q40_master.h>
#include "dmasound.h"
#define DMASOUND_Q40_REVISION 0
#define DMASOUND_Q40_EDITION 3
static int expand_bal; /* Balance factor for expanding (not volume!) */
static int expand_data; /* Data for expanding */
/*** Low level stuff *********************************************************/
static void *Q40Alloc(unsigned int size, gfp_t flags);
static void Q40Free(void *, unsigned int);
static int Q40IrqInit(void);
#ifdef MODULE
static void Q40IrqCleanUp(void);
#endif
static void Q40Silence(void);
static void Q40Init(void);
static int Q40SetFormat(int format);
static int Q40SetVolume(int volume);
static void Q40PlayNextFrame(int index);
static void Q40Play(void);
static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
static void Q40Interrupt(void);
/*** Mid level stuff *********************************************************/
/* userCount, frameUsed, frameLeft == byte counts */
static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
ssize_t count, used;
u_char *p = (u_char *) &frame[*frameUsed];
used = count = min_t(size_t, userCount, frameLeft);
if (copy_from_user(p,userPtr,count))
return -EFAULT;
while (count > 0) {
*p = table[*p]+128;
p++;
count--;
}
*frameUsed += used ;
return used;
}
static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
u_char *p = (u_char *) &frame[*frameUsed];
used = count = min_t(size_t, userCount, frameLeft);
if (copy_from_user(p,userPtr,count))
return -EFAULT;
while (count > 0) {
*p = *p + 128;
p++;
count--;
}
*frameUsed += used;
return used;
}
static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
u_char *p = (u_char *) &frame[*frameUsed];
used = count = min_t(size_t, userCount, frameLeft);
if (copy_from_user(p,userPtr,count))
return -EFAULT;
*frameUsed += used;
return used;
}
/* a bit too complicated to optimise right now ..*/
static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned char *table = (unsigned char *)
(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
unsigned int data = expand_data;
u_char *p = (u_char *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = table[c];
data += 0x80;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c ;
data += 0x80;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c ;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) ;
utotal -= userCount;
return utotal;
}
/* compressing versions */
static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned char *table = (unsigned char *)
(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
unsigned int data = expand_data;
u_char *p = (u_char *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
while(bal<0) {
if (userCount == 0)
goto lout;
if (!(bal<(-hSpeed))) {
if (get_user(c, userPtr))
return -EFAULT;
data = 0x80 + table[c];
}
userPtr++;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
lout:
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
while (bal < 0) {
if (userCount == 0)
goto lout;
if (!(bal<(-hSpeed))) {
if (get_user(c, userPtr))
return -EFAULT;
data = c + 0x80;
}
userPtr++;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
lout:
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
while (bal < 0) {
if (userCount == 0)
goto lout;
if (!(bal<(-hSpeed))) {
if (get_user(c, userPtr))
return -EFAULT;
data = c ;
}
userPtr++;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
lout:
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) ;
utotal -= userCount;
return utotal;
}
static TRANS transQ40Normal = {
q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
};
static TRANS transQ40Expanding = {
q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
};
static TRANS transQ40Compressing = {
q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
};
/*** Low level stuff *********************************************************/
static void *Q40Alloc(unsigned int size, gfp_t flags)
{
return kmalloc(size, flags); /* change to vmalloc */
}
static void Q40Free(void *ptr, unsigned int size)
{
kfree(ptr);
}
static int __init Q40IrqInit(void)
{
/* Register interrupt handler. */
if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
"DMA sound", Q40Interrupt))
return 0;
return(1);
}
#ifdef MODULE
static void Q40IrqCleanUp(void)
{
master_outb(0,SAMPLE_ENABLE_REG);
free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
}
#endif /* MODULE */
static void Q40Silence(void)
{
master_outb(0,SAMPLE_ENABLE_REG);
*DAC_LEFT=*DAC_RIGHT=127;
}
static char *q40_pp;
static unsigned int q40_sc;
static void Q40PlayNextFrame(int index)
{
u_char *start;
u_long size;
u_char speed;
int error;
/* used by Q40Play() if all doubts whether there really is something
* to be played are already wiped out.
*/
start = write_sq.buffers[write_sq.front];
size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
q40_pp=start;
q40_sc=size;
write_sq.front = (write_sq.front+1) % write_sq.max_count;
write_sq.active++;
speed=(dmasound.hard.speed==10000 ? 0 : 1);
master_outb( 0,SAMPLE_ENABLE_REG);
free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
if (dmasound.soft.stereo)
error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
"Q40 sound", Q40Interrupt);
else
error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
"Q40 sound", Q40Interrupt);
if (error && printk_ratelimit())
pr_err("Couldn't register sound interrupt\n");
master_outb( speed, SAMPLE_RATE_REG);
master_outb( 1,SAMPLE_CLEAR_REG);
master_outb( 1,SAMPLE_ENABLE_REG);
}
static void Q40Play(void)
{
unsigned long flags;
if (write_sq.active || write_sq.count<=0 ) {
/* There's already a frame loaded */
return;
}
/* nothing in the queue */
if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
/* hmmm, the only existing frame is not
* yet filled and we're not syncing?
*/
return;
}
spin_lock_irqsave(&dmasound.lock, flags);
Q40PlayNextFrame(1);
spin_unlock_irqrestore(&dmasound.lock, flags);
}
static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
{
spin_lock(&dmasound.lock);
if (q40_sc>1){
*DAC_LEFT=*q40_pp++;
*DAC_RIGHT=*q40_pp++;
q40_sc -=2;
master_outb(1,SAMPLE_CLEAR_REG);
}else Q40Interrupt();
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
{
spin_lock(&dmasound.lock);
if (q40_sc>0){
*DAC_LEFT=*q40_pp;
*DAC_RIGHT=*q40_pp++;
q40_sc --;
master_outb(1,SAMPLE_CLEAR_REG);
}else Q40Interrupt();
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static void Q40Interrupt(void)
{
if (!write_sq.active) {
/* playing was interrupted and sq_reset() has already cleared
* the sq variables, so better don't do anything here.
*/
WAKE_UP(write_sq.sync_queue);
master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
goto exit;
} else write_sq.active=0;
write_sq.count--;
Q40Play();
if (q40_sc<2)
{ /* there was nothing to play, disable irq */
master_outb(0,SAMPLE_ENABLE_REG);
*DAC_LEFT=*DAC_RIGHT=127;
}
WAKE_UP(write_sq.action_queue);
exit:
master_outb(1,SAMPLE_CLEAR_REG);
}
static void Q40Init(void)
{
int i, idx;
const int freq[] = {10000, 20000};
/* search a frequency that fits into the allowed error range */
idx = -1;
for (i = 0; i < 2; i++)
if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
idx = i;
dmasound.hard = dmasound.soft;
/*sound.hard.stereo=1;*/ /* no longer true */
dmasound.hard.size=8;
if (idx > -1) {
dmasound.soft.speed = freq[idx];
dmasound.trans_write = &transQ40Normal;
} else
dmasound.trans_write = &transQ40Expanding;
Q40Silence();
if (dmasound.hard.speed > 20200) {
/* squeeze the sound, we do that */
dmasound.hard.speed = 20000;
dmasound.trans_write = &transQ40Compressing;
} else if (dmasound.hard.speed > 10000) {
dmasound.hard.speed = 20000;
} else {
dmasound.hard.speed = 10000;
}
expand_bal = -dmasound.soft.speed;
}
static int Q40SetFormat(int format)
{
/* Q40 sound supports only 8bit modes */
switch (format) {
case AFMT_QUERY:
return(dmasound.soft.format);
case AFMT_MU_LAW:
case AFMT_A_LAW:
case AFMT_S8:
case AFMT_U8:
break;
default:
format = AFMT_S8;
}
dmasound.soft.format = format;
dmasound.soft.size = 8;
if (dmasound.minDev == SND_DEV_DSP) {
dmasound.dsp.format = format;
dmasound.dsp.size = 8;
}
Q40Init();
return(format);
}
static int Q40SetVolume(int volume)
{
return 0;
}
/*** Machine definitions *****************************************************/
static SETTINGS def_hard = {
.format = AFMT_U8,
.stereo = 0,
.size = 8,
.speed = 10000
} ;
static SETTINGS def_soft = {
.format = AFMT_U8,
.stereo = 0,
.size = 8,
.speed = 8000
} ;
static MACHINE machQ40 = {
.name = "Q40",
.name2 = "Q40",
.owner = THIS_MODULE,
.dma_alloc = Q40Alloc,
.dma_free = Q40Free,
.irqinit = Q40IrqInit,
#ifdef MODULE
.irqcleanup = Q40IrqCleanUp,
#endif /* MODULE */
.init = Q40Init,
.silence = Q40Silence,
.setFormat = Q40SetFormat,
.setVolume = Q40SetVolume,
.play = Q40Play,
.min_dsp_speed = 10000,
.version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
.hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
.capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
};
/*** Config & Setup **********************************************************/
static int __init dmasound_q40_init(void)
{
if (MACH_IS_Q40) {
dmasound.mach = machQ40;
dmasound.mach.default_hard = def_hard ;
dmasound.mach.default_soft = def_soft ;
return dmasound_init();
} else
return -ENODEV;
}
static void __exit dmasound_q40_cleanup(void)
{
dmasound_deinit();
}
module_init(dmasound_q40_init);
module_exit(dmasound_q40_cleanup);
MODULE_DESCRIPTION("Q40/Q60 sound driver");
MODULE_LICENSE("GPL");

101
sound/oss/hex2hex.c Normal file
View file

@ -0,0 +1,101 @@
/*
* hex2hex reads stdin in Intel HEX format and produces an
* (unsigned char) array which contains the bytes and writes it
* to stdout using C syntax
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); }
#define MAX_SIZE (256*1024)
unsigned char buf[MAX_SIZE];
static int loadhex(FILE *inf, unsigned char *buf)
{
int l=0, c, i;
while ((c=getc(inf))!=EOF)
{
if (c == ':') /* Sync with beginning of line */
{
int n, check;
unsigned char sum;
int addr;
int linetype;
if (fscanf(inf, "%02x", &n) != 1)
ABANDON("File format error");
sum = n;
if (fscanf(inf, "%04x", &addr) != 1)
ABANDON("File format error");
sum += addr/256;
sum += addr%256;
if (fscanf(inf, "%02x", &linetype) != 1)
ABANDON("File format error");
sum += linetype;
if (linetype != 0)
continue;
for (i=0;i<n;i++)
{
if (fscanf(inf, "%02x", &c) != 1)
ABANDON("File format error");
if (addr >= MAX_SIZE)
ABANDON("File too large");
buf[addr++] = c;
if (addr > l)
l = addr;
sum += c;
}
if (fscanf(inf, "%02x", &check) != 1)
ABANDON("File format error");
sum = ~sum + 1;
if (check != sum)
ABANDON("Line checksum error");
}
}
return l;
}
int main( int argc, const char * argv [] )
{
const char * varline;
int i,l;
int id=0;
if(argv[1] && strcmp(argv[1], "-i")==0)
{
argv++;
argc--;
id=1;
}
if(argv[1]==NULL)
{
fprintf(stderr,"hex2hex: [-i] filename\n");
exit(1);
}
varline = argv[1];
l = loadhex(stdin, buf);
printf("/*\n *\t Computer generated file. Do not edit.\n */\n");
printf("static int %s_len = %d;\n", varline, l);
printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":"");
for (i=0;i<l;i++)
{
if (i) printf(",");
if (i && !(i % 16)) printf("\n");
printf("0x%02x", buf[i]);
}
printf("\n};\n\n");
return 0;
}

229
sound/oss/kahlua.c Normal file
View file

@ -0,0 +1,229 @@
/*
* Initialisation code for Cyrix/NatSemi VSA1 softaudio
*
* (C) Copyright 2003 Red Hat Inc <alan@lxorguk.ukuu.org.uk>
*
* XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
* The older version (VSA1) provides fairly good soundblaster emulation
* although there are a couple of bugs: large DMA buffers break record,
* and the MPU event handling seems suspect. VSA2 allows the native driver
* to control the AC97 audio engine directly and requires a different driver.
*
* Thanks to National Semiconductor for providing the needed information
* on the XpressAudio(tm) internals.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* TO DO:
* Investigate whether we can portably support Cognac (5520) in the
* same manner.
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "sound_config.h"
#include "sb.h"
/*
* Read a soundblaster compatible mixer register.
* In this case we are actually reading an SMI trap
* not real hardware.
*/
static u8 mixer_read(unsigned long io, u8 reg)
{
outb(reg, io + 4);
udelay(20);
reg = inb(io + 5);
udelay(20);
return reg;
}
static int probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct address_info *hw_config;
unsigned long base;
void __iomem *mem;
unsigned long io;
u16 map;
u8 irq, dma8, dma16;
int oldquiet;
extern int sb_be_quiet;
base = pci_resource_start(pdev, 0);
if(base == 0UL)
return 1;
mem = ioremap(base, 128);
if (!mem)
return 1;
map = readw(mem + 0x18); /* Read the SMI enables */
iounmap(mem);
/* Map bits
0:1 * 0x20 + 0x200 = sb base
2 sb enable
3 adlib enable
5 MPU enable 0x330
6 MPU enable 0x300
The other bits may be used internally so must be masked */
io = 0x220 + 0x20 * (map & 3);
if(map & (1<<2))
printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io);
else
return 1;
if(map & (1<<5))
printk(KERN_INFO "kahlua: MPU at 0x300\n");
else if(map & (1<<6))
printk(KERN_INFO "kahlua: MPU at 0x330\n");
irq = mixer_read(io, 0x80) & 0x0F;
dma8 = mixer_read(io, 0x81);
// printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8);
if(dma8 & 0x20)
dma16 = 5;
else if(dma8 & 0x40)
dma16 = 6;
else if(dma8 & 0x80)
dma16 = 7;
else
{
printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n");
return 1;
}
if(dma8 & 0x01)
dma8 = 0;
else if(dma8 & 0x02)
dma8 = 1;
else if(dma8 & 0x08)
dma8 = 3;
else
{
printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n");
return 1;
}
if(irq & 1)
irq = 9;
else if(irq & 2)
irq = 5;
else if(irq & 4)
irq = 7;
else if(irq & 8)
irq = 10;
else
{
printk(KERN_ERR "kahlua: SB IRQ not set.\n");
return 1;
}
printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n",
irq, dma8, dma16);
hw_config = kzalloc(sizeof(struct address_info), GFP_KERNEL);
if(hw_config == NULL)
{
printk(KERN_ERR "kahlua: out of memory.\n");
return 1;
}
pci_set_drvdata(pdev, hw_config);
hw_config->io_base = io;
hw_config->irq = irq;
hw_config->dma = dma8;
hw_config->dma2 = dma16;
hw_config->name = "Cyrix XpressAudio";
hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ;
if (!request_region(io, 16, "soundblaster"))
goto err_out_free;
if(sb_dsp_detect(hw_config, 0, 0, NULL)==0)
{
printk(KERN_ERR "kahlua: audio not responding.\n");
release_region(io, 16);
goto err_out_free;
}
oldquiet = sb_be_quiet;
sb_be_quiet = 1;
if(sb_dsp_init(hw_config, THIS_MODULE))
{
sb_be_quiet = oldquiet;
goto err_out_free;
}
sb_be_quiet = oldquiet;
return 0;
err_out_free:
kfree(hw_config);
return 1;
}
static void remove_one(struct pci_dev *pdev)
{
struct address_info *hw_config = pci_get_drvdata(pdev);
sb_dsp_unload(hw_config, 0);
kfree(hw_config);
}
MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio");
MODULE_LICENSE("GPL");
/*
* 5530 only. The 5510/5520 decode is different.
*/
static const struct pci_device_id id_tbl[] = {
{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO), 0 },
{ }
};
MODULE_DEVICE_TABLE(pci, id_tbl);
static struct pci_driver kahlua_driver = {
.name = "kahlua",
.id_table = id_tbl,
.probe = probe_one,
.remove = remove_one,
};
static int __init kahlua_init_module(void)
{
printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n");
return pci_register_driver(&kahlua_driver);
}
static void kahlua_cleanup_module(void)
{
pci_unregister_driver(&kahlua_driver);
}
module_init(kahlua_init_module);
module_exit(kahlua_cleanup_module);

22
sound/oss/midi_ctrl.h Normal file
View file

@ -0,0 +1,22 @@
static unsigned char ctrl_def_values[128] =
{
0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */
0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */
0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */
0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 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,0x7f,0x7f, 0x7f,0x7f,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 */
};

712
sound/oss/midi_synth.c Normal file
View file

@ -0,0 +1,712 @@
/*
* sound/oss/midi_synth.c
*
* High level midi sequencer manager for dumb MIDI interfaces.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Andrew Veliath : fixed running status in MIDI input state machine
*/
#define USE_SEQ_MACROS
#define USE_SIMPLE_MACROS
#include "sound_config.h"
#define _MIDI_SYNTH_C_
#include "midi_synth.h"
static int midi2synth[MAX_MIDI_DEV];
static int sysex_state[MAX_MIDI_DEV] =
{0};
static unsigned char prev_out_status[MAX_MIDI_DEV];
#define STORE(cmd) \
{ \
int len; \
unsigned char obuf[8]; \
cmd; \
seq_input_event(obuf, len); \
}
#define _seqbuf obuf
#define _seqbufptr 0
#define _SEQ_ADVBUF(x) len=x
void
do_midi_msg(int synthno, unsigned char *msg, int mlen)
{
switch (msg[0] & 0xf0)
{
case 0x90:
if (msg[2] != 0)
{
STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
break;
}
msg[2] = 64;
case 0x80:
STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
break;
case 0xA0:
STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
break;
case 0xB0:
STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f,
msg[1], msg[2]));
break;
case 0xC0:
STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1]));
break;
case 0xD0:
STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1]));
break;
case 0xE0:
STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
(msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7)));
break;
default:
/* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */
;
}
}
EXPORT_SYMBOL(do_midi_msg);
static void
midi_outc(int midi_dev, int data)
{
int timeout;
for (timeout = 0; timeout < 3200; timeout++)
if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff)))
{
if (data & 0x80) /*
* Status byte
*/
prev_out_status[midi_dev] =
(unsigned char) (data & 0xff); /*
* Store for running status
*/
return; /*
* Mission complete
*/
}
/*
* Sorry! No space on buffers.
*/
printk("Midi send timed out\n");
}
static int
prefix_cmd(int midi_dev, unsigned char status)
{
if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
return 1;
return midi_devs[midi_dev]->prefix_cmd(midi_dev, status);
}
static void
midi_synth_input(int orig_dev, unsigned char data)
{
int dev;
struct midi_input_info *inc;
static unsigned char len_tab[] = /* # of data bytes following a status
*/
{
2, /* 8x */
2, /* 9x */
2, /* Ax */
2, /* Bx */
1, /* Cx */
1, /* Dx */
2, /* Ex */
0 /* Fx */
};
if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
return;
if (data == 0xfe) /* Ignore active sensing */
return;
dev = midi2synth[orig_dev];
inc = &midi_devs[orig_dev]->in_info;
switch (inc->m_state)
{
case MST_INIT:
if (data & 0x80) /* MIDI status byte */
{
if ((data & 0xf0) == 0xf0) /* Common message */
{
switch (data)
{
case 0xf0: /* Sysex */
inc->m_state = MST_SYSEX;
break; /* Sysex */
case 0xf1: /* MTC quarter frame */
case 0xf3: /* Song select */
inc->m_state = MST_DATA;
inc->m_ptr = 1;
inc->m_left = 1;
inc->m_buf[0] = data;
break;
case 0xf2: /* Song position pointer */
inc->m_state = MST_DATA;
inc->m_ptr = 1;
inc->m_left = 2;
inc->m_buf[0] = data;
break;
default:
inc->m_buf[0] = data;
inc->m_ptr = 1;
do_midi_msg(dev, inc->m_buf, inc->m_ptr);
inc->m_ptr = 0;
inc->m_left = 0;
}
} else
{
inc->m_state = MST_DATA;
inc->m_ptr = 1;
inc->m_left = len_tab[(data >> 4) - 8];
inc->m_buf[0] = inc->m_prev_status = data;
}
} else if (inc->m_prev_status & 0x80) {
/* Data byte (use running status) */
inc->m_ptr = 2;
inc->m_buf[1] = data;
inc->m_buf[0] = inc->m_prev_status;
inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1;
if (inc->m_left > 0)
inc->m_state = MST_DATA; /* Not done yet */
else {
inc->m_state = MST_INIT;
do_midi_msg(dev, inc->m_buf, inc->m_ptr);
inc->m_ptr = 0;
}
}
break; /* MST_INIT */
case MST_DATA:
inc->m_buf[inc->m_ptr++] = data;
if (--inc->m_left <= 0)
{
inc->m_state = MST_INIT;
do_midi_msg(dev, inc->m_buf, inc->m_ptr);
inc->m_ptr = 0;
}
break; /* MST_DATA */
case MST_SYSEX:
if (data == 0xf7) /* Sysex end */
{
inc->m_state = MST_INIT;
inc->m_left = 0;
inc->m_ptr = 0;
}
break; /* MST_SYSEX */
default:
printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
inc->m_state = MST_INIT;
}
}
static void
leave_sysex(int dev)
{
int orig_dev = synth_devs[dev]->midi_dev;
int timeout = 0;
if (!sysex_state[dev])
return;
sysex_state[dev] = 0;
while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) &&
timeout < 1000)
timeout++;
sysex_state[dev] = 0;
}
static void
midi_synth_output(int dev)
{
/*
* Currently NOP
*/
}
int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
{
/*
* int orig_dev = synth_devs[dev]->midi_dev;
*/
switch (cmd) {
case SNDCTL_SYNTH_INFO:
if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info)))
return -EFAULT;
return 0;
case SNDCTL_SYNTH_MEMAVL:
return 0x7fffffff;
default:
return -EINVAL;
}
}
EXPORT_SYMBOL(midi_synth_ioctl);
int
midi_synth_kill_note(int dev, int channel, int note, int velocity)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, chn;
if (note < 0 || note > 127)
return 0;
if (channel < 0 || channel > 15)
return 0;
if (velocity < 0)
velocity = 0;
if (velocity > 127)
velocity = 127;
leave_sysex(dev);
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
{ /*
* Use running status
*/
if (!prefix_cmd(orig_dev, note))
return 0;
midi_outc(orig_dev, note);
if (msg == 0x90) /*
* Running status = Note on
*/
midi_outc(orig_dev, 0); /*
* Note on with velocity 0 == note
* off
*/
else
midi_outc(orig_dev, velocity);
} else
{
if (velocity == 64)
{
if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
return 0;
midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
* Note on
*/
midi_outc(orig_dev, note);
midi_outc(orig_dev, 0); /*
* Zero G
*/
} else
{
if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f)))
return 0;
midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /*
* Note off
*/
midi_outc(orig_dev, note);
midi_outc(orig_dev, velocity);
}
}
return 0;
}
EXPORT_SYMBOL(midi_synth_kill_note);
int
midi_synth_set_instr(int dev, int channel, int instr_no)
{
int orig_dev = synth_devs[dev]->midi_dev;
if (instr_no < 0 || instr_no > 127)
instr_no = 0;
if (channel < 0 || channel > 15)
return 0;
leave_sysex(dev);
if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f)))
return 0;
midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /*
* Program change
*/
midi_outc(orig_dev, instr_no);
return 0;
}
EXPORT_SYMBOL(midi_synth_set_instr);
int
midi_synth_start_note(int dev, int channel, int note, int velocity)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, chn;
if (note < 0 || note > 127)
return 0;
if (channel < 0 || channel > 15)
return 0;
if (velocity < 0)
velocity = 0;
if (velocity > 127)
velocity = 127;
leave_sysex(dev);
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (chn == channel && msg == 0x90)
{ /*
* Use running status
*/
if (!prefix_cmd(orig_dev, note))
return 0;
midi_outc(orig_dev, note);
midi_outc(orig_dev, velocity);
} else
{
if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
return 0;
midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
* Note on
*/
midi_outc(orig_dev, note);
midi_outc(orig_dev, velocity);
}
return 0;
}
EXPORT_SYMBOL(midi_synth_start_note);
void
midi_synth_reset(int dev)
{
leave_sysex(dev);
}
EXPORT_SYMBOL(midi_synth_reset);
int
midi_synth_open(int dev, int mode)
{
int orig_dev = synth_devs[dev]->midi_dev;
int err;
struct midi_input_info *inc;
if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
return -ENXIO;
midi2synth[orig_dev] = dev;
sysex_state[dev] = 0;
prev_out_status[orig_dev] = 0;
if ((err = midi_devs[orig_dev]->open(orig_dev, mode,
midi_synth_input, midi_synth_output)) < 0)
return err;
inc = &midi_devs[orig_dev]->in_info;
/* save_flags(flags);
cli();
don't know against what irqhandler to protect*/
inc->m_busy = 0;
inc->m_state = MST_INIT;
inc->m_ptr = 0;
inc->m_left = 0;
inc->m_prev_status = 0x00;
/* restore_flags(flags); */
return 1;
}
EXPORT_SYMBOL(midi_synth_open);
void
midi_synth_close(int dev)
{
int orig_dev = synth_devs[dev]->midi_dev;
leave_sysex(dev);
/*
* Shut up the synths by sending just single active sensing message.
*/
midi_devs[orig_dev]->outputc(orig_dev, 0xfe);
midi_devs[orig_dev]->close(orig_dev);
}
EXPORT_SYMBOL(midi_synth_close);
void
midi_synth_hw_control(int dev, unsigned char *event)
{
}
EXPORT_SYMBOL(midi_synth_hw_control);
int
midi_synth_load_patch(int dev, int format, const char __user *addr,
int count, int pmgr_flag)
{
int orig_dev = synth_devs[dev]->midi_dev;
struct sysex_info sysex;
int i;
unsigned long left, src_offs, eox_seen = 0;
int first_byte = 1;
int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
leave_sysex(dev);
if (!prefix_cmd(orig_dev, 0xf0))
return 0;
/* Invalid patch format */
if (format != SYSEX_PATCH)
return -EINVAL;
/* Patch header too short */
if (count < hdr_size)
return -EINVAL;
count -= hdr_size;
/*
* Copy the header from user space
*/
if (copy_from_user(&sysex, addr, hdr_size))
return -EFAULT;
/* Sysex record too short */
if ((unsigned)count < (unsigned)sysex.len)
sysex.len = count;
left = sysex.len;
src_offs = 0;
for (i = 0; i < left && !signal_pending(current); i++)
{
unsigned char data;
if (get_user(data,
(unsigned char __user *)(addr + hdr_size + i)))
return -EFAULT;
eox_seen = (i > 0 && data & 0x80); /* End of sysex */
if (eox_seen && data != 0xf7)
data = 0xf7;
if (i == 0)
{
if (data != 0xf0)
{
printk(KERN_WARNING "midi_synth: Sysex start missing\n");
return -EINVAL;
}
}
while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
!signal_pending(current))
schedule();
if (!first_byte && data & 0x80)
return 0;
first_byte = 0;
}
if (!eox_seen)
midi_outc(orig_dev, 0xf7);
return 0;
}
EXPORT_SYMBOL(midi_synth_load_patch);
void midi_synth_panning(int dev, int channel, int pressure)
{
}
EXPORT_SYMBOL(midi_synth_panning);
void midi_synth_aftertouch(int dev, int channel, int pressure)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, chn;
if (pressure < 0 || pressure > 127)
return;
if (channel < 0 || channel > 15)
return;
leave_sysex(dev);
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (msg != 0xd0 || chn != channel) /*
* Test for running status
*/
{
if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f)))
return;
midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /*
* Channel pressure
*/
} else if (!prefix_cmd(orig_dev, pressure))
return;
midi_outc(orig_dev, pressure);
}
EXPORT_SYMBOL(midi_synth_aftertouch);
void
midi_synth_controller(int dev, int channel, int ctrl_num, int value)
{
int orig_dev = synth_devs[dev]->midi_dev;
int chn, msg;
if (ctrl_num < 0 || ctrl_num > 127)
return;
if (channel < 0 || channel > 15)
return;
leave_sysex(dev);
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (msg != 0xb0 || chn != channel)
{
if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f)))
return;
midi_outc(orig_dev, 0xb0 | (channel & 0x0f));
} else if (!prefix_cmd(orig_dev, ctrl_num))
return;
midi_outc(orig_dev, ctrl_num);
midi_outc(orig_dev, value & 0x7f);
}
EXPORT_SYMBOL(midi_synth_controller);
void
midi_synth_bender(int dev, int channel, int value)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, prev_chn;
if (channel < 0 || channel > 15)
return;
if (value < 0 || value > 16383)
return;
leave_sysex(dev);
msg = prev_out_status[orig_dev] & 0xf0;
prev_chn = prev_out_status[orig_dev] & 0x0f;
if (msg != 0xd0 || prev_chn != channel) /*
* Test for running status
*/
{
if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f)))
return;
midi_outc(orig_dev, 0xe0 | (channel & 0x0f));
} else if (!prefix_cmd(orig_dev, value & 0x7f))
return;
midi_outc(orig_dev, value & 0x7f);
midi_outc(orig_dev, (value >> 7) & 0x7f);
}
EXPORT_SYMBOL(midi_synth_bender);
void
midi_synth_setup_voice(int dev, int voice, int channel)
{
}
EXPORT_SYMBOL(midi_synth_setup_voice);
int
midi_synth_send_sysex(int dev, unsigned char *bytes, int len)
{
int orig_dev = synth_devs[dev]->midi_dev;
int i;
for (i = 0; i < len; i++)
{
switch (bytes[i])
{
case 0xf0: /* Start sysex */
if (!prefix_cmd(orig_dev, 0xf0))
return 0;
sysex_state[dev] = 1;
break;
case 0xf7: /* End sysex */
if (!sysex_state[dev]) /* Orphan sysex end */
return 0;
sysex_state[dev] = 0;
break;
default:
if (!sysex_state[dev])
return 0;
if (bytes[i] & 0x80) /* Error. Another message before sysex end */
{
bytes[i] = 0xf7; /* Sysex end */
sysex_state[dev] = 0;
}
}
if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]))
{
/*
* Hardware level buffer is full. Abort the sysex message.
*/
int timeout = 0;
bytes[i] = 0xf7;
sysex_state[dev] = 0;
while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) &&
timeout < 1000)
timeout++;
}
if (!sysex_state[dev])
return 0;
}
return 0;
}
EXPORT_SYMBOL(midi_synth_send_sysex);

47
sound/oss/midi_synth.h Normal file
View file

@ -0,0 +1,47 @@
int midi_synth_ioctl (int dev,
unsigned int cmd, void __user * 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, const char __user * addr,
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);
void midi_synth_bender (int dev, int chn, int value);
void midi_synth_setup_voice (int dev, int voice, int chn);
int midi_synth_send_sysex(int dev, unsigned char *bytes,int len);
#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 =
{
.owner = THIS_MODULE,
.id = "MIDI",
.info = &std_synth_info,
.midi_dev = 0,
.synth_type = SYNTH_TYPE_MIDI,
.synth_subtype = 0,
.open = midi_synth_open,
.close = midi_synth_close,
.ioctl = midi_synth_ioctl,
.kill_note = midi_synth_kill_note,
.start_note = midi_synth_start_note,
.set_instr = midi_synth_set_instr,
.reset = midi_synth_reset,
.hw_control = midi_synth_hw_control,
.load_patch = midi_synth_load_patch,
.aftertouch = midi_synth_aftertouch,
.controller = midi_synth_controller,
.panning = midi_synth_panning,
.bender = midi_synth_bender,
.setup_voice = midi_synth_setup_voice,
.send_sysex = midi_synth_send_sysex
};
#endif

425
sound/oss/midibuf.c Normal file
View file

@ -0,0 +1,425 @@
/*
* sound/oss/midibuf.c
*
* Device file manager for /dev/midi#
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
*/
#include <linux/stddef.h>
#include <linux/kmod.h>
#include <linux/spinlock.h>
#define MIDIBUF_C
#include "sound_config.h"
/*
* Don't make MAX_QUEUE_SIZE larger than 4000
*/
#define MAX_QUEUE_SIZE 4000
static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV];
static wait_queue_head_t input_sleeper[MAX_MIDI_DEV];
struct midi_buf
{
int len, head, tail;
unsigned char queue[MAX_QUEUE_SIZE];
};
struct midi_parms
{
long prech_timeout; /*
* Timeout before the first ch
*/
};
static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
static struct midi_parms parms[MAX_MIDI_DEV];
static void midi_poll(unsigned long dummy);
static DEFINE_TIMER(poll_timer, midi_poll, 0, 0);
static volatile int open_devs;
static DEFINE_SPINLOCK(lock);
#define DATA_AVAIL(q) (q->len)
#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
#define QUEUE_BYTE(q, data) \
if (SPACE_AVAIL(q)) \
{ \
unsigned long flags; \
spin_lock_irqsave(&lock, flags); \
q->queue[q->tail] = (data); \
q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
spin_unlock_irqrestore(&lock, flags); \
}
#define REMOVE_BYTE(q, data) \
if (DATA_AVAIL(q)) \
{ \
unsigned long flags; \
spin_lock_irqsave(&lock, flags); \
data = q->queue[q->head]; \
q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
spin_unlock_irqrestore(&lock, flags); \
}
static void drain_midi_queue(int dev)
{
/*
* Give the Midi driver time to drain its output queues
*/
if (midi_devs[dev]->buffer_status != NULL)
wait_event_interruptible_timeout(midi_sleeper[dev],
!midi_devs[dev]->buffer_status(dev), HZ/10);
}
static void midi_input_intr(int dev, unsigned char data)
{
if (midi_in_buf[dev] == NULL)
return;
if (data == 0xfe) /*
* Active sensing
*/
return; /*
* Ignore
*/
if (SPACE_AVAIL(midi_in_buf[dev])) {
QUEUE_BYTE(midi_in_buf[dev], data);
wake_up(&input_sleeper[dev]);
}
}
static void midi_output_intr(int dev)
{
/*
* Currently NOP
*/
}
static void midi_poll(unsigned long dummy)
{
unsigned long flags;
int dev;
spin_lock_irqsave(&lock, flags);
if (open_devs)
{
for (dev = 0; dev < num_midis; dev++)
if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
{
while (DATA_AVAIL(midi_out_buf[dev]))
{
int ok;
int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
ok = midi_devs[dev]->outputc(dev, c);
spin_lock_irqsave(&lock, flags);
if (!ok)
break;
midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
midi_out_buf[dev]->len--;
}
if (DATA_AVAIL(midi_out_buf[dev]) < 100)
wake_up(&midi_sleeper[dev]);
}
poll_timer.expires = (1) + jiffies;
add_timer(&poll_timer);
/*
* Come back later
*/
}
spin_unlock_irqrestore(&lock, flags);
}
int MIDIbuf_open(int dev, struct file *file)
{
int mode, err;
dev = dev >> 4;
mode = translate_mode(file);
if (num_midis > MAX_MIDI_DEV)
{
printk(KERN_ERR "midi: Too many midi interfaces\n");
num_midis = MAX_MIDI_DEV;
}
if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
return -ENXIO;
/*
* Interrupts disabled. Be careful
*/
module_put(midi_devs[dev]->owner);
if ((err = midi_devs[dev]->open(dev, mode,
midi_input_intr, midi_output_intr)) < 0)
return err;
parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT;
midi_in_buf[dev] = vmalloc(sizeof(struct midi_buf));
if (midi_in_buf[dev] == NULL)
{
printk(KERN_WARNING "midi: Can't allocate buffer\n");
midi_devs[dev]->close(dev);
return -EIO;
}
midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
midi_out_buf[dev] = vmalloc(sizeof(struct midi_buf));
if (midi_out_buf[dev] == NULL)
{
printk(KERN_WARNING "midi: Can't allocate buffer\n");
midi_devs[dev]->close(dev);
vfree(midi_in_buf[dev]);
midi_in_buf[dev] = NULL;
return -EIO;
}
midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
open_devs++;
init_waitqueue_head(&midi_sleeper[dev]);
init_waitqueue_head(&input_sleeper[dev]);
if (open_devs < 2) /* This was first open */
{
poll_timer.expires = 1 + jiffies;
add_timer(&poll_timer); /* Start polling */
}
return err;
}
void MIDIbuf_release(int dev, struct file *file)
{
int mode;
dev = dev >> 4;
mode = translate_mode(file);
if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
return;
/*
* Wait until the queue is empty
*/
if (mode != OPEN_READ)
{
midi_devs[dev]->outputc(dev, 0xfe); /*
* Active sensing to shut the
* devices
*/
wait_event_interruptible(midi_sleeper[dev],
!DATA_AVAIL(midi_out_buf[dev]));
/*
* Sync
*/
drain_midi_queue(dev); /*
* Ensure the output queues are empty
*/
}
midi_devs[dev]->close(dev);
open_devs--;
if (open_devs == 0)
del_timer_sync(&poll_timer);
vfree(midi_in_buf[dev]);
vfree(midi_out_buf[dev]);
midi_in_buf[dev] = NULL;
midi_out_buf[dev] = NULL;
module_put(midi_devs[dev]->owner);
}
int MIDIbuf_write(int dev, struct file *file, const char __user *buf, int count)
{
int c, n, i;
unsigned char tmp_data;
dev = dev >> 4;
if (!count)
return 0;
c = 0;
while (c < count)
{
n = SPACE_AVAIL(midi_out_buf[dev]);
if (n == 0) { /*
* No space just now.
*/
if (file->f_flags & O_NONBLOCK) {
c = -EAGAIN;
goto out;
}
if (wait_event_interruptible(midi_sleeper[dev],
SPACE_AVAIL(midi_out_buf[dev])))
{
c = -EINTR;
goto out;
}
n = SPACE_AVAIL(midi_out_buf[dev]);
}
if (n > (count - c))
n = count - c;
for (i = 0; i < n; i++)
{
/* BROKE BROKE BROKE - CAN'T DO THIS WITH CLI !! */
/* yes, think the same, so I removed the cli() brackets
QUEUE_BYTE is protected against interrupts */
if (copy_from_user((char *) &tmp_data, &(buf)[c], 1)) {
c = -EFAULT;
goto out;
}
QUEUE_BYTE(midi_out_buf[dev], tmp_data);
c++;
}
}
out:
return c;
}
int MIDIbuf_read(int dev, struct file *file, char __user *buf, int count)
{
int n, c = 0;
unsigned char tmp_data;
dev = dev >> 4;
if (!DATA_AVAIL(midi_in_buf[dev])) { /*
* No data yet, wait
*/
if (file->f_flags & O_NONBLOCK) {
c = -EAGAIN;
goto out;
}
wait_event_interruptible_timeout(input_sleeper[dev],
DATA_AVAIL(midi_in_buf[dev]),
parms[dev].prech_timeout);
if (signal_pending(current))
c = -EINTR; /* The user is getting restless */
}
if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /*
* Got some bytes
*/
{
n = DATA_AVAIL(midi_in_buf[dev]);
if (n > count)
n = count;
c = 0;
while (c < n)
{
char *fixit;
REMOVE_BYTE(midi_in_buf[dev], tmp_data);
fixit = (char *) &tmp_data;
/* BROKE BROKE BROKE */
/* yes removed the cli() brackets again
should q->len,tail&head be atomic_t? */
if (copy_to_user(&(buf)[c], fixit, 1)) {
c = -EFAULT;
goto out;
}
c++;
}
}
out:
return c;
}
int MIDIbuf_ioctl(int dev, struct file *file,
unsigned int cmd, void __user *arg)
{
int val;
dev = dev >> 4;
if (((cmd >> 8) & 0xff) == 'C')
{
if (midi_devs[dev]->coproc) /* Coprocessor ioctl */
return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/
return -ENXIO;
}
else
{
switch (cmd)
{
case SNDCTL_MIDI_PRETIME:
if (get_user(val, (int __user *)arg))
return -EFAULT;
if (val < 0)
val = 0;
val = (HZ * val) / 10;
parms[dev].prech_timeout = val;
return put_user(val, (int __user *)arg);
default:
if (!midi_devs[dev]->ioctl)
return -EINVAL;
return midi_devs[dev]->ioctl(dev, cmd, arg);
}
}
}
/* No kernel lock - fine */
unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait)
{
unsigned int mask = 0;
dev = dev >> 4;
/* input */
poll_wait(file, &input_sleeper[dev], wait);
if (DATA_AVAIL(midi_in_buf[dev]))
mask |= POLLIN | POLLRDNORM;
/* output */
poll_wait(file, &midi_sleeper[dev], wait);
if (!SPACE_AVAIL(midi_out_buf[dev]))
mask |= POLLOUT | POLLWRNORM;
return mask;
}
int MIDIbuf_avail(int dev)
{
if (midi_in_buf[dev])
return DATA_AVAIL (midi_in_buf[dev]);
return 0;
}
EXPORT_SYMBOL(MIDIbuf_avail);

1804
sound/oss/mpu401.c Normal file

File diff suppressed because it is too large Load diff

11
sound/oss/mpu401.h Normal file
View file

@ -0,0 +1,11 @@
/* From uart401.c */
int probe_uart401 (struct address_info *hw_config, struct module *owner);
void unload_uart401 (struct address_info *hw_config);
irqreturn_t uart401intr (int irq, void *dev_id);
/* From mpu401.c */
int probe_mpu401(struct address_info *hw_config, struct resource *ports);
int attach_mpu401(struct address_info * hw_config, struct module *owner);
void unload_mpu401(struct address_info *hw_info);

413
sound/oss/msnd.c Normal file
View file

@ -0,0 +1,413 @@
/*********************************************************************
*
* msnd.c - Driver Base
*
* Turtle Beach MultiSound Sound Card Driver for Linux
*
* Copyright (C) 1998 Andrew Veliath
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
********************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
#include <asm/irq.h>
#include "msnd.h"
#define LOGNAME "msnd"
#define MSND_MAX_DEVS 4
static multisound_dev_t *devs[MSND_MAX_DEVS];
static int num_devs;
int msnd_register(multisound_dev_t *dev)
{
int i;
for (i = 0; i < MSND_MAX_DEVS; ++i)
if (devs[i] == NULL)
break;
if (i == MSND_MAX_DEVS)
return -ENOMEM;
devs[i] = dev;
++num_devs;
return 0;
}
void msnd_unregister(multisound_dev_t *dev)
{
int i;
for (i = 0; i < MSND_MAX_DEVS; ++i)
if (devs[i] == dev)
break;
if (i == MSND_MAX_DEVS) {
printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
return;
}
devs[i] = NULL;
--num_devs;
}
void msnd_init_queue(void __iomem *base, int start, int size)
{
writew(PCTODSP_BASED(start), base + JQS_wStart);
writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
writew(0, base + JQS_wHead);
writew(0, base + JQS_wTail);
}
void msnd_fifo_init(msnd_fifo *f)
{
f->data = NULL;
}
void msnd_fifo_free(msnd_fifo *f)
{
vfree(f->data);
f->data = NULL;
}
int msnd_fifo_alloc(msnd_fifo *f, size_t n)
{
msnd_fifo_free(f);
f->data = vmalloc(n);
f->n = n;
f->tail = 0;
f->head = 0;
f->len = 0;
if (!f->data)
return -ENOMEM;
return 0;
}
void msnd_fifo_make_empty(msnd_fifo *f)
{
f->len = f->tail = f->head = 0;
}
int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
{
int count = 0;
while ((count < len) && (f->len != f->n)) {
int nwritten;
if (f->head <= f->tail) {
nwritten = len - count;
if (nwritten > f->n - f->tail)
nwritten = f->n - f->tail;
}
else {
nwritten = f->head - f->tail;
if (nwritten > len - count)
nwritten = len - count;
}
memcpy_fromio(f->data + f->tail, buf, nwritten);
count += nwritten;
buf += nwritten;
f->len += nwritten;
f->tail += nwritten;
f->tail %= f->n;
}
return count;
}
int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
{
int count = 0;
while ((count < len) && (f->len != f->n)) {
int nwritten;
if (f->head <= f->tail) {
nwritten = len - count;
if (nwritten > f->n - f->tail)
nwritten = f->n - f->tail;
}
else {
nwritten = f->head - f->tail;
if (nwritten > len - count)
nwritten = len - count;
}
memcpy(f->data + f->tail, buf, nwritten);
count += nwritten;
buf += nwritten;
f->len += nwritten;
f->tail += nwritten;
f->tail %= f->n;
}
return count;
}
int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
{
int count = 0;
while ((count < len) && (f->len > 0)) {
int nread;
if (f->tail <= f->head) {
nread = len - count;
if (nread > f->n - f->head)
nread = f->n - f->head;
}
else {
nread = f->tail - f->head;
if (nread > len - count)
nread = len - count;
}
memcpy_toio(buf, f->data + f->head, nread);
count += nread;
buf += nread;
f->len -= nread;
f->head += nread;
f->head %= f->n;
}
return count;
}
int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
{
int count = 0;
while ((count < len) && (f->len > 0)) {
int nread;
if (f->tail <= f->head) {
nread = len - count;
if (nread > f->n - f->head)
nread = f->n - f->head;
}
else {
nread = f->tail - f->head;
if (nread > len - count)
nread = len - count;
}
memcpy(buf, f->data + f->head, nread);
count += nread;
buf += nread;
f->len -= nread;
f->head += nread;
f->head %= f->n;
}
return count;
}
static int msnd_wait_TXDE(multisound_dev_t *dev)
{
register unsigned int io = dev->io;
register int timeout = 1000;
while(timeout-- > 0)
if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
return 0;
return -EIO;
}
static int msnd_wait_HC0(multisound_dev_t *dev)
{
register unsigned int io = dev->io;
register int timeout = 1000;
while(timeout-- > 0)
if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
return 0;
return -EIO;
}
int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
{
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (msnd_wait_HC0(dev) == 0) {
msnd_outb(cmd, dev->io + HP_CVR);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
spin_unlock_irqrestore(&dev->lock, flags);
printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
return -EIO;
}
int msnd_send_word(multisound_dev_t *dev, unsigned char high,
unsigned char mid, unsigned char low)
{
register unsigned int io = dev->io;
if (msnd_wait_TXDE(dev) == 0) {
msnd_outb(high, io + HP_TXH);
msnd_outb(mid, io + HP_TXM);
msnd_outb(low, io + HP_TXL);
return 0;
}
printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
return -EIO;
}
int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
{
int i;
if (len % 3 != 0) {
printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");
return -EINVAL;
}
for (i = 0; i < len; i += 3)
if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
return -EIO;
msnd_inb(dev->io + HP_RXL);
msnd_inb(dev->io + HP_CVR);
return 0;
}
int msnd_enable_irq(multisound_dev_t *dev)
{
unsigned long flags;
if (dev->irq_ref++)
return 0;
printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
spin_lock_irqsave(&dev->lock, flags);
if (msnd_wait_TXDE(dev) == 0) {
msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
if (dev->type == msndClassic)
msnd_outb(dev->irqid, dev->io + HP_IRQM);
msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
enable_irq(dev->irq);
msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
spin_unlock_irqrestore(&dev->lock, flags);
printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
return -EIO;
}
int msnd_disable_irq(multisound_dev_t *dev)
{
unsigned long flags;
if (--dev->irq_ref > 0)
return 0;
if (dev->irq_ref < 0)
printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
spin_lock_irqsave(&dev->lock, flags);
if (msnd_wait_TXDE(dev) == 0) {
msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
if (dev->type == msndClassic)
msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
disable_irq(dev->irq);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
spin_unlock_irqrestore(&dev->lock, flags);
printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
return -EIO;
}
#ifndef LINUX20
EXPORT_SYMBOL(msnd_register);
EXPORT_SYMBOL(msnd_unregister);
EXPORT_SYMBOL(msnd_init_queue);
EXPORT_SYMBOL(msnd_fifo_init);
EXPORT_SYMBOL(msnd_fifo_free);
EXPORT_SYMBOL(msnd_fifo_alloc);
EXPORT_SYMBOL(msnd_fifo_make_empty);
EXPORT_SYMBOL(msnd_fifo_write_io);
EXPORT_SYMBOL(msnd_fifo_read_io);
EXPORT_SYMBOL(msnd_fifo_write);
EXPORT_SYMBOL(msnd_fifo_read);
EXPORT_SYMBOL(msnd_send_dsp_cmd);
EXPORT_SYMBOL(msnd_send_word);
EXPORT_SYMBOL(msnd_upload_host);
EXPORT_SYMBOL(msnd_enable_irq);
EXPORT_SYMBOL(msnd_disable_irq);
#endif
#ifdef MODULE
MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>");
MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base");
MODULE_LICENSE("GPL");
int init_module(void)
{
return 0;
}
void cleanup_module(void)
{
}
#endif

278
sound/oss/msnd.h Normal file
View file

@ -0,0 +1,278 @@
/*********************************************************************
*
* msnd.h
*
* Turtle Beach MultiSound Sound Card Driver for Linux
*
* Some parts of this header file were derived from the Turtle Beach
* MultiSound Driver Development Kit.
*
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
********************************************************************/
#ifndef __MSND_H
#define __MSND_H
#define VERSION "0.8.3.1"
#define DEFSAMPLERATE DSP_DEFAULT_SPEED
#define DEFSAMPLESIZE AFMT_U8
#define DEFCHANNELS 1
#define DEFFIFOSIZE 128
#define SNDCARD_MSND 38
#define SRAM_BANK_SIZE 0x8000
#define SRAM_CNTL_START 0x7F00
#define DSP_BASE_ADDR 0x4000
#define DSP_BANK_BASE 0x4000
#define HP_ICR 0x00
#define HP_CVR 0x01
#define HP_ISR 0x02
#define HP_IVR 0x03
#define HP_NU 0x04
#define HP_INFO 0x04
#define HP_TXH 0x05
#define HP_RXH 0x05
#define HP_TXM 0x06
#define HP_RXM 0x06
#define HP_TXL 0x07
#define HP_RXL 0x07
#define HP_ICR_DEF 0x00
#define HP_CVR_DEF 0x12
#define HP_ISR_DEF 0x06
#define HP_IVR_DEF 0x0f
#define HP_NU_DEF 0x00
#define HP_IRQM 0x09
#define HPR_BLRC 0x08
#define HPR_SPR1 0x09
#define HPR_SPR2 0x0A
#define HPR_TCL0 0x0B
#define HPR_TCL1 0x0C
#define HPR_TCL2 0x0D
#define HPR_TCL3 0x0E
#define HPR_TCL4 0x0F
#define HPICR_INIT 0x80
#define HPICR_HM1 0x40
#define HPICR_HM0 0x20
#define HPICR_HF1 0x10
#define HPICR_HF0 0x08
#define HPICR_TREQ 0x02
#define HPICR_RREQ 0x01
#define HPCVR_HC 0x80
#define HPISR_HREQ 0x80
#define HPISR_DMA 0x40
#define HPISR_HF3 0x10
#define HPISR_HF2 0x08
#define HPISR_TRDY 0x04
#define HPISR_TXDE 0x02
#define HPISR_RXDF 0x01
#define HPIO_290 0
#define HPIO_260 1
#define HPIO_250 2
#define HPIO_240 3
#define HPIO_230 4
#define HPIO_220 5
#define HPIO_210 6
#define HPIO_3E0 7
#define HPMEM_NONE 0
#define HPMEM_B000 1
#define HPMEM_C800 2
#define HPMEM_D000 3
#define HPMEM_D400 4
#define HPMEM_D800 5
#define HPMEM_E000 6
#define HPMEM_E800 7
#define HPIRQ_NONE 0
#define HPIRQ_5 1
#define HPIRQ_7 2
#define HPIRQ_9 3
#define HPIRQ_10 4
#define HPIRQ_11 5
#define HPIRQ_12 6
#define HPIRQ_15 7
#define HIMT_PLAY_DONE 0x00
#define HIMT_RECORD_DONE 0x01
#define HIMT_MIDI_EOS 0x02
#define HIMT_MIDI_OUT 0x03
#define HIMT_MIDI_IN_UCHAR 0x0E
#define HIMT_DSP 0x0F
#define HDEX_BASE 0x92
#define HDEX_PLAY_START (0 + HDEX_BASE)
#define HDEX_PLAY_STOP (1 + HDEX_BASE)
#define HDEX_PLAY_PAUSE (2 + HDEX_BASE)
#define HDEX_PLAY_RESUME (3 + HDEX_BASE)
#define HDEX_RECORD_START (4 + HDEX_BASE)
#define HDEX_RECORD_STOP (5 + HDEX_BASE)
#define HDEX_MIDI_IN_START (6 + HDEX_BASE)
#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE)
#define HDEX_MIDI_OUT_START (8 + HDEX_BASE)
#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE)
#define HDEX_AUX_REQ (10 + HDEX_BASE)
#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
#define LOWORD(l) ((WORD)(DWORD)(l))
#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
#define LOBYTE(w) ((BYTE)(w))
#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16)))
#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8)))
#define PCTODSP_OFFSET(w) (USHORT)((w)/2)
#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR)
#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2)
#ifdef SLOWIO
#define msnd_outb outb_p
#define msnd_inb inb_p
#else
#define msnd_outb outb
#define msnd_inb inb
#endif
/* JobQueueStruct */
#define JQS_wStart 0x00
#define JQS_wSize 0x02
#define JQS_wHead 0x04
#define JQS_wTail 0x06
#define JQS__size 0x08
/* DAQueueDataStruct */
#define DAQDS_wStart 0x00
#define DAQDS_wSize 0x02
#define DAQDS_wFormat 0x04
#define DAQDS_wSampleSize 0x06
#define DAQDS_wChannels 0x08
#define DAQDS_wSampleRate 0x0A
#define DAQDS_wIntMsg 0x0C
#define DAQDS_wFlags 0x0E
#define DAQDS__size 0x10
typedef u8 BYTE;
typedef u16 USHORT;
typedef u16 WORD;
typedef u32 DWORD;
typedef void __iomem * LPDAQD;
/* Generic FIFO */
typedef struct {
size_t n, len;
char *data;
int head, tail;
} msnd_fifo;
typedef struct multisound_dev {
/* Linux device info */
char *name;
int dsp_minor, mixer_minor;
int ext_midi_dev, hdr_midi_dev;
/* Hardware resources */
int io, numio;
int memid, irqid;
int irq, irq_ref;
unsigned char info;
void __iomem *base;
/* Motorola 56k DSP SMA */
void __iomem *SMA;
void __iomem *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ;
void __iomem *pwDSPQData, *pwMIDQData, *pwMODQData;
int dspq_data_buff, dspq_buff_size;
/* State variables */
enum { msndClassic, msndPinnacle } type;
fmode_t mode;
unsigned long flags;
#define F_RESETTING 0
#define F_HAVEDIGITAL 1
#define F_AUDIO_WRITE_INUSE 2
#define F_WRITING 3
#define F_WRITEBLOCK 4
#define F_WRITEFLUSH 5
#define F_AUDIO_READ_INUSE 6
#define F_READING 7
#define F_READBLOCK 8
#define F_EXT_MIDI_INUSE 9
#define F_HDR_MIDI_INUSE 10
#define F_DISABLE_WRITE_NDELAY 11
wait_queue_head_t writeblock;
wait_queue_head_t readblock;
wait_queue_head_t writeflush;
spinlock_t lock;
int nresets;
unsigned long recsrc;
int left_levels[32];
int right_levels[32];
int mixer_mod_count;
int calibrate_signal;
int play_sample_size, play_sample_rate, play_channels;
int play_ndelay;
int rec_sample_size, rec_sample_rate, rec_channels;
int rec_ndelay;
BYTE bCurrentMidiPatch;
/* Digital audio FIFOs */
msnd_fifo DAPF, DARF;
int fifosize;
int last_playbank, last_recbank;
/* MIDI in callback */
void (*midi_in_interrupt)(struct multisound_dev *);
} multisound_dev_t;
#ifndef mdelay
# define mdelay(a) udelay((a) * 1000)
#endif
int msnd_register(multisound_dev_t *dev);
void msnd_unregister(multisound_dev_t *dev);
void msnd_init_queue(void __iomem *, int start, int size);
void msnd_fifo_init(msnd_fifo *f);
void msnd_fifo_free(msnd_fifo *f);
int msnd_fifo_alloc(msnd_fifo *f, size_t n);
void msnd_fifo_make_empty(msnd_fifo *f);
int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len);
int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len);
int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len);
int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len);
int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd);
int msnd_send_word(multisound_dev_t *dev, unsigned char high,
unsigned char mid, unsigned char low);
int msnd_upload_host(multisound_dev_t *dev, char *bin, int len);
int msnd_enable_irq(multisound_dev_t *dev);
int msnd_disable_irq(multisound_dev_t *dev);
#endif /* __MSND_H */

3
sound/oss/msnd_classic.c Normal file
View file

@ -0,0 +1,3 @@
/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
#define MSND_CLASSIC
#include "msnd_pinnacle.c"

185
sound/oss/msnd_classic.h Normal file
View file

@ -0,0 +1,185 @@
/*********************************************************************
*
* msnd_classic.h
*
* Turtle Beach MultiSound Sound Card Driver for Linux
*
* Some parts of this header file were derived from the Turtle Beach
* MultiSound Driver Development Kit.
*
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
********************************************************************/
#ifndef __MSND_CLASSIC_H
#define __MSND_CLASSIC_H
#define DSP_NUMIO 0x10
#define HP_MEMM 0x08
#define HP_BITM 0x0E
#define HP_WAIT 0x0D
#define HP_DSPR 0x0A
#define HP_PROR 0x0B
#define HP_BLKS 0x0C
#define HPPRORESET_OFF 0
#define HPPRORESET_ON 1
#define HPDSPRESET_OFF 0
#define HPDSPRESET_ON 1
#define HPBLKSEL_0 0
#define HPBLKSEL_1 1
#define HPWAITSTATE_0 0
#define HPWAITSTATE_1 1
#define HPBITMODE_16 0
#define HPBITMODE_8 1
#define HIDSP_INT_PLAY_UNDER 0x00
#define HIDSP_INT_RECORD_OVER 0x01
#define HIDSP_INPUT_CLIPPING 0x02
#define HIDSP_MIDI_IN_OVER 0x10
#define HIDSP_MIDI_OVERRUN_ERR 0x13
#define HDEXAR_CLEAR_PEAKS 1
#define HDEXAR_IN_SET_POTS 2
#define HDEXAR_AUX_SET_POTS 3
#define HDEXAR_CAL_A_TO_D 4
#define HDEXAR_RD_EXT_DSP_BITS 5
#define TIME_PRO_RESET_DONE 0x028A
#define TIME_PRO_SYSEX 0x0040
#define TIME_PRO_RESET 0x0032
#define AGND 0x01
#define SIGNAL 0x02
#define EXT_DSP_BIT_DCAL 0x0001
#define EXT_DSP_BIT_MIDI_CON 0x0002
#define BUFFSIZE 0x8000
#define HOSTQ_SIZE 0x40
#define SRAM_CNTL_START 0x7F00
#define SMA_STRUCT_START 0x7F40
#define DAP_BUFF_SIZE 0x2400
#define DAR_BUFF_SIZE 0x2000
#define DAPQ_STRUCT_SIZE 0x10
#define DARQ_STRUCT_SIZE 0x10
#define DAPQ_BUFF_SIZE (3 * 0x10)
#define DARQ_BUFF_SIZE (3 * 0x10)
#define MODQ_BUFF_SIZE 0x400
#define MIDQ_BUFF_SIZE 0x200
#define DSPQ_BUFF_SIZE 0x40
#define DAPQ_DATA_BUFF 0x6C00
#define DARQ_DATA_BUFF 0x6C30
#define MODQ_DATA_BUFF 0x6C60
#define MIDQ_DATA_BUFF 0x7060
#define DSPQ_DATA_BUFF 0x7260
#define DAPQ_OFFSET SRAM_CNTL_START
#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
#define MOP_SYNTH 0x10
#define MOP_EXTOUT 0x32
#define MOP_EXTTHRU 0x02
#define MOP_OUTMASK 0x01
#define MIP_EXTIN 0x01
#define MIP_SYNTH 0x00
#define MIP_INMASK 0x32
/* Classic SMA Common Data */
#define SMA_wCurrPlayBytes 0x0000
#define SMA_wCurrRecordBytes 0x0002
#define SMA_wCurrPlayVolLeft 0x0004
#define SMA_wCurrPlayVolRight 0x0006
#define SMA_wCurrInVolLeft 0x0008
#define SMA_wCurrInVolRight 0x000a
#define SMA_wUser_3 0x000c
#define SMA_wUser_4 0x000e
#define SMA_dwUser_5 0x0010
#define SMA_dwUser_6 0x0014
#define SMA_wUser_7 0x0018
#define SMA_wReserved_A 0x001a
#define SMA_wReserved_B 0x001c
#define SMA_wReserved_C 0x001e
#define SMA_wReserved_D 0x0020
#define SMA_wReserved_E 0x0022
#define SMA_wReserved_F 0x0024
#define SMA_wReserved_G 0x0026
#define SMA_wReserved_H 0x0028
#define SMA_wCurrDSPStatusFlags 0x002a
#define SMA_wCurrHostStatusFlags 0x002c
#define SMA_wCurrInputTagBits 0x002e
#define SMA_wCurrLeftPeak 0x0030
#define SMA_wCurrRightPeak 0x0032
#define SMA_wExtDSPbits 0x0034
#define SMA_bExtHostbits 0x0036
#define SMA_bBoardLevel 0x0037
#define SMA_bInPotPosRight 0x0038
#define SMA_bInPotPosLeft 0x0039
#define SMA_bAuxPotPosRight 0x003a
#define SMA_bAuxPotPosLeft 0x003b
#define SMA_wCurrMastVolLeft 0x003c
#define SMA_wCurrMastVolRight 0x003e
#define SMA_bUser_12 0x0040
#define SMA_bUser_13 0x0041
#define SMA_wUser_14 0x0042
#define SMA_wUser_15 0x0044
#define SMA_wCalFreqAtoD 0x0046
#define SMA_wUser_16 0x0048
#define SMA_wUser_17 0x004a
#define SMA__size 0x004c
#ifdef HAVE_DSPCODEH
# include "msndperm.c"
# include "msndinit.c"
# define PERMCODE msndperm
# define INITCODE msndinit
# define PERMCODESIZE sizeof(msndperm)
# define INITCODESIZE sizeof(msndinit)
#else
# ifndef CONFIG_MSNDCLAS_INIT_FILE
# define CONFIG_MSNDCLAS_INIT_FILE \
"/etc/sound/msndinit.bin"
# endif
# ifndef CONFIG_MSNDCLAS_PERM_FILE
# define CONFIG_MSNDCLAS_PERM_FILE \
"/etc/sound/msndperm.bin"
# endif
# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE
# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE
# define PERMCODE dspini
# define INITCODE permini
# define PERMCODESIZE sizeof_dspini
# define INITCODESIZE sizeof_permini
#endif
#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)"
#endif /* __MSND_CLASSIC_H */

1940
sound/oss/msnd_pinnacle.c Normal file

File diff suppressed because it is too large Load diff

246
sound/oss/msnd_pinnacle.h Normal file
View file

@ -0,0 +1,246 @@
/*********************************************************************
*
* msnd_pinnacle.h
*
* Turtle Beach MultiSound Sound Card Driver for Linux
*
* Some parts of this header file were derived from the Turtle Beach
* MultiSound Driver Development Kit.
*
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
********************************************************************/
#ifndef __MSND_PINNACLE_H
#define __MSND_PINNACLE_H
#define DSP_NUMIO 0x08
#define IREG_LOGDEVICE 0x07
#define IREG_ACTIVATE 0x30
#define LD_ACTIVATE 0x01
#define LD_DISACTIVATE 0x00
#define IREG_EECONTROL 0x3F
#define IREG_MEMBASEHI 0x40
#define IREG_MEMBASELO 0x41
#define IREG_MEMCONTROL 0x42
#define IREG_MEMRANGEHI 0x43
#define IREG_MEMRANGELO 0x44
#define MEMTYPE_8BIT 0x00
#define MEMTYPE_16BIT 0x02
#define MEMTYPE_RANGE 0x00
#define MEMTYPE_HIADDR 0x01
#define IREG_IO0_BASEHI 0x60
#define IREG_IO0_BASELO 0x61
#define IREG_IO1_BASEHI 0x62
#define IREG_IO1_BASELO 0x63
#define IREG_IRQ_NUMBER 0x70
#define IREG_IRQ_TYPE 0x71
#define IRQTYPE_HIGH 0x02
#define IRQTYPE_LOW 0x00
#define IRQTYPE_LEVEL 0x01
#define IRQTYPE_EDGE 0x00
#define HP_DSPR 0x04
#define HP_BLKS 0x04
#define HPDSPRESET_OFF 2
#define HPDSPRESET_ON 0
#define HPBLKSEL_0 2
#define HPBLKSEL_1 3
#define HIMT_DAT_OFF 0x03
#define HIDSP_PLAY_UNDER 0x00
#define HIDSP_INT_PLAY_UNDER 0x01
#define HIDSP_SSI_TX_UNDER 0x02
#define HIDSP_RECQ_OVERFLOW 0x08
#define HIDSP_INT_RECORD_OVER 0x09
#define HIDSP_SSI_RX_OVERFLOW 0x0a
#define HIDSP_MIDI_IN_OVER 0x10
#define HIDSP_MIDI_FRAME_ERR 0x11
#define HIDSP_MIDI_PARITY_ERR 0x12
#define HIDSP_MIDI_OVERRUN_ERR 0x13
#define HIDSP_INPUT_CLIPPING 0x20
#define HIDSP_MIX_CLIPPING 0x30
#define HIDSP_DAT_IN_OFF 0x21
#define HDEXAR_SET_ANA_IN 0
#define HDEXAR_CLEAR_PEAKS 1
#define HDEXAR_IN_SET_POTS 2
#define HDEXAR_AUX_SET_POTS 3
#define HDEXAR_CAL_A_TO_D 4
#define HDEXAR_RD_EXT_DSP_BITS 5
#define HDEXAR_SET_SYNTH_IN 4
#define HDEXAR_READ_DAT_IN 5
#define HDEXAR_MIC_SET_POTS 6
#define HDEXAR_SET_DAT_IN 7
#define HDEXAR_SET_SYNTH_48 8
#define HDEXAR_SET_SYNTH_44 9
#define TIME_PRO_RESET_DONE 0x028A
#define TIME_PRO_SYSEX 0x001E
#define TIME_PRO_RESET 0x0032
#define AGND 0x01
#define SIGNAL 0x02
#define EXT_DSP_BIT_DCAL 0x0001
#define EXT_DSP_BIT_MIDI_CON 0x0002
#define BUFFSIZE 0x8000
#define HOSTQ_SIZE 0x40
#define SRAM_CNTL_START 0x7F00
#define SMA_STRUCT_START 0x7F40
#define DAP_BUFF_SIZE 0x2400
#define DAR_BUFF_SIZE 0x2000
#define DAPQ_STRUCT_SIZE 0x10
#define DARQ_STRUCT_SIZE 0x10
#define DAPQ_BUFF_SIZE (3 * 0x10)
#define DARQ_BUFF_SIZE (3 * 0x10)
#define MODQ_BUFF_SIZE 0x400
#define MIDQ_BUFF_SIZE 0x800
#define DSPQ_BUFF_SIZE 0x5A0
#define DAPQ_DATA_BUFF 0x6C00
#define DARQ_DATA_BUFF 0x6C30
#define MODQ_DATA_BUFF 0x6C60
#define MIDQ_DATA_BUFF 0x7060
#define DSPQ_DATA_BUFF 0x7860
#define DAPQ_OFFSET SRAM_CNTL_START
#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
#define MOP_WAVEHDR 0
#define MOP_EXTOUT 1
#define MOP_HWINIT 0xfe
#define MOP_NONE 0xff
#define MOP_MAX 1
#define MIP_EXTIN 0
#define MIP_WAVEHDR 1
#define MIP_HWINIT 0xfe
#define MIP_MAX 1
/* Pinnacle/Fiji SMA Common Data */
#define SMA_wCurrPlayBytes 0x0000
#define SMA_wCurrRecordBytes 0x0002
#define SMA_wCurrPlayVolLeft 0x0004
#define SMA_wCurrPlayVolRight 0x0006
#define SMA_wCurrInVolLeft 0x0008
#define SMA_wCurrInVolRight 0x000a
#define SMA_wCurrMHdrVolLeft 0x000c
#define SMA_wCurrMHdrVolRight 0x000e
#define SMA_dwCurrPlayPitch 0x0010
#define SMA_dwCurrPlayRate 0x0014
#define SMA_wCurrMIDIIOPatch 0x0018
#define SMA_wCurrPlayFormat 0x001a
#define SMA_wCurrPlaySampleSize 0x001c
#define SMA_wCurrPlayChannels 0x001e
#define SMA_wCurrPlaySampleRate 0x0020
#define SMA_wCurrRecordFormat 0x0022
#define SMA_wCurrRecordSampleSize 0x0024
#define SMA_wCurrRecordChannels 0x0026
#define SMA_wCurrRecordSampleRate 0x0028
#define SMA_wCurrDSPStatusFlags 0x002a
#define SMA_wCurrHostStatusFlags 0x002c
#define SMA_wCurrInputTagBits 0x002e
#define SMA_wCurrLeftPeak 0x0030
#define SMA_wCurrRightPeak 0x0032
#define SMA_bMicPotPosLeft 0x0034
#define SMA_bMicPotPosRight 0x0035
#define SMA_bMicPotMaxLeft 0x0036
#define SMA_bMicPotMaxRight 0x0037
#define SMA_bInPotPosLeft 0x0038
#define SMA_bInPotPosRight 0x0039
#define SMA_bAuxPotPosLeft 0x003a
#define SMA_bAuxPotPosRight 0x003b
#define SMA_bInPotMaxLeft 0x003c
#define SMA_bInPotMaxRight 0x003d
#define SMA_bAuxPotMaxLeft 0x003e
#define SMA_bAuxPotMaxRight 0x003f
#define SMA_bInPotMaxMethod 0x0040
#define SMA_bAuxPotMaxMethod 0x0041
#define SMA_wCurrMastVolLeft 0x0042
#define SMA_wCurrMastVolRight 0x0044
#define SMA_wCalFreqAtoD 0x0046
#define SMA_wCurrAuxVolLeft 0x0048
#define SMA_wCurrAuxVolRight 0x004a
#define SMA_wCurrPlay1VolLeft 0x004c
#define SMA_wCurrPlay1VolRight 0x004e
#define SMA_wCurrPlay2VolLeft 0x0050
#define SMA_wCurrPlay2VolRight 0x0052
#define SMA_wCurrPlay3VolLeft 0x0054
#define SMA_wCurrPlay3VolRight 0x0056
#define SMA_wCurrPlay4VolLeft 0x0058
#define SMA_wCurrPlay4VolRight 0x005a
#define SMA_wCurrPlay1PeakLeft 0x005c
#define SMA_wCurrPlay1PeakRight 0x005e
#define SMA_wCurrPlay2PeakLeft 0x0060
#define SMA_wCurrPlay2PeakRight 0x0062
#define SMA_wCurrPlay3PeakLeft 0x0064
#define SMA_wCurrPlay3PeakRight 0x0066
#define SMA_wCurrPlay4PeakLeft 0x0068
#define SMA_wCurrPlay4PeakRight 0x006a
#define SMA_wCurrPlayPeakLeft 0x006c
#define SMA_wCurrPlayPeakRight 0x006e
#define SMA_wCurrDATSR 0x0070
#define SMA_wCurrDATRXCHNL 0x0072
#define SMA_wCurrDATTXCHNL 0x0074
#define SMA_wCurrDATRXRate 0x0076
#define SMA_dwDSPPlayCount 0x0078
#define SMA__size 0x007c
#ifdef HAVE_DSPCODEH
# include "pndsperm.c"
# include "pndspini.c"
# define PERMCODE pndsperm
# define INITCODE pndspini
# define PERMCODESIZE sizeof(pndsperm)
# define INITCODESIZE sizeof(pndspini)
#else
# ifndef CONFIG_MSNDPIN_INIT_FILE
# define CONFIG_MSNDPIN_INIT_FILE \
"/etc/sound/pndspini.bin"
# endif
# ifndef CONFIG_MSNDPIN_PERM_FILE
# define CONFIG_MSNDPIN_PERM_FILE \
"/etc/sound/pndsperm.bin"
# endif
# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE
# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE
# define PERMCODE dspini
# define INITCODE permini
# define PERMCODESIZE sizeof_dspini
# define INITCODESIZE sizeof_permini
#endif
#define LONGNAME "MultiSound (Pinnacle/Fiji)"
#endif /* __MSND_PINNACLE_H */

1255
sound/oss/opl3.c Normal file

File diff suppressed because it is too large Load diff

246
sound/oss/opl3_hw.h Normal file
View file

@ -0,0 +1,246 @@
/*
* opl3_hw.h - Definitions of the OPL-3 registers
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
*
* 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 exceptions 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 OPL4_ENABLE 0x02
#define KBD_SPLIT_REGISTER 0x08 /* Left side */
#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
#define KEYBOARD_SPLIT 0x40
#define PERCOSSION_REGISTER 0xbd /* Left side only */
#define TREMOLO_DEPTH 0x80
#define VIBRATO_DEPTH 0x40
#define PERCOSSION_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 halves (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 halves.
*
* 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 pv_map[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}}
};
/*
* DMA buffer calls
*/

45
sound/oss/os.h Normal file
View file

@ -0,0 +1,45 @@
#define ALLOW_SELECT
#undef NO_INLINE_ASM
#define SHORT_BANNERS
#define MANUAL_PNP
#undef DO_TIMINGS
#include <linux/module.h>
#ifdef __KERNEL__
#include <linux/string.h>
#include <linux/fs.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/param.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <asm/page.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/pci.h>
#endif
#include <linux/soundcard.h>
#define FALSE 0
#define TRUE 1
extern int sound_alloc_dma(int chn, char *deviceID);
extern int sound_open_dma(int chn, char *deviceID);
extern void sound_free_dma(int chn);
extern void sound_close_dma(int chn);
extern void reprogram_timer(void);
#define USE_AUTOINIT_DMA
extern void *sound_mem_blocks[1024];
extern int sound_nblocks;
#undef PSEUDO_DMA_AUTOINIT
#define ALLOW_BUFFER_MAPPING
extern const struct file_operations oss_sound_fops;

20
sound/oss/pas2.h Normal file
View file

@ -0,0 +1,20 @@
/* From pas_card.c */
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);
void pas_pcm_init(struct address_info *hw_config);
/* From pas_mixer.c */
int pas_init_mixer(void);
/* From pas_midi.c */
void pas_midi_init(void);
void pas_midi_interrupt(void);
/* From pas2_mixer.c*/
void mix_write(unsigned char data, int ioaddr);

458
sound/oss/pas2_card.c Normal file
View file

@ -0,0 +1,458 @@
/*
* sound/oss/pas2_card.c
*
* Detection routine for the Pro Audio Spectrum cards.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include "sound_config.h"
#include "pas2.h"
#include "sb.h"
static unsigned char dma_bits[] = {
4, 1, 2, 3, 0, 5, 6, 7
};
static unsigned char irq_bits[] = {
0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11
};
static unsigned char sb_irq_bits[] = {
0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20,
0x00, 0x08, 0x28, 0x30, 0x38, 0, 0
};
static unsigned char sb_dma_bits[] = {
0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0
};
/*
* The Address Translation code is used to convert I/O register addresses to
* be relative to the given base -register
*/
int pas_translate_code = 0;
static int pas_intr_mask;
static int pas_irq;
static int pas_sb_base;
DEFINE_SPINLOCK(pas_lock);
#ifndef CONFIG_PAS_JOYSTICK
static bool joystick;
#else
static bool joystick = 1;
#endif
#ifdef SYMPHONY_PAS
static bool symphony = 1;
#else
static bool symphony;
#endif
#ifdef BROKEN_BUS_CLOCK
static bool broken_bus_clock = 1;
#else
static bool broken_bus_clock;
#endif
static struct address_info cfg;
static struct address_info cfg2;
char pas_model = 0;
static char *pas_model_names[] = {
"",
"Pro AudioSpectrum+",
"CDPC",
"Pro AudioSpectrum 16",
"Pro AudioSpectrum 16D"
};
/*
* pas_read() and pas_write() are equivalents of inb and outb
* These routines perform the I/O address translation required
* to support other than the default base address
*/
unsigned char pas_read(int ioaddr)
{
return inb(ioaddr + pas_translate_code);
}
void pas_write(unsigned char data, int ioaddr)
{
outb((data), ioaddr + pas_translate_code);
}
/******************* Begin of the Interrupt Handler ********************/
static irqreturn_t pasintr(int irq, void *dev_id)
{
int status;
status = pas_read(0x0B89);
pas_write(status, 0x0B89); /* Clear interrupt */
if (status & 0x08)
{
pas_pcm_interrupt(status, 1);
status &= ~0x08;
}
if (status & 0x10)
{
pas_midi_interrupt();
status &= ~0x10;
}
return IRQ_HANDLED;
}
int pas_set_intr(int mask)
{
if (!mask)
return 0;
pas_intr_mask |= mask;
pas_write(pas_intr_mask, 0x0B8B);
return 0;
}
int pas_remove_intr(int mask)
{
if (!mask)
return 0;
pas_intr_mask &= ~mask;
pas_write(pas_intr_mask, 0x0B8B);
return 0;
}
/******************* End of the Interrupt handler **********************/
/******************* Begin of the Initialization Code ******************/
static int __init config_pas_hw(struct address_info *hw_config)
{
char ok = 1;
unsigned int_ptrs; /* scsi/sound interrupt pointers */
pas_irq = hw_config->irq;
pas_write(0x00, 0x0B8B);
pas_write(0x36, 0x138B);
pas_write(0x36, 0x1388);
pas_write(0, 0x1388);
pas_write(0x74, 0x138B);
pas_write(0x74, 0x1389);
pas_write(0, 0x1389);
pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
pas_write(0x01 | 0x02 | 0x04 | 0x10 /*
* |
* 0x80
*/ , 0xB88);
pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388);
if (pas_irq < 0 || pas_irq > 15)
{
printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
hw_config->irq=-1;
ok = 0;
}
else
{
int_ptrs = pas_read(0xF38A);
int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq];
pas_write(int_ptrs, 0xF38A);
if (!irq_bits[pas_irq])
{
printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
hw_config->irq=-1;
ok = 0;
}
else
{
if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) {
printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq);
hw_config->irq=-1;
ok = 0;
}
}
}
if (hw_config->dma < 0 || hw_config->dma > 7)
{
printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
hw_config->dma=-1;
ok = 0;
}
else
{
pas_write(dma_bits[hw_config->dma], 0xF389);
if (!dma_bits[hw_config->dma])
{
printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
hw_config->dma=-1;
ok = 0;
}
else
{
if (sound_alloc_dma(hw_config->dma, "PAS16"))
{
printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
hw_config->dma=-1;
ok = 0;
}
}
}
/*
* This fixes the timing problems of the PAS due to the Symphony chipset
* as per Media Vision. Only define this if your PAS doesn't work correctly.
*/
if(symphony)
{
outb((0x05), 0xa8);
outb((0x60), 0xa9);
}
if(broken_bus_clock)
pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
else
/*
* pas_write(0x01, 0x8388);
*/
pas_write(0x01 | 0x10 | 0x20, 0x8388);
pas_write(0x18, 0x838A); /* ??? */
pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */
pas_write(8, 0xBF8A);
mix_write(0x80 | 5, 0x078B);
mix_write(5, 0x078B);
{
struct address_info *sb_config;
sb_config = &cfg2;
if (sb_config->io_base)
{
unsigned char irq_dma;
/*
* Turn on Sound Blaster compatibility
* bit 1 = SB emulation
* bit 0 = MPU401 emulation (CDPC only :-( )
*/
pas_write(0x02, 0xF788);
/*
* "Emulation address"
*/
pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
pas_sb_base = sb_config->io_base;
if (!sb_dma_bits[sb_config->dma])
printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
if (!sb_irq_bits[sb_config->irq])
printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
irq_dma = sb_dma_bits[sb_config->dma] |
sb_irq_bits[sb_config->irq];
pas_write(irq_dma, 0xFB8A);
}
else
pas_write(0x00, 0xF788);
}
if (!ok)
printk(KERN_WARNING "PAS16: Driver not enabled\n");
return ok;
}
static int __init 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), 0x9A01); /* Activate first board */
outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */
pas_translate_code = hw_config->io_base - 0x388;
pas_write(1, 0xBF88); /* Select one wait states */
board_id = pas_read(0x0B8B);
if (board_id == 0xff)
return 0;
/*
* We probably have a PAS-series board, now check for a PAS16-series board
* by trying to change the board revision bits. PAS16-series hardware won't
* let you do this - the bits are read-only.
*/
foo = board_id ^ 0xe0;
pas_write(foo, 0x0B8B);
foo = pas_read(0x0B8B);
pas_write(board_id, 0x0B8B);
if (board_id != foo)
return 0;
pas_model = pas_read(0xFF88);
return pas_model;
}
static void __init attach_pas_card(struct address_info *hw_config)
{
pas_irq = hw_config->irq;
if (detect_pas_hw(hw_config))
{
if ((pas_model = pas_read(0xFF88)))
{
char temp[100];
if (pas_model < 0 ||
pas_model >= ARRAY_SIZE(pas_model_names)) {
printk(KERN_ERR "pas2 unrecognized model.\n");
return;
}
sprintf(temp,
"%s rev %d", pas_model_names[(int) pas_model],
pas_read(0x2789));
conf_printf(temp, hw_config);
}
if (config_pas_hw(hw_config))
{
pas_pcm_init(hw_config);
pas_midi_init();
pas_init_mixer();
}
}
}
static inline int __init probe_pas(struct address_info *hw_config)
{
return detect_pas_hw(hw_config);
}
static void __exit unload_pas(struct address_info *hw_config)
{
extern int pas_audiodev;
extern int pas2_mididev;
if (hw_config->dma>0)
sound_free_dma(hw_config->dma);
if (hw_config->irq>0)
free_irq(hw_config->irq, hw_config);
if(pas_audiodev!=-1)
sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
if(pas2_mididev!=-1)
sound_unload_mididev(pas2_mididev);
if(pas_audiodev!=-1)
sound_unload_audiodev(pas_audiodev);
}
static int __initdata io = -1;
static int __initdata irq = -1;
static int __initdata dma = -1;
static int __initdata dma16 = -1; /* Set this for modules that need it */
static int __initdata sb_io = 0;
static int __initdata sb_irq = -1;
static int __initdata sb_dma = -1;
static int __initdata sb_dma16 = -1;
module_param(io, int, 0);
module_param(irq, int, 0);
module_param(dma, int, 0);
module_param(dma16, int, 0);
module_param(sb_io, int, 0);
module_param(sb_irq, int, 0);
module_param(sb_dma, int, 0);
module_param(sb_dma16, int, 0);
module_param(joystick, bool, 0);
module_param(symphony, bool, 0);
module_param(broken_bus_clock, bool, 0);
MODULE_LICENSE("GPL");
static int __init init_pas2(void)
{
printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n");
cfg.io_base = io;
cfg.irq = irq;
cfg.dma = dma;
cfg.dma2 = dma16;
cfg2.io_base = sb_io;
cfg2.irq = sb_irq;
cfg2.dma = sb_dma;
cfg2.dma2 = sb_dma16;
if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
return -EINVAL;
}
if (!probe_pas(&cfg))
return -ENODEV;
attach_pas_card(&cfg);
return 0;
}
static void __exit cleanup_pas2(void)
{
unload_pas(&cfg);
}
module_init(init_pas2);
module_exit(cleanup_pas2);
#ifndef MODULE
static int __init setup_pas2(char *str)
{
/* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */
int ints[9];
str = get_options(str, ARRAY_SIZE(ints), ints);
io = ints[1];
irq = ints[2];
dma = ints[3];
dma16 = ints[4];
sb_io = ints[5];
sb_irq = ints[6];
sb_dma = ints[7];
sb_dma16 = ints[8];
return 1;
}
__setup("pas2=", setup_pas2);
#endif

262
sound/oss/pas2_midi.c Normal file
View file

@ -0,0 +1,262 @@
/*
* sound/oss/pas2_midi.c
*
* The low level driver for the PAS Midi Interface.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
* Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer()
*/
#include <linux/init.h>
#include <linux/spinlock.h>
#include "sound_config.h"
#include "pas2.h"
extern spinlock_t pas_lock;
static int midi_busy, input_opened;
static int my_dev;
int pas2_mididev=-1;
static unsigned char tmp_queue[256];
static volatile int qlen;
static volatile unsigned char qhead, qtail;
static void (*midi_input_intr) (int dev, unsigned char data);
static int pas_midi_open(int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
int err;
unsigned long flags;
unsigned char ctrl;
if (midi_busy)
return -EBUSY;
/*
* Reset input and output FIFO pointers
*/
pas_write(0x20 | 0x40,
0x178b);
spin_lock_irqsave(&pas_lock, flags);
if ((err = pas_set_intr(0x10)) < 0)
{
spin_unlock_irqrestore(&pas_lock, flags);
return err;
}
/*
* Enable input available and output FIFO empty interrupts
*/
ctrl = 0;
input_opened = 0;
midi_input_intr = input;
if (mode == OPEN_READ || mode == OPEN_READWRITE)
{
ctrl |= 0x04; /* Enable input */
input_opened = 1;
}
if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
{
ctrl |= 0x08 | 0x10; /* Enable output */
}
pas_write(ctrl, 0x178b);
/*
* Acknowledge any pending interrupts
*/
pas_write(0xff, 0x1B88);
spin_unlock_irqrestore(&pas_lock, flags);
midi_busy = 1;
qlen = qhead = qtail = 0;
return 0;
}
static void pas_midi_close(int dev)
{
/*
* Reset FIFO pointers, disable intrs
*/
pas_write(0x20 | 0x40, 0x178b);
pas_remove_intr(0x10);
midi_busy = 0;
}
static int dump_to_midi(unsigned char midi_byte)
{
int fifo_space, x;
fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
/*
* The MIDI FIFO space register and it's documentation is nonunderstandable.
* There seem to be no way to differentiate between buffer full and buffer
* empty situations. For this reason we don't never write the buffer
* completely full. In this way we can assume that 0 (or is it 15)
* means that the buffer is empty.
*/
if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */
return 0; /* Ask upper layers to retry after some time */
pas_write(midi_byte, 0x178A);
return 1;
}
static int pas_midi_out(int dev, unsigned char midi_byte)
{
unsigned long flags;
/*
* Drain the local queue first
*/
spin_lock_irqsave(&pas_lock, flags);
while (qlen && dump_to_midi(tmp_queue[qhead]))
{
qlen--;
qhead++;
}
spin_unlock_irqrestore(&pas_lock, flags);
/*
* Output the byte if the local queue is empty.
*/
if (!qlen)
if (dump_to_midi(midi_byte))
return 1;
/*
* Put to the local queue
*/
if (qlen >= 256)
return 0; /* Local queue full */
spin_lock_irqsave(&pas_lock, flags);
tmp_queue[qtail] = midi_byte;
qlen++;
qtail++;
spin_unlock_irqrestore(&pas_lock, flags);
return 1;
}
static int pas_midi_start_read(int dev)
{
return 0;
}
static int pas_midi_end_read(int dev)
{
return 0;
}
static void pas_midi_kick(int dev)
{
}
static int pas_buffer_status(int dev)
{
return qlen;
}
#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static struct midi_operations pas_midi_operations =
{
.owner = THIS_MODULE,
.info = {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
.converter = &std_midi_synth,
.in_info = {0},
.open = pas_midi_open,
.close = pas_midi_close,
.outputc = pas_midi_out,
.start_read = pas_midi_start_read,
.end_read = pas_midi_end_read,
.kick = pas_midi_kick,
.buffer_status = pas_buffer_status,
};
void __init pas_midi_init(void)
{
int dev = sound_alloc_mididev();
if (dev == -1)
{
printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
return;
}
std_midi_synth.midi_dev = my_dev = dev;
midi_devs[dev] = &pas_midi_operations;
pas2_mididev = dev;
sequencer_init();
}
void pas_midi_interrupt(void)
{
unsigned char stat;
int i, incount;
stat = pas_read(0x1B88);
if (stat & 0x04) /* Input data available */
{
incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */
if (!incount)
incount = 16;
for (i = 0; i < incount; i++)
if (input_opened)
{
midi_input_intr(my_dev, pas_read(0x178A));
} else
pas_read(0x178A); /* Flush */
}
if (stat & (0x08 | 0x10))
{
spin_lock(&pas_lock);/* called in irq context */
while (qlen && dump_to_midi(tmp_queue[qhead]))
{
qlen--;
qhead++;
}
spin_unlock(&pas_lock);
}
if (stat & 0x40)
{
printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
}
pas_write(stat, 0x1B88); /* Acknowledge interrupts */
}

327
sound/oss/pas2_mixer.c Normal file
View file

@ -0,0 +1,327 @@
/*
* sound/oss/pas2_mixer.c
*
* Mixer routines for the Pro Audio Spectrum cards.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer()
*/
#include <linux/init.h>
#include "sound_config.h"
#include "pas2.h"
extern int pas_translate_code;
extern char pas_model;
extern int *pas_osp;
extern int pas_audiodev;
static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */
static int mode_control;
#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)
static int *levels;
static int default_levels[32] =
{
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 */
};
void
mix_write(unsigned char data, int ioaddr)
{
/*
* 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 need to do this because it isn't timing problem but chip access
* sequence problem.
*/
if (pas_model == 4)
{
outw(data | (data << 8), (ioaddr + pas_translate_code) - 1);
outb((0x80), 0);
} else
pas_write(data, ioaddr);
}
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;
if (bits & 0x10)
{
left |= mixer;
right |= mixer;
}
if (bits == 0x03 || bits == 0x04)
{
mix_write(0x80 | bits, 0x078B);
mix_write(left, 0x078B);
right_vol = left_vol;
} else
{
mix_write(0x80 | 0x20 | bits, 0x078B);
mix_write(left, 0x078B);
mix_write(0x80 | 0x40 | bits, 0x078B);
mix_write(right, 0x078B);
}
return (left_vol | (right_vol << 8));
}
static void
set_mode(int new_mode)
{
mix_write(0x80 | 0x05, 0x078B);
mix_write(new_mode, 0x078B);
mode_control = new_mode;
}
static int
pas_mixer_set(int whichDev, unsigned int level)
{
int left, right, devmask, changed, i, mixer = 0;
left = level & 0x7f;
right = (level & 0x7f00) >> 8;
if (whichDev < SOUND_MIXER_NRDEVICES) {
if ((1 << whichDev) & rec_devices)
mixer = 0x20;
else
mixer = 0x00;
}
switch (whichDev)
{
case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
levels[whichDev] = mixer_output(right, left, 63, 0x01, 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, 0x03, 0);
break;
case SOUND_MIXER_TREBLE: /* Treble (0-12) */
levels[whichDev] = mixer_output(right, left, 12, 0x04, 0);
break;
case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer);
break;
case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer);
break;
case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer);
break;
case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer);
break;
case SOUND_MIXER_LINE: /* External line (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer);
break;
case SOUND_MIXER_CD: /* CD (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer);
break;
case SOUND_MIXER_MIC: /* External microphone (0-31) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer);
break;
case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */
levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01,
0x00);
break;
case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
levels[whichDev] = mixer_output(right, left, 15, 0x02, 0);
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 -EINVAL;
}
return (levels[whichDev]);
}
/*****/
static void
pas_mixer_reset(void)
{
int foo;
for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
pas_mixer_set(foo, levels[foo]);
set_mode(0x04 | 0x01);
}
static int pas_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
{
int level,v ;
int __user *p = (int __user *)arg;
if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
if (get_user(level, p))
return -EFAULT;
if (level == -1) /* Return current settings */
level = (mode_control & 0x04);
else {
mode_control &= ~0x04;
if (level)
mode_control |= 0x04;
set_mode(mode_control);
}
level = !!level;
return put_user(level, p);
}
if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
if (get_user(level, p))
return -EFAULT;
if (level == -1) { /* Return current settings */
if (!(mode_control & 0x03))
level = 0;
else
level = ((mode_control & 0x03) + 1) * 20;
} else {
int i = 0;
level &= 0x7f;
if (level)
i = (level / 20) - 1;
mode_control &= ~0x03;
mode_control |= i & 0x03;
set_mode(mode_control);
if (i)
i = (i + 1) * 20;
level = i;
}
return put_user(level, p);
}
if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
if (get_user(level, p))
return -EFAULT;
if (level == -1) /* Return current settings */
level = !(pas_read(0x0B8A) & 0x20);
else {
if (level)
pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
else
pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A);
level = !(pas_read(0x0B8A) & 0x20);
}
return put_user(level, p);
}
if (((cmd >> 8) & 0xff) == 'M') {
if (get_user(v, p))
return -EFAULT;
if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
v = pas_mixer_set(cmd & 0xff, v);
} else {
switch (cmd & 0xff) {
case SOUND_MIXER_RECSRC:
v = rec_devices;
break;
case SOUND_MIXER_STEREODEVS:
v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
break;
case SOUND_MIXER_DEVMASK:
v = SUPPORTED_MIXER_DEVICES;
break;
case SOUND_MIXER_RECMASK:
v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
break;
case SOUND_MIXER_CAPS:
v = 0; /* No special capabilities */
break;
default:
v = levels[cmd & 0xff];
break;
}
}
return put_user(v, p);
}
return -EINVAL;
}
static struct mixer_operations pas_mixer_operations =
{
.owner = THIS_MODULE,
.id = "PAS16",
.name = "Pro Audio Spectrum 16",
.ioctl = pas_mixer_ioctl
};
int __init
pas_init_mixer(void)
{
int d;
levels = load_mixer_volumes("PAS16_1", default_levels, 1);
pas_mixer_reset();
if ((d = sound_alloc_mixerdev()) != -1)
{
audio_devs[pas_audiodev]->mixer_dev = d;
mixer_devs[d] = &pas_mixer_operations;
}
return 1;
}

419
sound/oss/pas2_pcm.c Normal file
View file

@ -0,0 +1,419 @@
/*
* pas2_pcm.c Audio routines for PAS16
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Alan Cox : Swatted a double allocation of device bug. Made a few
* more things module options.
* Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
*/
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/timex.h>
#include "sound_config.h"
#include "pas2.h"
#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; /* sampling rate */
static unsigned char pcm_channels = 1; /* channels (1 or 2) */
static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
static unsigned char pcm_filter; /* filter FLAG */
static unsigned char pcm_mode = PCM_NON;
static unsigned long pcm_count;
static unsigned short pcm_bitsok = 8; /* mask of OK bits */
static int pcm_busy;
int pas_audiodev = -1;
static int open_mode;
extern spinlock_t pas_lock;
static int pcm_set_speed(int arg)
{
int foo, tmp;
unsigned long flags;
if (arg == 0)
return pcm_speed;
if (arg > 44100)
arg = 44100;
if (arg < 5000)
arg = 5000;
if (pcm_channels & 2)
{
foo = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg;
arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo;
}
else
{
foo = (PIT_TICK_RATE + (arg / 2)) / arg;
arg = (PIT_TICK_RATE + (foo / 2)) / foo;
}
pcm_speed = arg;
tmp = pas_read(0x0B8A);
/*
* Set anti-aliasing filters according to sample rate. You really *NEED*
* to enable this feature for all normal recording unless you want to
* experiment with aliasing effects.
* These filters apply to the selected "recording" source.
* I (pfw) don't know the encoding of these 5 bits. The values shown
* come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
*
* I cleared bit 5 of these values, since that bit controls the master
* mute flag. (Olav Wölfelschneider)
*
*/
#if !defined NO_AUTO_FILTER_SET
tmp &= 0xe0;
if (pcm_speed >= 2 * 17897)
tmp |= 0x01;
else if (pcm_speed >= 2 * 15909)
tmp |= 0x02;
else if (pcm_speed >= 2 * 11931)
tmp |= 0x09;
else if (pcm_speed >= 2 * 8948)
tmp |= 0x11;
else if (pcm_speed >= 2 * 5965)
tmp |= 0x19;
else if (pcm_speed >= 2 * 2982)
tmp |= 0x04;
pcm_filter = tmp;
#endif
spin_lock_irqsave(&pas_lock, flags);
pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
pas_write(0x00 | 0x30 | 0x04, 0x138B);
pas_write(foo & 0xff, 0x1388);
pas_write((foo >> 8) & 0xff, 0x1388);
pas_write(tmp, 0x0B8A);
spin_unlock_irqrestore(&pas_lock, flags);
return pcm_speed;
}
static int pcm_set_channels(int arg)
{
if ((arg != 1) && (arg != 2))
return pcm_channels;
if (arg != pcm_channels)
{
pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
pcm_channels = arg;
pcm_set_speed(pcm_speed); /* The speed must be reinitialized */
}
return pcm_channels;
}
static int pcm_set_bits(int arg)
{
if (arg == 0)
return pcm_bits;
if ((arg & pcm_bitsok) != arg)
return pcm_bits;
if (arg != pcm_bits)
{
pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
pcm_bits = arg;
}
return pcm_bits;
}
static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
{
int val, ret;
int __user *p = arg;
switch (cmd)
{
case SOUND_PCM_WRITE_RATE:
if (get_user(val, p))
return -EFAULT;
ret = pcm_set_speed(val);
break;
case SOUND_PCM_READ_RATE:
ret = pcm_speed;
break;
case SNDCTL_DSP_STEREO:
if (get_user(val, p))
return -EFAULT;
ret = pcm_set_channels(val + 1) - 1;
break;
case SOUND_PCM_WRITE_CHANNELS:
if (get_user(val, p))
return -EFAULT;
ret = pcm_set_channels(val);
break;
case SOUND_PCM_READ_CHANNELS:
ret = pcm_channels;
break;
case SNDCTL_DSP_SETFMT:
if (get_user(val, p))
return -EFAULT;
ret = pcm_set_bits(val);
break;
case SOUND_PCM_READ_BITS:
ret = pcm_bits;
break;
default:
return -EINVAL;
}
return put_user(ret, p);
}
static void pas_audio_reset(int dev)
{
pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */
}
static int pas_audio_open(int dev, int mode)
{
int err;
unsigned long flags;
spin_lock_irqsave(&pas_lock, flags);
if (pcm_busy)
{
spin_unlock_irqrestore(&pas_lock, flags);
return -EBUSY;
}
pcm_busy = 1;
spin_unlock_irqrestore(&pas_lock, flags);
if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
return err;
pcm_count = 0;
open_mode = mode;
return 0;
}
static void pas_audio_close(int dev)
{
unsigned long flags;
spin_lock_irqsave(&pas_lock, flags);
pas_audio_reset(dev);
pas_remove_intr(PAS_PCM_INTRBITS);
pcm_mode = PCM_NON;
pcm_busy = 0;
spin_unlock_irqrestore(&pas_lock, flags);
}
static void pas_audio_output_block(int dev, unsigned long buf, int count,
int intrflag)
{
unsigned long flags, cnt;
cnt = count;
if (audio_devs[dev]->dmap_out->dma > 3)
cnt >>= 1;
if (audio_devs[dev]->flags & DMA_AUTOMODE &&
intrflag &&
cnt == pcm_count)
return;
spin_lock_irqsave(&pas_lock, flags);
pas_write(pas_read(0xF8A) & ~0x40,
0xF8A);
/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
if (audio_devs[dev]->dmap_out->dma > 3)
count >>= 1;
if (count != pcm_count)
{
pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
pas_write(0x40 | 0x30 | 0x04, 0x138B);
pas_write(count & 0xff, 0x1389);
pas_write((count >> 8) & 0xff, 0x1389);
pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
pcm_count = count;
}
pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
#ifdef NO_TRIGGER
pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
#endif
pcm_mode = PCM_DAC;
spin_unlock_irqrestore(&pas_lock, flags);
}
static void pas_audio_start_input(int dev, unsigned long buf, int count,
int intrflag)
{
unsigned long flags;
int cnt;
cnt = count;
if (audio_devs[dev]->dmap_out->dma > 3)
cnt >>= 1;
if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
intrflag &&
cnt == pcm_count)
return;
spin_lock_irqsave(&pas_lock, flags);
/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
if (audio_devs[dev]->dmap_out->dma > 3)
count >>= 1;
if (count != pcm_count)
{
pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
pas_write(0x40 | 0x30 | 0x04, 0x138B);
pas_write(count & 0xff, 0x1389);
pas_write((count >> 8) & 0xff, 0x1389);
pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
pcm_count = count;
}
pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
#ifdef NO_TRIGGER
pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
#endif
pcm_mode = PCM_ADC;
spin_unlock_irqrestore(&pas_lock, flags);
}
#ifndef NO_TRIGGER
static void pas_audio_trigger(int dev, int state)
{
unsigned long flags;
spin_lock_irqsave(&pas_lock, flags);
state &= open_mode;
if (state & PCM_ENABLE_OUTPUT)
pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
else if (state & PCM_ENABLE_INPUT)
pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
else
pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
spin_unlock_irqrestore(&pas_lock, flags);
}
#endif
static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
{
pas_audio_reset(dev);
return 0;
}
static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
{
pas_audio_reset(dev);
return 0;
}
static struct audio_driver pas_audio_driver =
{
.owner = THIS_MODULE,
.open = pas_audio_open,
.close = pas_audio_close,
.output_block = pas_audio_output_block,
.start_input = pas_audio_start_input,
.ioctl = pas_audio_ioctl,
.prepare_for_input = pas_audio_prepare_for_input,
.prepare_for_output = pas_audio_prepare_for_output,
.halt_io = pas_audio_reset,
.trigger = pas_audio_trigger
};
void __init pas_pcm_init(struct address_info *hw_config)
{
pcm_bitsok = 8;
if (pas_read(0xEF8B) & 0x08)
pcm_bitsok |= 16;
pcm_set_speed(DSP_DEFAULT_SPEED);
if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
"Pro Audio Spectrum",
&pas_audio_driver,
sizeof(struct audio_driver),
DMA_AUTOMODE,
AFMT_U8 | AFMT_S16_LE,
NULL,
hw_config->dma,
hw_config->dma)) < 0)
printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
}
void pas_pcm_interrupt(unsigned char status, int cause)
{
if (cause == 1)
{
/*
* Halt the PCM first. Otherwise we don't have time to start a new
* block before the PCM chip proceeds to the next sample
*/
if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
switch (pcm_mode)
{
case PCM_DAC:
DMAbuf_outputintr(pas_audiodev, 1);
break;
case PCM_ADC:
DMAbuf_inputintr(pas_audiodev);
break;
default:
printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
}
}
}

1270
sound/oss/pss.c Normal file

File diff suppressed because it is too large Load diff

185
sound/oss/sb.h Normal file
View file

@ -0,0 +1,185 @@
#define DSP_RESET (devc->base + 0x6)
#define DSP_READ (devc->base + 0xA)
#define DSP_WRITE (devc->base + 0xC)
#define DSP_COMMAND (devc->base + 0xC)
#define DSP_STATUS (devc->base + 0xC)
#define DSP_DATA_AVAIL (devc->base + 0xE)
#define DSP_DATA_AVL16 (devc->base + 0xF)
#define MIXER_ADDR (devc->base + 0x4)
#define MIXER_DATA (devc->base + 0x5)
#define OPL3_LEFT (devc->base + 0x0)
#define OPL3_RIGHT (devc->base + 0x2)
#define OPL3_BOTH (devc->base + 0x8)
/* DSP Commands */
#define DSP_CMD_SPKON 0xD1
#define DSP_CMD_SPKOFF 0xD3
#define DSP_CMD_DMAON 0xD0
#define DSP_CMD_DMAOFF 0xD4
#define IMODE_NONE 0
#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
#define IMODE_INPUT PCM_ENABLE_INPUT
#define IMODE_INIT 3
#define IMODE_MIDI 4
#define NORMAL_MIDI 0
#define UART_MIDI 1
/*
* Device models
*/
#define MDL_NONE 0
#define MDL_SB1 1 /* SB1.0 or 1.5 */
#define MDL_SB2 2 /* SB2.0 */
#define MDL_SB201 3 /* SB2.01 */
#define MDL_SBPRO 4 /* SB Pro */
#define MDL_SB16 5 /* SB16/32/AWE */
#define MDL_SBPNP 6 /* SB16/32/AWE PnP */
#define MDL_JAZZ 10 /* Media Vision Jazz16 */
#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */
#define MDL_ESS 12 /* ESS ES688 and ES1688 */
#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */
#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */
#define MDL_AEDSP 15 /* Audio Excel DSP 16 */
#define MDL_ESSPCI 16 /* ESS PCI card */
#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */
#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */
/* register assignment */
#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */
/* to 48kHz */
/*
* Config flags
*/
#define SB_NO_MIDI 0x00000001
#define SB_NO_MIXER 0x00000002
#define SB_NO_AUDIO 0x00000004
#define SB_NO_RECORDING 0x00000008 /* No audio recording */
#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER)
#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */
struct mixer_def {
unsigned int regno: 8;
unsigned int bitoffs:4;
unsigned int nbits:4;
};
typedef struct mixer_def mixer_tab[32][2];
typedef struct mixer_def mixer_ent;
struct sb_module_options
{
int esstype; /* ESS chip type */
int acer; /* Do acer notebook init? */
int sm_games; /* Logitech soundman games? */
};
typedef struct sb_devc {
int dev;
/* Hardware parameters */
int *osp;
int minor, major;
int type;
int model, submodel;
int caps;
# define SBCAP_STEREO 0x00000001
# define SBCAP_16BITS 0x00000002
/* Hardware resources */
int base;
int irq;
int dma8, dma16;
int pcibase; /* For ESS Maestro etc */
/* State variables */
int opened;
/* new audio fields for full duplex support */
int fullduplex;
int duplex;
int speed, bits, channels;
volatile int irq_ok;
volatile int intr_active, irq_mode;
/* duplicate audio fields for full duplex support */
volatile int intr_active_16, irq_mode_16;
/* Mixer fields */
int *levels;
mixer_tab *iomap;
size_t iomap_sz; /* number or records in the iomap table */
int mixer_caps, recmask, outmask, supported_devices;
int supported_rec_devices, supported_out_devices;
int my_mixerdev;
int sbmixnum;
/* Audio fields */
unsigned long trg_buf;
int trigger_bits;
int trg_bytes;
int trg_intrflag;
int trg_restart;
/* duplicate audio fields for full duplex support */
unsigned long trg_buf_16;
int trigger_bits_16;
int trg_bytes_16;
int trg_intrflag_16;
int trg_restart_16;
unsigned char tconst;
/* MIDI fields */
int my_mididev;
int input_opened;
int midi_broken;
void (*midi_input_intr) (int dev, unsigned char data);
void *midi_irq_cookie; /* IRQ cookie for the midi */
spinlock_t lock;
struct sb_module_options sbmo; /* Module options */
} sb_devc;
/*
* PCI card types
*/
#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */
#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */
/*
* Functions
*/
int sb_dsp_command (sb_devc *devc, unsigned char val);
int sb_dsp_get_byte(sb_devc * devc);
int sb_dsp_reset (sb_devc *devc);
void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo);
int sb_dsp_init (struct address_info *hw_config, struct module *owner);
void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
int sb_mixer_init(sb_devc *devc, struct module *owner);
void sb_mixer_unload(sb_devc *devc);
void sb_mixer_set_stereo (sb_devc *devc, int mode);
void smw_mixer_init(sb_devc *devc);
void sb_dsp_midi_init (sb_devc *devc, struct module *owner);
void sb_audio_init (sb_devc *devc, char *name, struct module *owner);
void sb_midi_interrupt (sb_devc *devc);
void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right);
int sb_audio_open(int dev, int mode);
void sb_audio_close(int dev);
/* From sb_common.c */
void sb_dsp_disable_midi(int port);
int probe_sbmpu (struct address_info *hw_config, struct module *owner);
void unload_sbmpu (struct address_info *hw_config);
void unload_sb16(struct address_info *hw_info);
void unload_sb16midi(struct address_info *hw_info);

1101
sound/oss/sb_audio.c Normal file

File diff suppressed because it is too large Load diff

354
sound/oss/sb_card.c Normal file
View file

@ -0,0 +1,354 @@
/*
* sound/oss/sb_card.c
*
* Detection routine for the ISA Sound Blaster and compatible sound
* cards.
*
* This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this
* software for more info.
*
* This is a complete rewrite of the detection routines. This was
* prompted by the PnP API change during v2.5 and the ugly state the
* code was in.
*
* Copyright (C) by Paul Laufer 2002. Based on code originally by
* Hannu Savolainen which was modified by many others over the
* years. Authors specifically mentioned in the previous version were:
* Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de
* Melo, Daniel Church, and myself.
*
* 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com>
* 02-07-2003 Bug made it into first release. Take two.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/init.h>
#include "sound_config.h"
#include "sb_mixer.h"
#include "sb.h"
#ifdef CONFIG_PNP
#include <linux/pnp.h>
#endif /* CONFIG_PNP */
#include "sb_card.h"
MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver");
MODULE_LICENSE("GPL");
extern void *smw_free;
static int __initdata mpu_io = 0;
static int __initdata io = -1;
static int __initdata irq = -1;
static int __initdata dma = -1;
static int __initdata dma16 = -1;
static int __initdata type = 0; /* Can set this to a specific card type */
static int __initdata esstype = 0; /* ESS chip type */
static int __initdata acer = 0; /* Do acer notebook init? */
static int __initdata sm_games = 0; /* Logitech soundman games? */
static struct sb_card_config *legacy = NULL;
#ifdef CONFIG_PNP
static int pnp_registered;
static int __initdata pnp = 1;
/*
static int __initdata uart401 = 0;
*/
#else
static int __initdata pnp = 0;
#endif
module_param(io, int, 000);
MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
module_param(irq, int, 000);
MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)");
module_param(dma, int, 000);
MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)");
module_param(dma16, int, 000);
MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)");
module_param(mpu_io, int, 000);
MODULE_PARM_DESC(mpu_io, "MPU base address");
module_param(type, int, 000);
MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \
"work with pnp)");
module_param(sm_games, int, 000);
MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \
"(doesn't work with pnp)");
module_param(esstype, int, 000);
MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)");
module_param(acer, int, 000);
MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\
"(doesn't work with pnp)");
#ifdef CONFIG_PNP
module_param(pnp, int, 000);
MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\
"Default is 1.\n");
/* Not done yet.... */
/*
module_param(uart401, int, 000);
MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\
"the mpu on some clones");
*/
#endif /* CONFIG_PNP */
/* OSS subsystem card registration shared by PnP and legacy routines */
static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo)
{
if (!request_region(scc->conf.io_base, 16, "soundblaster")) {
printk(KERN_ERR "sb: ports busy.\n");
kfree(scc);
return -EBUSY;
}
if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) {
release_region(scc->conf.io_base, 16);
printk(KERN_ERR "sb: Failed DSP Detect.\n");
kfree(scc);
return -ENODEV;
}
if(!sb_dsp_init(&scc->conf, THIS_MODULE)) {
printk(KERN_ERR "sb: Failed DSP init.\n");
kfree(scc);
return -ENODEV;
}
if(scc->mpucnf.io_base > 0) {
scc->mpu = 1;
printk(KERN_INFO "sb: Turning on MPU\n");
if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE))
scc->mpu = 0;
}
return 1;
}
static void sb_unload(struct sb_card_config *scc)
{
sb_dsp_unload(&scc->conf, 0);
if(scc->mpu)
unload_sbmpu(&scc->mpucnf);
kfree(scc);
}
/* Register legacy card with OSS subsystem */
static int __init sb_init_legacy(void)
{
struct sb_module_options sbmo = {0};
if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "sb: Error: Could not allocate memory\n");
return -ENOMEM;
}
legacy->conf.io_base = io;
legacy->conf.irq = irq;
legacy->conf.dma = dma;
legacy->conf.dma2 = dma16;
legacy->conf.card_subtype = type;
legacy->mpucnf.io_base = mpu_io;
legacy->mpucnf.irq = -1;
legacy->mpucnf.dma = -1;
legacy->mpucnf.dma2 = -1;
sbmo.esstype = esstype;
sbmo.sm_games = sm_games;
sbmo.acer = acer;
return sb_register_oss(legacy, &sbmo);
}
#ifdef CONFIG_PNP
/* Populate the OSS subsystem structures with information from PnP */
static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc)
{
scc->conf.io_base = -1;
scc->conf.irq = -1;
scc->conf.dma = -1;
scc->conf.dma2 = -1;
scc->mpucnf.io_base = -1;
scc->mpucnf.irq = -1;
scc->mpucnf.dma = -1;
scc->mpucnf.dma2 = -1;
/* All clones layout their PnP tables differently and some use
different logical devices for the MPU */
if(!strncmp("CTL",scc->card_id,3)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,0);
scc->conf.dma2 = pnp_dma(dev,1);
scc->mpucnf.io_base = pnp_port_start(dev,1);
return;
}
if(!strncmp("tBA",scc->card_id,3)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,0);
scc->conf.dma2 = pnp_dma(dev,1);
return;
}
if(!strncmp("ESS",scc->card_id,3)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,0);
scc->conf.dma2 = pnp_dma(dev,1);
scc->mpucnf.io_base = pnp_port_start(dev,2);
return;
}
if(!strncmp("CMI",scc->card_id,3)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,0);
scc->conf.dma2 = pnp_dma(dev,1);
return;
}
if(!strncmp("RWB",scc->card_id,3)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,0);
return;
}
if(!strncmp("ALS",scc->card_id,3)) {
if(!strncmp("ALS0007",scc->card_id,7)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,0);
} else {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,1);
scc->conf.dma2 = pnp_dma(dev,0);
}
return;
}
if(!strncmp("RTL",scc->card_id,3)) {
scc->conf.io_base = pnp_port_start(dev,0);
scc->conf.irq = pnp_irq(dev,0);
scc->conf.dma = pnp_dma(dev,1);
scc->conf.dma2 = pnp_dma(dev,0);
}
}
static unsigned int sb_pnp_devices;
/* Probe callback function for the PnP API */
static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id)
{
struct sb_card_config *scc;
struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */
struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL);
if(!dev){
return -EBUSY;
}
if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "sb: Error: Could not allocate memory\n");
return -ENOMEM;
}
printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \
"%s, Device PnP id = %s\n", card->card->name, card_id->id,
dev->id->id);
scc->card_id = card_id->id;
scc->dev_id = dev->id->id;
sb_dev2cfg(dev, scc);
printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \
"dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq,
scc->conf.dma, scc->conf.dma2);
pnp_set_card_drvdata(card, scc);
sb_pnp_devices++;
return sb_register_oss(scc, &sbmo);
}
static void sb_pnp_remove(struct pnp_card_link *card)
{
struct sb_card_config *scc = pnp_get_card_drvdata(card);
if(!scc)
return;
printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id);
sb_unload(scc);
}
static struct pnp_card_driver sb_pnp_driver = {
.name = "OSS SndBlstr", /* 16 character limit */
.id_table = sb_pnp_card_table,
.probe = sb_pnp_probe,
.remove = sb_pnp_remove,
};
MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table);
#endif /* CONFIG_PNP */
static void sb_unregister_all(void)
{
#ifdef CONFIG_PNP
if (pnp_registered)
pnp_unregister_card_driver(&sb_pnp_driver);
#endif
}
static int __init sb_init(void)
{
int lres = 0;
int pres = 0;
printk(KERN_INFO "sb: Init: Starting Probe...\n");
if(io != -1 && irq != -1 && dma != -1) {
printk(KERN_INFO "sb: Probing legacy card with io=%x, "\
"irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16);
lres = sb_init_legacy();
} else if((io != -1 || irq != -1 || dma != -1) ||
(!pnp && (io == -1 && irq == -1 && dma == -1)))
printk(KERN_ERR "sb: Error: At least io, irq, and dma "\
"must be set for legacy cards.\n");
#ifdef CONFIG_PNP
if(pnp) {
int err = pnp_register_card_driver(&sb_pnp_driver);
if (!err)
pnp_registered = 1;
pres = sb_pnp_devices;
}
#endif
printk(KERN_INFO "sb: Init: Done\n");
/* If either PnP or Legacy registered a card then return
* success */
if (pres == 0 && lres <= 0) {
sb_unregister_all();
return -ENODEV;
}
return 0;
}
static void __exit sb_exit(void)
{
printk(KERN_INFO "sb: Unloading...\n");
/* Unload legacy card */
if (legacy) {
printk (KERN_INFO "sb: Unloading legacy card\n");
sb_unload(legacy);
}
sb_unregister_all();
vfree(smw_free);
smw_free = NULL;
}
module_init(sb_init);
module_exit(sb_exit);

149
sound/oss/sb_card.h Normal file
View file

@ -0,0 +1,149 @@
/*
* sound/oss/sb_card.h
*
* This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this
* software for more info.
*
* 02-05-2002 Original Release, Paul Laufer <paul@laufernet.com>
*/
struct sb_card_config {
struct address_info conf;
struct address_info mpucnf;
const char *card_id;
const char *dev_id;
int mpu;
};
#ifdef CONFIG_PNP
/*
* SoundBlaster PnP tables and structures.
*/
/* Card PnP ID Table */
static struct pnp_card_device_id sb_pnp_card_table[] = {
/* Sound Blaster 16 */
{.id = "CTL0024", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL0025", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL0026", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL0027", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL0028", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL0029", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL002a", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL002b", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL002c", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster 16 */
{.id = "CTL00ed", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
/* Sound Blaster 16 */
{.id = "CTL0086", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
/* Sound Blaster Vibra16S */
{.id = "CTL0051", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
/* Sound Blaster Vibra16C */
{.id = "CTL0070", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
/* Sound Blaster Vibra16CL */
{.id = "CTL0080", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
/* Sound Blaster Vibra16CL */
{.id = "CTL00F0", .driver_data = 0, .devs = { {.id="CTL0043"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0039", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0042", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0043", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0044", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0045", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0046", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0047", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0048", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL0054", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
/* Sound Blaster AWE 32 */
{.id = "CTL009C", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
/* Createive SB32 PnP */
{.id = "CTL009F", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL009D", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
/* Sound Blaster AWE 64 Gold */
{.id = "CTL009E", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
/* Sound Blaster AWE 64 Gold */
{.id = "CTL00B2", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL00C1", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL00C3", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL00C5", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL00C7", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL00E4", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
/* Sound Blaster AWE 64 */
{.id = "CTL00E9", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
/* ESS 1868 */
{.id = "ESS0968", .driver_data = 0, .devs = { {.id="ESS0968"}, } },
/* ESS 1868 */
{.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS1868"}, } },
/* ESS 1868 */
{.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS8611"}, } },
/* ESS 1869 PnP AudioDrive */
{.id = "ESS0003", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
/* ESS 1869 */
{.id = "ESS1869", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
/* ESS 1878 */
{.id = "ESS1878", .driver_data = 0, .devs = { {.id="ESS1878"}, } },
/* ESS 1879 */
{.id = "ESS1879", .driver_data = 0, .devs = { {.id="ESS1879"}, } },
/* CMI 8330 SoundPRO */
{.id = "CMI0001", .driver_data = 0, .devs = { {.id="@X@0001"},
{.id="@H@0001"},
{.id="@@@0001"}, } },
/* Diamond DT0197H */
{.id = "RWR1688", .driver_data = 0, .devs = { {.id="@@@0001"},
{.id="@X@0001"},
{.id="@H@0001"}, } },
/* ALS007 */
{.id = "ALS0007", .driver_data = 0, .devs = { {.id="@@@0001"},
{.id="@X@0001"},
{.id="@H@0001"}, } },
/* ALS100 */
{.id = "ALS0001", .driver_data = 0, .devs = { {.id="@@@0001"},
{.id="@X@0001"},
{.id="@H@0001"}, } },
/* ALS110 */
{.id = "ALS0110", .driver_data = 0, .devs = { {.id="@@@1001"},
{.id="@X@1001"},
{.id="@H@0001"}, } },
/* ALS120 */
{.id = "ALS0120", .driver_data = 0, .devs = { {.id="@@@2001"},
{.id="@X@2001"},
{.id="@H@0001"}, } },
/* ALS200 */
{.id = "ALS0200", .driver_data = 0, .devs = { {.id="@@@0020"},
{.id="@X@0030"},
{.id="@H@0001"}, } },
/* ALS200 */
{.id = "RTL3000", .driver_data = 0, .devs = { {.id="@@@2001"},
{.id="@X@2001"},
{.id="@H@0001"}, } },
/* Sound Blaster 16 (Virtual PC 2004) */
{.id = "tBA03b0", .driver_data = 0, .devs = { {.id="PNPb003"}, } },
/* -end- */
{.id = "", }
};
#endif

1287
sound/oss/sb_common.c Normal file

File diff suppressed because it is too large Load diff

1827
sound/oss/sb_ess.c Normal file

File diff suppressed because it is too large Load diff

34
sound/oss/sb_ess.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Created: 9-Jan-1999 Rolf Fokkens
*/
extern void ess_intr
(sb_devc *devc);
extern int ess_dsp_init
(sb_devc *devc, struct address_info *hw_config);
extern struct audio_driver *ess_audio_init
(sb_devc *devc, int *audio_flags, int *format_mask);
extern int ess_midi_init
(sb_devc *devc, struct address_info *hw_config);
extern void ess_mixer_init
(sb_devc *devc);
extern int ess_init
(sb_devc *devc, struct address_info *hw_config);
extern int ess_dsp_reset
(sb_devc *devc);
extern void ess_setmixer
(sb_devc *devc, unsigned int port, unsigned int value);
extern unsigned int ess_getmixer
(sb_devc *devc, unsigned int port);
extern int ess_mixer_set
(sb_devc *devc, int dev, int left, int right);
extern int ess_mixer_reset
(sb_devc *devc);
extern void ess_mixer_reload
(sb_devc * devc, int dev);
extern int ess_set_recmask
(sb_devc *devc, int *mask);

206
sound/oss/sb_midi.c Normal file
View file

@ -0,0 +1,206 @@
/*
* sound/oss/sb_midi.c
*
* The low level driver for the Sound Blaster DS chips.
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
#include <linux/spinlock.h>
#include <linux/slab.h>
#include "sound_config.h"
#include "sb.h"
#undef SB_TEST_IRQ
/*
* The DSP channel can be used either for input or output. Variable
* 'sb_irq_mode' will be set when the program calls read or write first time
* after open. Current version doesn't support mode changes without closing
* and reopening the device. Support for this feature may be implemented in a
* future version of this driver.
*/
static int sb_midi_open(int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
sb_devc *devc = midi_devs[dev]->devc;
unsigned long flags;
if (devc == NULL)
return -ENXIO;
spin_lock_irqsave(&devc->lock, flags);
if (devc->opened)
{
spin_unlock_irqrestore(&devc->lock, flags);
return -EBUSY;
}
devc->opened = 1;
spin_unlock_irqrestore(&devc->lock, flags);
devc->irq_mode = IMODE_MIDI;
devc->midi_broken = 0;
sb_dsp_reset(devc);
if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */
{
devc->opened = 0;
return -EIO;
}
devc->intr_active = 1;
if (mode & OPEN_READ)
{
devc->input_opened = 1;
devc->midi_input_intr = input;
}
return 0;
}
static void sb_midi_close(int dev)
{
sb_devc *devc = midi_devs[dev]->devc;
unsigned long flags;
if (devc == NULL)
return;
spin_lock_irqsave(&devc->lock, flags);
sb_dsp_reset(devc);
devc->intr_active = 0;
devc->input_opened = 0;
devc->opened = 0;
spin_unlock_irqrestore(&devc->lock, flags);
}
static int sb_midi_out(int dev, unsigned char midi_byte)
{
sb_devc *devc = midi_devs[dev]->devc;
if (devc == NULL)
return 1;
if (devc->midi_broken)
return 1;
if (!sb_dsp_command(devc, midi_byte))
{
devc->midi_broken = 1;
return 1;
}
return 1;
}
static int sb_midi_start_read(int dev)
{
return 0;
}
static int sb_midi_end_read(int dev)
{
sb_devc *devc = midi_devs[dev]->devc;
if (devc == NULL)
return -ENXIO;
sb_dsp_reset(devc);
devc->intr_active = 0;
return 0;
}
static int sb_midi_ioctl(int dev, unsigned cmd, void __user *arg)
{
return -EINVAL;
}
void sb_midi_interrupt(sb_devc * devc)
{
unsigned long flags;
unsigned char data;
if (devc == NULL)
return;
spin_lock_irqsave(&devc->lock, flags);
data = inb(DSP_READ);
if (devc->input_opened)
devc->midi_input_intr(devc->my_mididev, data);
spin_unlock_irqrestore(&devc->lock, flags);
}
#define MIDI_SYNTH_NAME "Sound Blaster Midi"
#define MIDI_SYNTH_CAPS 0
#include "midi_synth.h"
static struct midi_operations sb_midi_operations =
{
.owner = THIS_MODULE,
.info = {"Sound Blaster", 0, 0, SNDCARD_SB},
.converter = &std_midi_synth,
.in_info = {0},
.open = sb_midi_open,
.close = sb_midi_close,
.ioctl = sb_midi_ioctl,
.outputc = sb_midi_out,
.start_read = sb_midi_start_read,
.end_read = sb_midi_end_read,
};
void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
{
int dev;
if (devc->model < 2) /* No MIDI support for SB 1.x */
return;
dev = sound_alloc_mididev();
if (dev == -1)
{
printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
return;
}
std_midi_synth.midi_dev = devc->my_mididev = dev;
midi_devs[dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
if (midi_devs[dev] == NULL)
{
printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
sound_unload_mididev(dev);
return;
}
memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
sizeof(struct midi_operations));
if (owner)
midi_devs[dev]->owner = owner;
midi_devs[dev]->devc = devc;
midi_devs[dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
if (midi_devs[dev]->converter == NULL)
{
printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
kfree(midi_devs[dev]);
sound_unload_mididev(dev);
return;
}
memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth,
sizeof(struct synth_operations));
midi_devs[dev]->converter->id = "SBMIDI";
sequencer_init();
}

770
sound/oss/sb_mixer.c Normal file
View file

@ -0,0 +1,770 @@
/*
* sound/oss/sb_mixer.c
*
* The low level mixer driver for the Sound Blaster compatible cards.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch]
* Stanislav Voronyi <stas@esc.kharkov.com> : Support for AWE 3DSE device (Jun 7 1999)
*/
#include <linux/slab.h>
#include "sound_config.h"
#define __SB_MIXER_C__
#include "sb.h"
#include "sb_mixer.h"
#include "sb_ess.h"
#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
/* Same as SB Pro, unless I find otherwise */
#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
* channel is the COVOX/DisneySoundSource emulation volume control
* on the mixer. It does NOT control speaker volume. Should have own
* mask eventually?
*/
#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD)
#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD)
#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | \
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
SOUND_MASK_IMIX)
/* These are the only devices that are working at the moment. Others could
* be added once they are identified and a method is found to control them.
*/
#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
SOUND_MASK_PCM | SOUND_MASK_MIC | \
SOUND_MASK_CD | \
SOUND_MASK_VOLUME)
static mixer_tab sbpro_mix = {
MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
static mixer_tab sb16_mix = {
MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
};
static mixer_tab als007_mix =
{
MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4),
MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4),
MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4),
MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4),
MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
};
/* SM_GAMES Master volume is lower and PCM & FM volumes
higher than with SB Pro. This improves the
sound quality */
static int smg_default_levels[32] =
{
0x2020, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x6464, /* FM */
0x6464, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x0000, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b, /* Output gain */
0x4040, /* Line1 */
0x4040, /* Line2 */
0x1515 /* Line3 */
};
static int sb_default_levels[32] =
{
0x5a5a, /* Master Volume */
0x4b4b, /* Bass */
0x4b4b, /* Treble */
0x4b4b, /* FM */
0x4b4b, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
0x1010, /* Mic */
0x4b4b, /* CD */
0x0000, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b, /* Output gain */
0x4040, /* Line1 */
0x4040, /* Line2 */
0x1515 /* Line3 */
};
static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x40, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x10, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x04, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
0x20, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x08, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x02, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00 /* SOUND_MIXER_OGAIN */
};
static char smw_mix_regs[] = /* Left mixer registers */
{
0x0b, /* SOUND_MIXER_VOLUME */
0x0d, /* SOUND_MIXER_BASS */
0x0d, /* SOUND_MIXER_TREBLE */
0x05, /* SOUND_MIXER_SYNTH */
0x09, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
0x03, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
0x07, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00, /* SOUND_MIXER_RECLEV */
0x00, /* SOUND_MIXER_IGAIN */
0x00, /* SOUND_MIXER_OGAIN */
0x00, /* SOUND_MIXER_LINE1 */
0x00, /* SOUND_MIXER_LINE2 */
0x00 /* SOUND_MIXER_LINE3 */
};
static int sbmixnum = 1;
static void sb_mixer_reset(sb_devc * devc);
void sb_mixer_set_stereo(sb_devc * devc, int mode)
{
sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
}
static int detect_mixer(sb_devc * devc)
{
/* Just trust the mixer is there */
return 1;
}
static void oss_change_bits(sb_devc *devc, unsigned char *regval, int dev, int chn, int newval)
{
unsigned char mask;
int shift;
mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
newval = (int) ((newval * mask) + 50) / 100; /* Scale */
shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
*regval &= ~(mask << shift); /* Mask out previous value */
*regval |= (newval & mask) << shift; /* Set the new value */
}
static int sb_mixer_get(sb_devc * devc, int dev)
{
if (!((1 << dev) & devc->supported_devices))
return -EINVAL;
return devc->levels[dev];
}
void smw_mixer_init(sb_devc * devc)
{
int i;
sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */
sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */
devc->supported_devices = 0;
for (i = 0; i < sizeof(smw_mix_regs); i++)
if (smw_mix_regs[i] != 0)
devc->supported_devices |= (1 << i);
devc->supported_rec_devices = devc->supported_devices &
~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME);
sb_mixer_reset(devc);
}
int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
{
int regoffs;
unsigned char val;
if ((dev < 0) || (dev >= devc->iomap_sz))
return -EINVAL;
regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
if (regoffs == 0)
return -EINVAL;
val = sb_getmixer(devc, regoffs);
oss_change_bits(devc, &val, dev, LEFT_CHN, left);
if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /*
* Change register
*/
{
sb_setmixer(devc, regoffs, val); /*
* Save the old one
*/
regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
if (regoffs == 0)
return left | (left << 8); /*
* Just left channel present
*/
val = sb_getmixer(devc, regoffs); /*
* Read the new one
*/
}
oss_change_bits(devc, &val, dev, RIGHT_CHN, right);
sb_setmixer(devc, regoffs, val);
return left | (right << 8);
}
static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
{
int reg, val;
switch (dev)
{
case SOUND_MIXER_VOLUME:
sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */
sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
break;
case SOUND_MIXER_BASS:
case SOUND_MIXER_TREBLE:
devc->levels[dev] = left | (right << 8);
/* Set left bass and treble values */
val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
sb_setmixer(devc, 0x0d, val);
/* Set right bass and treble values */
val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
sb_setmixer(devc, 0x0e, val);
break;
default:
/* bounds check */
if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs))
return -EINVAL;
reg = smw_mix_regs[dev];
if (reg == 0)
return -EINVAL;
sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */
sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
}
devc->levels[dev] = left | (right << 8);
return left | (right << 8);
}
static int sb_mixer_set(sb_devc * devc, int dev, int value)
{
int left = value & 0x000000ff;
int right = (value & 0x0000ff00) >> 8;
int retval;
if (left > 100)
left = 100;
if (right > 100)
right = 100;
if ((dev < 0) || (dev > 31))
return -EINVAL;
if (!(devc->supported_devices & (1 << dev))) /*
* Not supported
*/
return -EINVAL;
/* Differentiate depending on the chipsets */
switch (devc->model) {
case MDL_SMW:
retval = smw_mixer_set(devc, dev, left, right);
break;
case MDL_ESS:
retval = ess_mixer_set(devc, dev, left, right);
break;
default:
retval = sb_common_mixer_set(devc, dev, left, right);
}
if (retval >= 0) devc->levels[dev] = retval;
return retval;
}
/*
* set_recsrc doesn't apply to ES188x
*/
static void set_recsrc(sb_devc * devc, int src)
{
sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
}
static int set_recmask(sb_devc * devc, int mask)
{
int devmask, i;
unsigned char regimageL, regimageR;
devmask = mask & devc->supported_rec_devices;
switch (devc->model)
{
case MDL_SBPRO:
case MDL_ESS:
case MDL_JAZZ:
case MDL_SMW:
if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
break;
}
if (devmask != SOUND_MASK_MIC &&
devmask != SOUND_MASK_LINE &&
devmask != SOUND_MASK_CD)
{
/*
* More than one device selected. Drop the
* previous selection
*/
devmask &= ~devc->recmask;
}
if (devmask != SOUND_MASK_MIC &&
devmask != SOUND_MASK_LINE &&
devmask != SOUND_MASK_CD)
{
/*
* More than one device selected. Default to
* mic
*/
devmask = SOUND_MASK_MIC;
}
if (devmask ^ devc->recmask) /*
* Input source changed
*/
{
switch (devmask)
{
case SOUND_MASK_MIC:
set_recsrc(devc, SRC__MIC);
break;
case SOUND_MASK_LINE:
set_recsrc(devc, SRC__LINE);
break;
case SOUND_MASK_CD:
set_recsrc(devc, SRC__CD);
break;
default:
set_recsrc(devc, SRC__MIC);
}
}
break;
case MDL_SB16:
if (!devmask)
devmask = SOUND_MASK_MIC;
if (devc->submodel == SUBMDL_ALS007)
{
switch (devmask)
{
case SOUND_MASK_LINE:
sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE);
break;
case SOUND_MASK_CD:
sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD);
break;
case SOUND_MASK_SYNTH:
sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH);
break;
default: /* Also takes care of SOUND_MASK_MIC case */
sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC);
break;
}
}
else
{
regimageL = regimageR = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
{
if ((1 << i) & devmask)
{
regimageL |= sb16_recmasks_L[i];
regimageR |= sb16_recmasks_R[i];
}
sb_setmixer (devc, SB16_IMASK_L, regimageL);
sb_setmixer (devc, SB16_IMASK_R, regimageR);
}
}
break;
}
devc->recmask = devmask;
return devc->recmask;
}
static int set_outmask(sb_devc * devc, int mask)
{
int devmask, i;
unsigned char regimage;
devmask = mask & devc->supported_out_devices;
switch (devc->model)
{
case MDL_SB16:
if (devc->submodel == SUBMDL_ALS007)
break;
else
{
regimage = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
{
if ((1 << i) & devmask)
{
regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]);
}
sb_setmixer (devc, SB16_OMASK, regimage);
}
}
break;
default:
break;
}
devc->outmask = devmask;
return devc->outmask;
}
static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
{
sb_devc *devc = mixer_devs[dev]->devc;
int val, ret;
int __user *p = arg;
/*
* Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1).
* Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1)
* or mode==2 put 3DSE state to mode.
*/
if (devc->model == MDL_SB16) {
if (cmd == SOUND_MIXER_AGC)
{
if (get_user(val, p))
return -EFAULT;
sb_setmixer(devc, 0x43, (~val) & 0x01);
return 0;
}
if (cmd == SOUND_MIXER_3DSE)
{
/* I put here 15, but I don't know the exact version.
At least my 4.13 havn't 3DSE, 4.16 has it. */
if (devc->minor < 15)
return -EINVAL;
if (get_user(val, p))
return -EFAULT;
if (val == 0 || val == 1)
sb_chgmixer(devc, AWE_3DSE, 0x01, val);
else if (val == 2)
{
ret = sb_getmixer(devc, AWE_3DSE)&0x01;
return put_user(ret, p);
}
else
return -EINVAL;
return 0;
}
}
if (((cmd >> 8) & 0xff) == 'M')
{
if (_SIOC_DIR(cmd) & _SIOC_WRITE)
{
if (get_user(val, p))
return -EFAULT;
switch (cmd & 0xff)
{
case SOUND_MIXER_RECSRC:
ret = set_recmask(devc, val);
break;
case SOUND_MIXER_OUTSRC:
ret = set_outmask(devc, val);
break;
default:
ret = sb_mixer_set(devc, cmd & 0xff, val);
}
}
else switch (cmd & 0xff)
{
case SOUND_MIXER_RECSRC:
ret = devc->recmask;
break;
case SOUND_MIXER_OUTSRC:
ret = devc->outmask;
break;
case SOUND_MIXER_DEVMASK:
ret = devc->supported_devices;
break;
case SOUND_MIXER_STEREODEVS:
ret = devc->supported_devices;
/* The ESS seems to have stereo mic controls */
if (devc->model == MDL_ESS)
ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
break;
case SOUND_MIXER_RECMASK:
ret = devc->supported_rec_devices;
break;
case SOUND_MIXER_OUTMASK:
ret = devc->supported_out_devices;
break;
case SOUND_MIXER_CAPS:
ret = devc->mixer_caps;
break;
default:
ret = sb_mixer_get(devc, cmd & 0xff);
break;
}
return put_user(ret, p);
} else
return -EINVAL;
}
static struct mixer_operations sb_mixer_operations =
{
.owner = THIS_MODULE,
.id = "SB",
.name = "Sound Blaster",
.ioctl = sb_mixer_ioctl
};
static struct mixer_operations als007_mixer_operations =
{
.owner = THIS_MODULE,
.id = "ALS007",
.name = "Avance ALS-007",
.ioctl = sb_mixer_ioctl
};
static void sb_mixer_reset(sb_devc * devc)
{
char name[32];
int i;
sprintf(name, "SB_%d", devc->sbmixnum);
if (devc->sbmo.sm_games)
devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
else
devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
sb_mixer_set(devc, i, devc->levels[i]);
if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
set_recmask(devc, SOUND_MASK_MIC);
}
}
int sb_mixer_init(sb_devc * devc, struct module *owner)
{
int mixer_type = 0;
int m;
devc->sbmixnum = sbmixnum++;
devc->levels = NULL;
sb_setmixer(devc, 0x00, 0); /* Reset mixer */
if (!(mixer_type = detect_mixer(devc)))
return 0; /* No mixer. Why? */
switch (devc->model)
{
case MDL_ESSPCI:
case MDL_YMPCI:
case MDL_SBPRO:
case MDL_AZTECH:
case MDL_JAZZ:
devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
devc->supported_devices = SBPRO_MIXER_DEVICES;
devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
devc->iomap = &sbpro_mix;
devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
break;
case MDL_ESS:
ess_mixer_init (devc);
break;
case MDL_SMW:
devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
devc->supported_devices = 0;
devc->supported_rec_devices = 0;
devc->iomap = &sbpro_mix;
devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
smw_mixer_init(devc);
break;
case MDL_SB16:
devc->mixer_caps = 0;
devc->supported_rec_devices = SB16_RECORDING_DEVICES;
devc->supported_out_devices = SB16_OUTFILTER_DEVICES;
if (devc->submodel != SUBMDL_ALS007)
{
devc->supported_devices = SB16_MIXER_DEVICES;
devc->iomap = &sb16_mix;
devc->iomap_sz = ARRAY_SIZE(sb16_mix);
}
else
{
devc->supported_devices = ALS007_MIXER_DEVICES;
devc->iomap = &als007_mix;
devc->iomap_sz = ARRAY_SIZE(als007_mix);
}
break;
default:
printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model);
return 0;
}
m = sound_alloc_mixerdev();
if (m == -1)
return 0;
mixer_devs[m] = kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
if (mixer_devs[m] == NULL)
{
printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
sound_unload_mixerdev(m);
return 0;
}
if (devc->submodel != SUBMDL_ALS007)
memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations));
else
memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations));
mixer_devs[m]->devc = devc;
if (owner)
mixer_devs[m]->owner = owner;
devc->my_mixerdev = m;
sb_mixer_reset(devc);
return 1;
}
void sb_mixer_unload(sb_devc *devc)
{
if (devc->my_mixerdev == -1)
return;
kfree(mixer_devs[devc->my_mixerdev]);
sound_unload_mixerdev(devc->my_mixerdev);
sbmixnum--;
}

105
sound/oss/sb_mixer.h Normal file
View file

@ -0,0 +1,105 @@
/*
* sound/oss/sb_mixer.h
*
* Definitions for the SB Pro and SB16 mixers
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Modified:
* Hunyue Yau Jan 6 1994
* Added defines for the Sound Galaxy NX Pro mixer.
*
* Rolf Fokkens Dec 20 1998
* Added defines for some ES188x chips.
*
* Rolf Fokkens Dec 27 1998
* Moved static stuff to sb_mixer.c
*
*/
/*
* Mixer registers
*
* NOTE! RECORD_SRC == IN_FILTER
*/
/*
* Mixer registers of SB Pro
*/
#define VOC_VOL 0x04
#define MIC_VOL 0x0A
#define MIC_MIX 0x0A
#define RECORD_SRC 0x0C
#define IN_FILTER 0x0C
#define OUT_FILTER 0x0E
#define MASTER_VOL 0x22
#define FM_VOL 0x26
#define CD_VOL 0x28
#define LINE_VOL 0x2E
#define IRQ_NR 0x80
#define DMA_NR 0x81
#define IRQ_STAT 0x82
#define OPSW 0x3c
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
#define FILT_OFF (1 << 5)
#define MONO_DAC 0x00
#define STEREO_DAC 0x02
/*
* Mixer registers of SB16
*/
#define SB16_OMASK 0x3c
#define SB16_IMASK_L 0x3d
#define SB16_IMASK_R 0x3e
#define LEFT_CHN 0
#define RIGHT_CHN 1
/*
* 3DSE register of AWE32/64
*/
#define AWE_3DSE 0x90
/*
* Mixer registers of ALS007
*/
#define ALS007_RECORD_SRC 0x6c
#define ALS007_OUTPUT_CTRL1 0x3c
#define ALS007_OUTPUT_CTRL2 0x4c
#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
{{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
/*
* Recording sources (SB Pro)
*/
#define SRC__MIC 1 /* Select Microphone recording source */
#define SRC__CD 3 /* Select CD recording source */
#define SRC__LINE 7 /* Use Line-in for recording source */
/*
* Recording sources for ALS-007
*/
#define ALS007_MIC 4
#define ALS007_LINE 6
#define ALS007_CD 2
#define ALS007_SYNTH 7

1669
sound/oss/sequencer.c Normal file

File diff suppressed because it is too large Load diff

18
sound/oss/sleep.h Normal file
View file

@ -0,0 +1,18 @@
#include <linux/wait.h>
/*
* Do not use. This is a replacement for the old
* "interruptible_sleep_on_timeout" function that has been
* deprecated for ages. All users should instead try to use
* wait_event_interruptible_timeout.
*/
static inline long
oss_broken_sleep_on(wait_queue_head_t *q, long timeout)
{
DEFINE_WAIT(wait);
prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE);
timeout = schedule_timeout(timeout);
finish_wait(q, &wait);
return timeout;
}

87
sound/oss/sound_calls.h Normal file
View file

@ -0,0 +1,87 @@
/*
* DMA buffer calls
*/
int DMAbuf_open(int dev, int mode);
int DMAbuf_release(int dev, int mode);
int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
int DMAbuf_rmchars(int dev, int buff_no, int c);
int DMAbuf_start_output(int dev, int buff_no, int l);
int DMAbuf_move_wrpointer(int dev, int l);
/* int DMAbuf_ioctl(int dev, unsigned int cmd, void __user *arg, int local); */
void DMAbuf_init(int dev, int dma1, int dma2);
void DMAbuf_deinit(int dev);
int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
void DMAbuf_inputintr(int dev);
void DMAbuf_outputintr(int dev, int underflow_flag);
struct dma_buffparms;
int DMAbuf_space_in_queue (int dev);
int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait);
void DMAbuf_start_devices(unsigned int devmask);
void DMAbuf_reset (int dev);
int DMAbuf_sync (int dev);
/*
* System calls for /dev/dsp and /dev/audio (audio.c)
*/
int audio_read (int dev, struct file *file, char __user *buf, int count);
int audio_write (int dev, struct file *file, const char __user *buf, int count);
int audio_open (int dev, struct file *file);
void audio_release (int dev, struct file *file);
int audio_ioctl (int dev, struct file *file,
unsigned int cmd, void __user *arg);
void audio_init_devices (void);
void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
/*
* System calls for the /dev/sequencer
*/
int sequencer_read (int dev, struct file *file, char __user *buf, int count);
int sequencer_write (int dev, struct file *file, const char __user *buf, int count);
int sequencer_open (int dev, struct file *file);
void sequencer_release (int dev, struct file *file);
int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait);
void sequencer_init (void);
void sequencer_unload (void);
void sequencer_timer(unsigned long dummy);
int note_to_freq(int note_num);
unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
int vibrato_bend);
void seq_input_event(unsigned char *event, int len);
void seq_copy_to_input (unsigned char *event, int len);
/*
* System calls for the /dev/midi
*/
int MIDIbuf_read (int dev, struct file *file, char __user *buf, int count);
int MIDIbuf_write (int dev, struct file *file, const char __user *buf, int count);
int MIDIbuf_open (int dev, struct file *file);
void MIDIbuf_release (int dev, struct file *file);
int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait);
int MIDIbuf_avail(int dev);
void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
/* From soundcard.c */
void request_sound_timer (int count);
void sound_stop_timer(void);
void conf_printf(char *name, struct address_info *hw_config);
void conf_printf2(char *name, int base, int irq, int dma, int dma2);
/* From sound_timer.c */
void sound_timer_interrupt(void);
void sound_timer_syncinterval(unsigned int new_usecs);
/* From midi_synth.c */
void do_midi_msg (int synthno, unsigned char *msg, int mlen);

143
sound/oss/sound_config.h Normal file
View file

@ -0,0 +1,143 @@
/* sound_config.h
*
* A driver for sound cards, misc. configuration parameters.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
#ifndef _SOUND_CONFIG_H_
#define _SOUND_CONFIG_H_
#include <linux/fs.h>
#include <linux/sound.h>
#include "os.h"
#include "soundvers.h"
#ifndef SND_DEFAULT_ENABLE
#define SND_DEFAULT_ENABLE 1
#endif
#ifndef MAX_REALTIME_FACTOR
#define MAX_REALTIME_FACTOR 4
#endif
/*
* Use always 64k buffer size. There is no reason to use shorter.
*/
#undef DSP_BUFFSIZE
#define DSP_BUFFSIZE (64*1024)
#ifndef DSP_BUFFCOUNT
#define DSP_BUFFCOUNT 1 /* 1 is recommended. */
#endif
#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
#ifndef CONFIG_PAS_BASE
#define CONFIG_PAS_BASE 0x388
#endif
/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
driver. (There is no need to alter this) */
#define SEQ_MAX_QUEUE 1024
#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */
/* 128 instruments for general MIDI setup and 16 unassigned */
#define SND_NDEVS 256 /* Number of supported devices */
#define DSP_DEFAULT_SPEED 8000
#define MAX_AUDIO_DEV 5
#define MAX_MIXER_DEV 5
#define MAX_SYNTH_DEV 5
#define MAX_MIDI_DEV 6
#define MAX_TIMER_DEV 4
struct address_info {
int io_base;
int irq;
int dma;
int dma2;
int always_detect; /* 1=Trust me, it's there */
char *name;
int driver_use_1; /* Driver defined field 1 */
int driver_use_2; /* Driver defined field 2 */
int *osp; /* OS specific info */
int card_subtype; /* Driver specific. Usually 0 */
void *memptr; /* Module memory chainer */
int slots[6]; /* To remember driver slot ids */
};
#define SYNTH_MAX_VOICES 32
struct voice_alloc_info {
int max_voice;
int used_voices;
int ptr; /* For device specific use */
unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
int timestamp;
int alloc_times[SYNTH_MAX_VOICES];
};
struct channel_info {
int pgm_num;
int bender_value;
int bender_range;
unsigned char controllers[128];
};
/*
* Process wakeup reasons
*/
#define WK_NONE 0x00
#define WK_WAKEUP 0x01
#define WK_TIMEOUT 0x02
#define WK_SIGNAL 0x04
#define WK_SLEEP 0x08
#define WK_SELECT 0x10
#define WK_ABORT 0x20
#define OPEN_READ PCM_ENABLE_INPUT
#define OPEN_WRITE PCM_ENABLE_OUTPUT
#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE)
static inline int translate_mode(struct file *file)
{
if (OPEN_READ == (__force int)FMODE_READ &&
OPEN_WRITE == (__force int)FMODE_WRITE)
return (__force int)(file->f_mode & (FMODE_READ | FMODE_WRITE));
else
return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) |
((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0);
}
#include "sound_calls.h"
#include "dev_table.h"
#ifndef DDB
#define DDB(x) do {} while (0)
#endif
#ifndef MDB
#ifdef MODULE
#define MDB(x) x
#else
#define MDB(x)
#endif
#endif
#define TIMER_ARMED 121234
#define TIMER_NOT_ARMED 1
#define MAX_MEM_BLOCKS 1024
#endif

View file

@ -0,0 +1,2 @@
extern int mod_firmware_load(const char *fn, char **fp);

327
sound/oss/sound_timer.c Normal file
View file

@ -0,0 +1,327 @@
/*
* sound/oss/sound_timer.c
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
*/
#include <linux/string.h>
#include <linux/spinlock.h>
#include "sound_config.h"
static volatile int initialized, opened, tmr_running;
static volatile time_t tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
static volatile unsigned long next_event_time;
static unsigned long prev_event_time;
static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
static struct sound_lowlev_timer *tmr;
static DEFINE_SPINLOCK(lock);
static unsigned long tmr2ticks(int tmr_value)
{
/*
* Convert timer ticks to MIDI ticks
*/
unsigned long tmp;
unsigned long scale;
tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
return (tmp + (scale / 2)) / scale;
}
void reprogram_timer(void)
{
unsigned long usecs_per_tick;
/*
* The user is changing the timer rate before setting a timer
* slap, bad bad not allowed.
*/
if(!tmr)
return;
usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
/*
* Don't kill the system by setting too high timer rate
*/
if (usecs_per_tick < 2000)
usecs_per_tick = 2000;
usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
}
void sound_timer_syncinterval(unsigned int new_usecs)
{
/*
* This routine is called by the hardware level if
* the clock frequency has changed for some reason.
*/
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks(tmr_ctr);
tmr_ctr = 0;
usecs_per_tmr = new_usecs;
}
EXPORT_SYMBOL(sound_timer_syncinterval);
static void tmr_reset(void)
{
unsigned long flags;
spin_lock_irqsave(&lock,flags);
tmr_offs = 0;
ticks_offs = 0;
tmr_ctr = 0;
next_event_time = (unsigned long) -1;
prev_event_time = 0;
curr_ticks = 0;
spin_unlock_irqrestore(&lock,flags);
}
static int timer_open(int dev, int mode)
{
if (opened)
return -EBUSY;
tmr_reset();
curr_tempo = 60;
curr_timebase = 100;
opened = 1;
reprogram_timer();
return 0;
}
static void timer_close(int dev)
{
opened = tmr_running = 0;
tmr->tmr_disable(tmr->dev);
}
static int timer_event(int dev, unsigned char *event)
{
unsigned char cmd = event[1];
unsigned long parm = *(int *) &event[4];
switch (cmd)
{
case TMR_WAIT_REL:
parm += prev_event_time;
case TMR_WAIT_ABS:
if (parm > 0)
{
long time;
if (parm <= curr_ticks) /* It's the time */
return TIMER_NOT_ARMED;
time = parm;
next_event_time = prev_event_time = time;
return TIMER_ARMED;
}
break;
case TMR_START:
tmr_reset();
tmr_running = 1;
reprogram_timer();
break;
case TMR_STOP:
tmr_running = 0;
break;
case TMR_CONTINUE:
tmr_running = 1;
reprogram_timer();
break;
case TMR_TEMPO:
if (parm)
{
if (parm < 8)
parm = 8;
if (parm > 250)
parm = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks(tmr_ctr);
tmr_ctr = 0;
curr_tempo = parm;
reprogram_timer();
}
break;
case TMR_ECHO:
seq_copy_to_input(event, 8);
break;
default:;
}
return TIMER_NOT_ARMED;
}
static unsigned long timer_get_time(int dev)
{
if (!opened)
return 0;
return curr_ticks;
}
static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
{
int __user *p = arg;
int val;
switch (cmd)
{
case SNDCTL_TMR_SOURCE:
val = TMR_INTERNAL;
break;
case SNDCTL_TMR_START:
tmr_reset();
tmr_running = 1;
return 0;
case SNDCTL_TMR_STOP:
tmr_running = 0;
return 0;
case SNDCTL_TMR_CONTINUE:
tmr_running = 1;
return 0;
case SNDCTL_TMR_TIMEBASE:
if (get_user(val, p))
return -EFAULT;
if (val)
{
if (val < 1)
val = 1;
if (val > 1000)
val = 1000;
curr_timebase = val;
}
val = curr_timebase;
break;
case SNDCTL_TMR_TEMPO:
if (get_user(val, p))
return -EFAULT;
if (val)
{
if (val < 8)
val = 8;
if (val > 250)
val = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks(tmr_ctr);
tmr_ctr = 0;
curr_tempo = val;
reprogram_timer();
}
val = curr_tempo;
break;
case SNDCTL_SEQ_CTRLRATE:
if (get_user(val, p))
return -EFAULT;
if (val != 0) /* Can't change */
return -EINVAL;
val = ((curr_tempo * curr_timebase) + 30) / 60;
break;
case SNDCTL_SEQ_GETTIME:
val = curr_ticks;
break;
case SNDCTL_TMR_METRONOME:
default:
return -EINVAL;
}
return put_user(val, p);
}
static void timer_arm(int dev, long time)
{
if (time < 0)
time = curr_ticks + 1;
else if (time <= curr_ticks) /* It's the time */
return;
next_event_time = prev_event_time = time;
return;
}
static struct sound_timer_operations sound_timer =
{
.owner = THIS_MODULE,
.info = {"Sound Timer", 0},
.priority = 1, /* Priority */
.devlink = 0, /* Local device link */
.open = timer_open,
.close = timer_close,
.event = timer_event,
.get_time = timer_get_time,
.ioctl = timer_ioctl,
.arm_timer = timer_arm
};
void sound_timer_interrupt(void)
{
unsigned long flags;
if (!opened)
return;
tmr->tmr_restart(tmr->dev);
if (!tmr_running)
return;
spin_lock_irqsave(&lock,flags);
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
if (curr_ticks >= next_event_time)
{
next_event_time = (unsigned long) -1;
sequencer_timer(0);
}
spin_unlock_irqrestore(&lock,flags);
}
EXPORT_SYMBOL(sound_timer_interrupt);
void sound_timer_init(struct sound_lowlev_timer *t, char *name)
{
int n;
if (initialized)
{
if (t->priority <= tmr->priority)
return; /* There is already a similar or better timer */
tmr = t;
return;
}
initialized = 1;
tmr = t;
n = sound_alloc_timerdev();
if (n == -1)
n = 0; /* Overwrite the system timer */
strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name));
sound_timer_devs[n] = &sound_timer;
}
EXPORT_SYMBOL(sound_timer_init);

733
sound/oss/soundcard.c Normal file
View file

@ -0,0 +1,733 @@
/*
* linux/sound/oss/soundcard.c
*
* Sound card driver for Linux
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* integrated sound_switch.c
* Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat,
* which should disappear in the near future)
* Eric Dumas : devfs support (22-Jan-98) <dumas@linux.eu.org> with
* fixups by C. Scott Ananian <cananian@alumni.princeton.edu>
* Richard Gooch : moved common (non OSS-specific) devices to sound_core.c
* Rob Riggs : Added persistent DMA buffers support (1998/10/17)
* Christoph Hellwig : Some cleanup work (2000/03/01)
*/
#include "sound_config.h"
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/ctype.h>
#include <linux/stddef.h>
#include <linux/kmod.h>
#include <linux/kernel.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/device.h>
/*
* This ought to be moved into include/asm/dma.h
*/
#ifndef valid_dma
#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4)
#endif
/*
* Table for permanently allocated memory (used when unloading the module)
*/
void * sound_mem_blocks[MAX_MEM_BLOCKS];
static DEFINE_MUTEX(soundcard_mutex);
int sound_nblocks = 0;
/* Persistent DMA buffers */
#ifdef CONFIG_SOUND_DMAP
int sound_dmap_flag = 1;
#else
int sound_dmap_flag = 0;
#endif
static char dma_alloc_map[MAX_DMA_CHANNELS];
#define DMA_MAP_UNAVAIL 0
#define DMA_MAP_FREE 1
#define DMA_MAP_BUSY 2
unsigned long seq_time = 0; /* Time for /dev/sequencer */
extern struct class *sound_class;
/*
* Table for configurable mixer volume handling
*/
static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
static int num_mixer_volumes;
int *load_mixer_volumes(char *name, int *levels, int present)
{
int i, n;
for (i = 0; i < num_mixer_volumes; i++) {
if (strncmp(name, mixer_vols[i].name, 32) == 0) {
if (present)
mixer_vols[i].num = i;
return mixer_vols[i].levels;
}
}
if (num_mixer_volumes >= MAX_MIXER_DEV) {
printk(KERN_ERR "Sound: Too many mixers (%s)\n", name);
return levels;
}
n = num_mixer_volumes++;
strncpy(mixer_vols[n].name, name, 32);
if (present)
mixer_vols[n].num = n;
else
mixer_vols[n].num = -1;
for (i = 0; i < 32; i++)
mixer_vols[n].levels[i] = levels[i];
return mixer_vols[n].levels;
}
EXPORT_SYMBOL(load_mixer_volumes);
static int set_mixer_levels(void __user * arg)
{
/* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
mixer_vol_table buf;
if (__copy_from_user(&buf, arg, sizeof(buf)))
return -EFAULT;
load_mixer_volumes(buf.name, buf.levels, 0);
if (__copy_to_user(arg, &buf, sizeof(buf)))
return -EFAULT;
return 0;
}
static int get_mixer_levels(void __user * arg)
{
int n;
if (__get_user(n, (int __user *)(&(((mixer_vol_table __user *)arg)->num))))
return -EFAULT;
if (n < 0 || n >= num_mixer_volumes)
return -EINVAL;
if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table)))
return -EFAULT;
return 0;
}
/* 4K page size but our output routines use some slack for overruns */
#define PROC_BLOCK_SIZE (3*1024)
static ssize_t sound_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int dev = iminor(file_inode(file));
int ret = -EINVAL;
/*
* The OSS drivers aren't remotely happy without this locking,
* and unless someone fixes them when they are about to bite the
* big one anyway, we might as well bandage here..
*/
mutex_lock(&soundcard_mutex);
switch (dev & 0x0f) {
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
ret = audio_read(dev, file, buf, count);
break;
case SND_DEV_SEQ:
case SND_DEV_SEQ2:
ret = sequencer_read(dev, file, buf, count);
break;
case SND_DEV_MIDIN:
ret = MIDIbuf_read(dev, file, buf, count);
}
mutex_unlock(&soundcard_mutex);
return ret;
}
static ssize_t sound_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int dev = iminor(file_inode(file));
int ret = -EINVAL;
mutex_lock(&soundcard_mutex);
switch (dev & 0x0f) {
case SND_DEV_SEQ:
case SND_DEV_SEQ2:
ret = sequencer_write(dev, file, buf, count);
break;
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
ret = audio_write(dev, file, buf, count);
break;
case SND_DEV_MIDIN:
ret = MIDIbuf_write(dev, file, buf, count);
break;
}
mutex_unlock(&soundcard_mutex);
return ret;
}
static int sound_open(struct inode *inode, struct file *file)
{
int dev = iminor(inode);
int retval;
if ((dev >= SND_NDEVS) || (dev < 0)) {
printk(KERN_ERR "Invalid minor device %d\n", dev);
return -ENXIO;
}
mutex_lock(&soundcard_mutex);
switch (dev & 0x0f) {
case SND_DEV_CTL:
dev >>= 4;
if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
request_module("mixer%d", dev);
}
retval = -ENXIO;
if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
break;
if (!try_module_get(mixer_devs[dev]->owner))
break;
retval = 0;
break;
case SND_DEV_SEQ:
case SND_DEV_SEQ2:
retval = sequencer_open(dev, file);
break;
case SND_DEV_MIDIN:
retval = MIDIbuf_open(dev, file);
break;
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
retval = audio_open(dev, file);
break;
default:
printk(KERN_ERR "Invalid minor device %d\n", dev);
retval = -ENXIO;
}
mutex_unlock(&soundcard_mutex);
return retval;
}
static int sound_release(struct inode *inode, struct file *file)
{
int dev = iminor(inode);
mutex_lock(&soundcard_mutex);
switch (dev & 0x0f) {
case SND_DEV_CTL:
module_put(mixer_devs[dev >> 4]->owner);
break;
case SND_DEV_SEQ:
case SND_DEV_SEQ2:
sequencer_release(dev, file);
break;
case SND_DEV_MIDIN:
MIDIbuf_release(dev, file);
break;
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
audio_release(dev, file);
break;
default:
printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
}
mutex_unlock(&soundcard_mutex);
return 0;
}
static int get_mixer_info(int dev, void __user *arg)
{
mixer_info info;
memset(&info, 0, sizeof(info));
strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
info.modify_counter = mixer_devs[dev]->modify_counter;
if (__copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int get_old_mixer_info(int dev, void __user *arg)
{
_old_mixer_info info;
memset(&info, 0, sizeof(info));
strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg)
{
if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
return -ENXIO;
/* Try to load the mixer... */
if (mixer_devs[mixdev] == NULL) {
request_module("mixer%d", mixdev);
}
if (mixdev >= num_mixers || !mixer_devs[mixdev])
return -ENXIO;
if (cmd == SOUND_MIXER_INFO)
return get_mixer_info(mixdev, arg);
if (cmd == SOUND_OLD_MIXER_INFO)
return get_old_mixer_info(mixdev, arg);
if (_SIOC_DIR(cmd) & _SIOC_WRITE)
mixer_devs[mixdev]->modify_counter++;
if (!mixer_devs[mixdev]->ioctl)
return -EINVAL;
return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
}
static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int len = 0, dtype;
int dev = iminor(file_inode(file));
long ret = -EINVAL;
void __user *p = (void __user *)arg;
if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
/*
* Have to validate the address given by the process.
*/
len = _SIOC_SIZE(cmd);
if (len < 1 || len > 65536 || !p)
return -EFAULT;
if (_SIOC_DIR(cmd) & _SIOC_WRITE)
if (!access_ok(VERIFY_READ, p, len))
return -EFAULT;
if (_SIOC_DIR(cmd) & _SIOC_READ)
if (!access_ok(VERIFY_WRITE, p, len))
return -EFAULT;
}
if (cmd == OSS_GETVERSION)
return __put_user(SOUND_VERSION, (int __user *)p);
mutex_lock(&soundcard_mutex);
if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */
(dev & 0x0f) != SND_DEV_CTL) {
dtype = dev & 0x0f;
switch (dtype) {
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
ret = sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
cmd, p);
break;
default:
ret = sound_mixer_ioctl(dev >> 4, cmd, p);
break;
}
mutex_unlock(&soundcard_mutex);
return ret;
}
switch (dev & 0x0f) {
case SND_DEV_CTL:
if (cmd == SOUND_MIXER_GETLEVELS)
ret = get_mixer_levels(p);
else if (cmd == SOUND_MIXER_SETLEVELS)
ret = set_mixer_levels(p);
else
ret = sound_mixer_ioctl(dev >> 4, cmd, p);
break;
case SND_DEV_SEQ:
case SND_DEV_SEQ2:
ret = sequencer_ioctl(dev, file, cmd, p);
break;
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
ret = audio_ioctl(dev, file, cmd, p);
break;
case SND_DEV_MIDIN:
ret = MIDIbuf_ioctl(dev, file, cmd, p);
break;
}
mutex_unlock(&soundcard_mutex);
return ret;
}
static unsigned int sound_poll(struct file *file, poll_table * wait)
{
struct inode *inode = file_inode(file);
int dev = iminor(inode);
switch (dev & 0x0f) {
case SND_DEV_SEQ:
case SND_DEV_SEQ2:
return sequencer_poll(dev, file, wait);
case SND_DEV_MIDIN:
return MIDIbuf_poll(dev, file, wait);
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
return DMAbuf_poll(file, dev >> 4, wait);
}
return 0;
}
static int sound_mmap(struct file *file, struct vm_area_struct *vma)
{
int dev_class;
unsigned long size;
struct dma_buffparms *dmap = NULL;
int dev = iminor(file_inode(file));
dev_class = dev & 0x0f;
dev >>= 4;
if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) {
printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
return -EINVAL;
}
mutex_lock(&soundcard_mutex);
if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */
dmap = audio_devs[dev]->dmap_out;
else if (vma->vm_flags & VM_READ)
dmap = audio_devs[dev]->dmap_in;
else {
printk(KERN_ERR "Sound: Undefined mmap() access\n");
mutex_unlock(&soundcard_mutex);
return -EINVAL;
}
if (dmap == NULL) {
printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
mutex_unlock(&soundcard_mutex);
return -EIO;
}
if (dmap->raw_buf == NULL) {
printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
mutex_unlock(&soundcard_mutex);
return -EIO;
}
if (dmap->mapping_flags) {
printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
mutex_unlock(&soundcard_mutex);
return -EIO;
}
if (vma->vm_pgoff != 0) {
printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
mutex_unlock(&soundcard_mutex);
return -EINVAL;
}
size = vma->vm_end - vma->vm_start;
if (size != dmap->bytes_in_use) {
printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
}
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
mutex_unlock(&soundcard_mutex);
return -EAGAIN;
}
dmap->mapping_flags |= DMA_MAP_MAPPED;
if( audio_devs[dev]->d->mmap)
audio_devs[dev]->d->mmap(dev);
memset(dmap->raw_buf,
dmap->neutral_byte,
dmap->bytes_in_use);
mutex_unlock(&soundcard_mutex);
return 0;
}
const struct file_operations oss_sound_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = sound_read,
.write = sound_write,
.poll = sound_poll,
.unlocked_ioctl = sound_ioctl,
.mmap = sound_mmap,
.open = sound_open,
.release = sound_release,
};
/*
* Create the required special subdevices
*/
static int create_special_devices(void)
{
int seq1,seq2;
seq1=register_sound_special(&oss_sound_fops, 1);
if(seq1==-1)
goto bad;
seq2=register_sound_special(&oss_sound_fops, 8);
if(seq2!=-1)
return 0;
unregister_sound_special(1);
bad:
return -1;
}
static int dmabuf;
static int dmabug;
module_param(dmabuf, int, 0444);
module_param(dmabug, int, 0444);
/* additional minors for compatibility */
struct oss_minor_dev {
unsigned short minor;
unsigned int enabled;
} dev_list[] = {
{ SND_DEV_DSP16 },
{ SND_DEV_AUDIO },
};
static int __init oss_init(void)
{
int err;
int i, j;
#ifdef CONFIG_PCI
if(dmabug)
isa_dma_bridge_buggy = dmabug;
#endif
err = create_special_devices();
if (err) {
printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
return err;
}
/* Protecting the innocent */
sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
j = 0;
do {
unsigned short minor = dev_list[i].minor + j * 0x10;
if (!register_sound_special(&oss_sound_fops, minor))
dev_list[i].enabled = (1 << j);
} while (++j < num_audiodevs);
}
if (sound_nblocks >= MAX_MEM_BLOCKS - 1)
printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
return 0;
}
static void __exit oss_cleanup(void)
{
int i, j;
for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
j = 0;
do {
if (dev_list[i].enabled & (1 << j))
unregister_sound_special(dev_list[i].minor);
} while (++j < num_audiodevs);
}
unregister_sound_special(1);
unregister_sound_special(8);
sound_stop_timer();
sequencer_unload();
for (i = 0; i < MAX_DMA_CHANNELS; i++)
if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) {
printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i);
sound_free_dma(i);
}
for (i = 0; i < sound_nblocks; i++)
vfree(sound_mem_blocks[i]);
}
module_init(oss_init);
module_exit(oss_cleanup);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("OSS Sound subsystem");
MODULE_AUTHOR("Hannu Savolainen, et al.");
int sound_alloc_dma(int chn, char *deviceID)
{
int err;
if ((err = request_dma(chn, deviceID)) != 0)
return err;
dma_alloc_map[chn] = DMA_MAP_FREE;
return 0;
}
EXPORT_SYMBOL(sound_alloc_dma);
int sound_open_dma(int chn, char *deviceID)
{
if (!valid_dma(chn)) {
printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn);
return 1;
}
if (dma_alloc_map[chn] != DMA_MAP_FREE) {
printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
return 1;
}
dma_alloc_map[chn] = DMA_MAP_BUSY;
return 0;
}
EXPORT_SYMBOL(sound_open_dma);
void sound_free_dma(int chn)
{
if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) {
/* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */
return;
}
free_dma(chn);
dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
}
EXPORT_SYMBOL(sound_free_dma);
void sound_close_dma(int chn)
{
if (dma_alloc_map[chn] != DMA_MAP_BUSY) {
printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn);
return;
}
dma_alloc_map[chn] = DMA_MAP_FREE;
}
EXPORT_SYMBOL(sound_close_dma);
static void do_sequencer_timer(unsigned long dummy)
{
sequencer_timer(0);
}
static DEFINE_TIMER(seq_timer, do_sequencer_timer, 0, 0);
void request_sound_timer(int count)
{
extern unsigned long seq_time;
if (count < 0) {
seq_timer.expires = (-count) + jiffies;
add_timer(&seq_timer);
return;
}
count += seq_time;
count -= jiffies;
if (count < 1)
count = 1;
seq_timer.expires = (count) + jiffies;
add_timer(&seq_timer);
}
void sound_stop_timer(void)
{
del_timer(&seq_timer);
}
void conf_printf(char *name, struct address_info *hw_config)
{
#ifndef CONFIG_SOUND_TRACEINIT
return;
#else
printk("<%s> at 0x%03x", name, hw_config->io_base);
if (hw_config->irq)
printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
if (hw_config->dma != -1 || hw_config->dma2 != -1)
{
printk(" dma %d", hw_config->dma);
if (hw_config->dma2 != -1)
printk(",%d", hw_config->dma2);
}
printk("\n");
#endif
}
EXPORT_SYMBOL(conf_printf);
void conf_printf2(char *name, int base, int irq, int dma, int dma2)
{
#ifndef CONFIG_SOUND_TRACEINIT
return;
#else
printk("<%s> at 0x%03x", name, base);
if (irq)
printk(" irq %d", (irq > 0) ? irq : -irq);
if (dma != -1 || dma2 != -1)
{
printk(" dma %d", dma);
if (dma2 != -1)
printk(",%d", dma2);
}
printk("\n");
#endif
}
EXPORT_SYMBOL(conf_printf2);

2
sound/oss/soundvers.h Normal file
View file

@ -0,0 +1,2 @@
#define SOUND_VERSION_STRING "3.8s2++-971130"
#define SOUND_INTERNAL_VERSION 0x030804

2781
sound/oss/swarm_cs4297a.c Normal file

File diff suppressed because it is too large Load diff

285
sound/oss/sys_timer.c Normal file
View file

@ -0,0 +1,285 @@
/*
* sound/oss/sys_timer.c
*
* The default timer for the Level 2 sequencer interface
* Uses the (1/HZ sec) timer of kernel.
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow)
*/
#include <linux/spinlock.h>
#include "sound_config.h"
static volatile int opened, tmr_running;
static volatile time_t tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
static volatile unsigned long next_event_time;
static unsigned long prev_event_time;
static void poll_def_tmr(unsigned long dummy);
static DEFINE_SPINLOCK(lock);
static DEFINE_TIMER(def_tmr, poll_def_tmr, 0, 0);
static unsigned long
tmr2ticks(int tmr_value)
{
/*
* Convert timer ticks to MIDI ticks
*/
unsigned long tmp;
unsigned long scale;
/* tmr_value (ticks per sec) *
1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
tmp = tmr_value * (1000000 / HZ);
scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
return (tmp + scale / 2) / scale;
}
static void
poll_def_tmr(unsigned long dummy)
{
if (opened)
{
{
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
}
if (tmr_running)
{
spin_lock(&lock);
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
if (curr_ticks >= next_event_time)
{
next_event_time = (unsigned long) -1;
sequencer_timer(0);
}
spin_unlock(&lock);
}
}
}
static void
tmr_reset(void)
{
unsigned long flags;
spin_lock_irqsave(&lock,flags);
tmr_offs = 0;
ticks_offs = 0;
tmr_ctr = 0;
next_event_time = (unsigned long) -1;
prev_event_time = 0;
curr_ticks = 0;
spin_unlock_irqrestore(&lock,flags);
}
static int
def_tmr_open(int dev, int mode)
{
if (opened)
return -EBUSY;
tmr_reset();
curr_tempo = 60;
curr_timebase = 100;
opened = 1;
{
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
}
return 0;
}
static void
def_tmr_close(int dev)
{
opened = tmr_running = 0;
del_timer(&def_tmr);
}
static int
def_tmr_event(int dev, unsigned char *event)
{
unsigned char cmd = event[1];
unsigned long parm = *(int *) &event[4];
switch (cmd)
{
case TMR_WAIT_REL:
parm += prev_event_time;
case TMR_WAIT_ABS:
if (parm > 0)
{
long time;
if (parm <= curr_ticks) /* It's the time */
return TIMER_NOT_ARMED;
time = parm;
next_event_time = prev_event_time = time;
return TIMER_ARMED;
}
break;
case TMR_START:
tmr_reset();
tmr_running = 1;
break;
case TMR_STOP:
tmr_running = 0;
break;
case TMR_CONTINUE:
tmr_running = 1;
break;
case TMR_TEMPO:
if (parm)
{
if (parm < 8)
parm = 8;
if (parm > 360)
parm = 360;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks(tmr_ctr);
tmr_ctr = 0;
curr_tempo = parm;
}
break;
case TMR_ECHO:
seq_copy_to_input(event, 8);
break;
default:;
}
return TIMER_NOT_ARMED;
}
static unsigned long
def_tmr_get_time(int dev)
{
if (!opened)
return 0;
return curr_ticks;
}
/* same as sound_timer.c:timer_ioctl!? */
static int def_tmr_ioctl(int dev, unsigned int cmd, void __user *arg)
{
int __user *p = arg;
int val;
switch (cmd) {
case SNDCTL_TMR_SOURCE:
return __put_user(TMR_INTERNAL, p);
case SNDCTL_TMR_START:
tmr_reset();
tmr_running = 1;
return 0;
case SNDCTL_TMR_STOP:
tmr_running = 0;
return 0;
case SNDCTL_TMR_CONTINUE:
tmr_running = 1;
return 0;
case SNDCTL_TMR_TIMEBASE:
if (__get_user(val, p))
return -EFAULT;
if (val) {
if (val < 1)
val = 1;
if (val > 1000)
val = 1000;
curr_timebase = val;
}
return __put_user(curr_timebase, p);
case SNDCTL_TMR_TEMPO:
if (__get_user(val, p))
return -EFAULT;
if (val) {
if (val < 8)
val = 8;
if (val > 250)
val = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks(tmr_ctr);
tmr_ctr = 0;
curr_tempo = val;
reprogram_timer();
}
return __put_user(curr_tempo, p);
case SNDCTL_SEQ_CTRLRATE:
if (__get_user(val, p))
return -EFAULT;
if (val != 0) /* Can't change */
return -EINVAL;
val = ((curr_tempo * curr_timebase) + 30) / 60;
return __put_user(val, p);
case SNDCTL_SEQ_GETTIME:
return __put_user(curr_ticks, p);
case SNDCTL_TMR_METRONOME:
/* NOP */
break;
default:;
}
return -EINVAL;
}
static void
def_tmr_arm(int dev, long time)
{
if (time < 0)
time = curr_ticks + 1;
else if (time <= curr_ticks) /* It's the time */
return;
next_event_time = prev_event_time = time;
return;
}
struct sound_timer_operations default_sound_timer =
{
.owner = THIS_MODULE,
.info = {"System clock", 0},
.priority = 0, /* Priority */
.devlink = 0, /* Local device link */
.open = def_tmr_open,
.close = def_tmr_close,
.event = def_tmr_event,
.get_time = def_tmr_get_time,
.ioctl = def_tmr_ioctl,
.arm_timer = def_tmr_arm
};

525
sound/oss/trix.c Normal file
View file

@ -0,0 +1,525 @@
/*
* sound/oss/trix.c
*
* Low level driver for the MediaTrix AudioTrix Pro
* (MT-0002-PC Control Chip)
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
* Changes
* Alan Cox Modularisation, cleanup.
* Christoph Hellwig Adapted to module_init/module_exit
* Arnaldo C. de Melo Got rid of attach_uart401
*/
#include <linux/init.h>
#include <linux/module.h>
#include "sound_config.h"
#include "sb.h"
#include "sound_firmware.h"
#include "ad1848.h"
#include "mpu401.h"
#include "trix_boot.h"
static int mpu;
static bool joystick;
static unsigned char trix_read(int addr)
{
outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
return inb(0x391); /* MT-0002-PC ASIC data */
}
static void trix_write(int addr, int data)
{
outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */
}
static void download_boot(int base)
{
int i = 0, n = trix_boot_len;
if (trix_boot_len == 0)
return;
trix_write(0xf8, 0x00); /* ??????? */
outb((0x01), base + 6); /* Clear the internal data pointer */
outb((0x00), base + 6); /* Restart */
/*
* Write the boot code to the RAM upload/download register.
* Each write increments the internal data pointer.
*/
outb((0x01), base + 6); /* Clear the internal data pointer */
outb((0x1A), 0x390); /* Select RAM download/upload port */
for (i = 0; i < n; i++)
outb((trix_boot[i]), 0x391);
for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */
outb((0x00), 0x391);
outb((0x00), base + 6); /* Reset */
outb((0x50), 0x390); /* ?????? */
}
static int trix_set_wss_port(struct address_info *hw_config)
{
unsigned char addr_bits;
if (trix_read(0x15) != 0x71) /* No ASIC signature */
{
MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n"));
return 0;
}
/*
* Reset some registers.
*/
trix_write(0x13, 0);
trix_write(0x14, 0);
/*
* Configure the ASIC to place the codec to the proper I/O location
*/
switch (hw_config->io_base)
{
case 0x530:
addr_bits = 0;
break;
case 0x604:
addr_bits = 1;
break;
case 0xE80:
addr_bits = 2;
break;
case 0xF40:
addr_bits = 3;
break;
default:
return 0;
}
trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits);
return 1;
}
/*
* Probe and attach routines for the Windows Sound System mode of
* AudioTrix Pro
*/
static int __init init_trix_wss(struct address_info *hw_config)
{
static unsigned char dma_bits[4] = {
1, 2, 0, 3
};
struct resource *ports;
int config_port = hw_config->io_base + 0;
int dma1 = hw_config->dma, dma2 = hw_config->dma2;
int old_num_mixers = num_mixers;
u8 config, bits;
int ret;
switch(hw_config->irq) {
case 7:
bits = 8;
break;
case 9:
bits = 0x10;
break;
case 10:
bits = 0x18;
break;
case 11:
bits = 0x20;
break;
default:
printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
return 0;
}
switch (dma1) {
case 0:
case 1:
case 3:
break;
default:
printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", dma1);
return 0;
}
switch (dma2) {
case -1:
case 0:
case 1:
case 3:
break;
default:
printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", dma2);
return 0;
}
/*
* Check if the IO port returns valid signature. The original MS Sound
* system returns 0x04 while some cards (AudioTrix Pro for example)
* return 0x00.
*/
ports = request_region(hw_config->io_base + 4, 4, "ad1848");
if (!ports) {
printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
return 0;
}
if (!request_region(hw_config->io_base, 4, "MSS config")) {
printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
release_region(hw_config->io_base + 4, 4);
return 0;
}
if (!trix_set_wss_port(hw_config))
goto fail;
config = inb(hw_config->io_base + 3);
if ((config & 0x3f) != 0x00)
{
MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base));
goto fail;
}
/*
* Check that DMA0 is not in use with a 8 bit board.
*/
if (dma1 == 0 && config & 0x80)
{
printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
goto fail;
}
if (hw_config->irq > 9 && config & 0x80)
{
printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
goto fail;
}
ret = ad1848_detect(ports, NULL, hw_config->osp);
if (!ret)
goto fail;
if (joystick==1)
trix_write(0x15, 0x80);
/*
* Set the IRQ and DMA addresses.
*/
outb((bits | 0x40), config_port);
if (dma2 == -1 || dma2 == dma1)
{
bits |= dma_bits[dma1];
dma2 = dma1;
}
else
{
unsigned char tmp;
tmp = trix_read(0x13) & ~30;
trix_write(0x13, tmp | 0x80 | (dma1 << 4));
tmp = trix_read(0x14) & ~30;
trix_write(0x14, tmp | 0x80 | (dma2 << 4));
}
outb((bits), config_port); /* Write IRQ+DMA setup */
hw_config->slots[0] = ad1848_init("AudioTrix Pro", ports,
hw_config->irq,
dma1,
dma2,
0,
hw_config->osp,
THIS_MODULE);
if (num_mixers > old_num_mixers) /* Mixer got installed */
{
AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */
AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */
AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */
}
return 1;
fail:
release_region(hw_config->io_base, 4);
release_region(hw_config->io_base + 4, 4);
return 0;
}
static int __init probe_trix_sb(struct address_info *hw_config)
{
int tmp;
unsigned char conf;
extern int sb_be_quiet;
int old_quiet;
static signed char irq_translate[] = {
-1, -1, -1, 0, 1, 2, -1, 3
};
if (trix_boot_len == 0)
return 0; /* No boot code -> no fun */
if ((hw_config->io_base & 0xffffff8f) != 0x200)
return 0;
tmp = hw_config->irq;
if (tmp > 7)
return 0;
if (irq_translate[tmp] == -1)
return 0;
tmp = hw_config->dma;
if (tmp != 1 && tmp != 3)
return 0;
if (!request_region(hw_config->io_base, 16, "soundblaster")) {
printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
return 0;
}
conf = 0x84; /* DMA and IRQ enable */
conf |= hw_config->io_base & 0x70; /* I/O address bits */
conf |= irq_translate[hw_config->irq];
if (hw_config->dma == 3)
conf |= 0x08;
trix_write(0x1b, conf);
download_boot(hw_config->io_base);
hw_config->name = "AudioTrix SB";
if (!sb_dsp_detect(hw_config, 0, 0, NULL)) {
release_region(hw_config->io_base, 16);
return 0;
}
hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
/* Prevent false alarms */
old_quiet = sb_be_quiet;
sb_be_quiet = 1;
sb_dsp_init(hw_config, THIS_MODULE);
sb_be_quiet = old_quiet;
return 1;
}
static int __init probe_trix_mpu(struct address_info *hw_config)
{
unsigned char conf;
static int irq_bits[] = {
-1, -1, -1, 1, 2, 3, -1, 4, -1, 5
};
if (hw_config->irq > 9)
{
printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
return 0;
}
if (irq_bits[hw_config->irq] == -1)
{
printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
return 0;
}
switch (hw_config->io_base)
{
case 0x330:
conf = 0x00;
break;
case 0x370:
conf = 0x04;
break;
case 0x3b0:
conf = 0x08;
break;
case 0x3f0:
conf = 0x0c;
break;
default:
return 0; /* Invalid port */
}
conf |= irq_bits[hw_config->irq] << 4;
trix_write(0x19, (trix_read(0x19) & 0x83) | conf);
hw_config->name = "AudioTrix Pro";
return probe_uart401(hw_config, THIS_MODULE);
}
static void __exit unload_trix_wss(struct address_info *hw_config)
{
int dma2 = hw_config->dma2;
if (dma2 == -1)
dma2 = hw_config->dma;
release_region(0x390, 2);
release_region(hw_config->io_base, 4);
ad1848_unload(hw_config->io_base + 4,
hw_config->irq,
hw_config->dma,
dma2,
0);
sound_unload_audiodev(hw_config->slots[0]);
}
static inline void __exit unload_trix_mpu(struct address_info *hw_config)
{
unload_uart401(hw_config);
}
static inline void __exit unload_trix_sb(struct address_info *hw_config)
{
sb_dsp_unload(hw_config, mpu);
}
static struct address_info cfg;
static struct address_info cfg2;
static struct address_info cfg_mpu;
static int sb;
static int fw_load;
static int __initdata io = -1;
static int __initdata irq = -1;
static int __initdata dma = -1;
static int __initdata dma2 = -1; /* Set this for modules that need it */
static int __initdata sb_io = -1;
static int __initdata sb_dma = -1;
static int __initdata sb_irq = -1;
static int __initdata mpu_io = -1;
static int __initdata mpu_irq = -1;
module_param(io, int, 0);
module_param(irq, int, 0);
module_param(dma, int, 0);
module_param(dma2, int, 0);
module_param(sb_io, int, 0);
module_param(sb_dma, int, 0);
module_param(sb_irq, int, 0);
module_param(mpu_io, int, 0);
module_param(mpu_irq, int, 0);
module_param(joystick, bool, 0);
static int __init init_trix(void)
{
printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
cfg.io_base = io;
cfg.irq = irq;
cfg.dma = dma;
cfg.dma2 = dma2;
cfg2.io_base = sb_io;
cfg2.irq = sb_irq;
cfg2.dma = sb_dma;
cfg_mpu.io_base = mpu_io;
cfg_mpu.irq = mpu_irq;
if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
return -EINVAL;
}
if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) {
printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n");
return -EINVAL;
}
if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) {
printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n");
return -EINVAL;
}
if (!trix_boot)
{
fw_load = 1;
trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin",
(char **) &trix_boot);
}
if (!request_region(0x390, 2, "AudioTrix")) {
printk(KERN_ERR "AudioTrix: Config port I/O conflict\n");
return -ENODEV;
}
if (!init_trix_wss(&cfg)) {
release_region(0x390, 2);
return -ENODEV;
}
/*
* We must attach in the right order to get the firmware
* loaded up in time.
*/
if (cfg2.io_base != -1) {
sb = probe_trix_sb(&cfg2);
}
if (cfg_mpu.io_base != -1)
mpu = probe_trix_mpu(&cfg_mpu);
return 0;
}
static void __exit cleanup_trix(void)
{
if (fw_load && trix_boot)
vfree(trix_boot);
if (sb)
unload_trix_sb(&cfg2);
if (mpu)
unload_trix_mpu(&cfg_mpu);
unload_trix_wss(&cfg);
}
module_init(init_trix);
module_exit(cleanup_trix);
#ifndef MODULE
static int __init setup_trix (char *str)
{
/* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */
int ints[9];
str = get_options(str, ARRAY_SIZE(ints), ints);
io = ints[1];
irq = ints[2];
dma = ints[3];
dma2 = ints[4];
sb_io = ints[5];
sb_irq = ints[6];
sb_dma = ints[6];
mpu_io = ints[7];
mpu_irq = ints[8];
return 1;
}
__setup("trix=", setup_trix);
#endif
MODULE_LICENSE("GPL");

23
sound/oss/tuning.h Normal file
View file

@ -0,0 +1,23 @@
static 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
};
static 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
};

480
sound/oss/uart401.c Normal file
View file

@ -0,0 +1,480 @@
/*
* sound/oss/uart401.c
*
* MPU-401 UART driver (formerly uart401_midi.c)
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*
* Changes:
* Alan Cox Reformatted, removed sound_mem usage, use normal Linux
* interrupt allocation. Protect against bogus unload
* Fixed to allow IRQ > 15
* Christoph Hellwig Adapted to module_init/module_exit
* Arnaldo C. de Melo got rid of check_region
*
* Status:
* Untested
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "sound_config.h"
#include "mpu401.h"
struct uart401_devc
{
int base;
int irq;
int *osp;
void (*midi_input_intr) (int dev, unsigned char data);
int opened, disabled;
volatile unsigned char input_byte;
int my_dev;
int share_irq;
spinlock_t lock;
};
#define DATAPORT (devc->base)
#define COMDPORT (devc->base+1)
#define STATPORT (devc->base+1)
static int uart401_status(struct uart401_devc *devc)
{
return inb(STATPORT);
}
#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY))
static void uart401_cmd(struct uart401_devc *devc, unsigned char cmd)
{
outb((cmd), COMDPORT);
}
static int uart401_read(struct uart401_devc *devc)
{
return inb(DATAPORT);
}
static void uart401_write(struct uart401_devc *devc, unsigned char byte)
{
outb((byte), DATAPORT);
}
#define OUTPUT_READY 0x40
#define INPUT_AVAIL 0x80
#define MPU_ACK 0xFE
#define MPU_RESET 0xFF
#define UART_MODE_ON 0x3F
static int reset_uart401(struct uart401_devc *devc);
static void enter_uart_mode(struct uart401_devc *devc);
static void uart401_input_loop(struct uart401_devc *devc)
{
int work_limit=30000;
while (input_avail(devc) && --work_limit)
{
unsigned char c = uart401_read(devc);
if (c == MPU_ACK)
devc->input_byte = c;
else if (devc->opened & OPEN_READ && devc->midi_input_intr)
devc->midi_input_intr(devc->my_dev, c);
}
if(work_limit==0)
printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base);
}
irqreturn_t uart401intr(int irq, void *dev_id)
{
struct uart401_devc *devc = dev_id;
if (devc == NULL)
{
printk(KERN_ERR "uart401: bad devc\n");
return IRQ_NONE;
}
if (input_avail(devc))
uart401_input_loop(devc);
return IRQ_HANDLED;
}
static int
uart401_open(int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
struct uart401_devc *devc = (struct uart401_devc *)
midi_devs[dev]->devc;
if (devc->opened)
return -EBUSY;
/* Flush the UART */
while (input_avail(devc))
uart401_read(devc);
devc->midi_input_intr = input;
devc->opened = mode;
enter_uart_mode(devc);
devc->disabled = 0;
return 0;
}
static void uart401_close(int dev)
{
struct uart401_devc *devc = (struct uart401_devc *)
midi_devs[dev]->devc;
reset_uart401(devc);
devc->opened = 0;
}
static int uart401_out(int dev, unsigned char midi_byte)
{
int timeout;
unsigned long flags;
struct uart401_devc *devc = (struct uart401_devc *)
midi_devs[dev]->devc;
if (devc->disabled)
return 1;
/*
* Test for input since pending input seems to block the output.
*/
spin_lock_irqsave(&devc->lock,flags);
if (input_avail(devc))
uart401_input_loop(devc);
spin_unlock_irqrestore(&devc->lock,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(devc); timeout--);
if (!output_ready(devc))
{
printk(KERN_WARNING "uart401: Timeout - Device not responding\n");
devc->disabled = 1;
reset_uart401(devc);
enter_uart_mode(devc);
return 1;
}
uart401_write(devc, midi_byte);
return 1;
}
static inline int uart401_start_read(int dev)
{
return 0;
}
static inline int uart401_end_read(int dev)
{
return 0;
}
static inline void uart401_kick(int dev)
{
}
static inline int uart401_buffer_status(int dev)
{
return 0;
}
#define MIDI_SYNTH_NAME "MPU-401 UART"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static const struct midi_operations uart401_operations =
{
.owner = THIS_MODULE,
.info = {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
.converter = &std_midi_synth,
.in_info = {0},
.open = uart401_open,
.close = uart401_close,
.outputc = uart401_out,
.start_read = uart401_start_read,
.end_read = uart401_end_read,
.kick = uart401_kick,
.buffer_status = uart401_buffer_status,
};
static void enter_uart_mode(struct uart401_devc *devc)
{
int ok, timeout;
unsigned long flags;
spin_lock_irqsave(&devc->lock,flags);
for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
devc->input_byte = 0;
uart401_cmd(devc, UART_MODE_ON);
ok = 0;
for (timeout = 50000; timeout > 0 && !ok; timeout--)
if (devc->input_byte == MPU_ACK)
ok = 1;
else if (input_avail(devc))
if (uart401_read(devc) == MPU_ACK)
ok = 1;
spin_unlock_irqrestore(&devc->lock,flags);
}
static int reset_uart401(struct uart401_devc *devc)
{
int ok, timeout, n;
/*
* Send the RESET command. Try again if no success at the first time.
*/
ok = 0;
for (n = 0; n < 2 && !ok; n++)
{
for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
devc->input_byte = 0;
uart401_cmd(devc, MPU_RESET);
/*
* 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 (devc->input_byte == MPU_ACK) /* Interrupt */
ok = 1;
else if (input_avail(devc))
{
if (uart401_read(devc) == MPU_ACK)
ok = 1;
}
}
}
/* Flush input before enabling interrupts */
if (ok)
uart401_input_loop(devc);
else
DDB(printk("Reset UART401 failed - No hardware detected.\n"));
return ok;
}
int probe_uart401(struct address_info *hw_config, struct module *owner)
{
struct uart401_devc *devc;
char *name = "MPU-401 (UART) MIDI";
int ok = 0;
unsigned long flags;
DDB(printk("Entered probe_uart401()\n"));
/* Default to "not found" */
hw_config->slots[4] = -1;
if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) {
printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base);
return 0;
}
devc = kmalloc(sizeof(struct uart401_devc), GFP_KERNEL);
if (!devc) {
printk(KERN_WARNING "uart401: Can't allocate memory\n");
goto cleanup_region;
}
devc->base = hw_config->io_base;
devc->irq = hw_config->irq;
devc->osp = hw_config->osp;
devc->midi_input_intr = NULL;
devc->opened = 0;
devc->input_byte = 0;
devc->my_dev = 0;
devc->share_irq = 0;
spin_lock_init(&devc->lock);
spin_lock_irqsave(&devc->lock,flags);
ok = reset_uart401(devc);
spin_unlock_irqrestore(&devc->lock,flags);
if (!ok)
goto cleanup_devc;
if (hw_config->name)
name = hw_config->name;
if (devc->irq < 0) {
devc->share_irq = 1;
devc->irq *= -1;
} else
devc->share_irq = 0;
if (!devc->share_irq)
if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) {
printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
devc->share_irq = 1;
}
devc->my_dev = sound_alloc_mididev();
enter_uart_mode(devc);
if (devc->my_dev == -1) {
printk(KERN_INFO "uart401: Too many midi devices detected\n");
goto cleanup_irq;
}
conf_printf(name, hw_config);
midi_devs[devc->my_dev] = kmemdup(&uart401_operations,
sizeof(struct midi_operations),
GFP_KERNEL);
if (!midi_devs[devc->my_dev]) {
printk(KERN_ERR "uart401: Failed to allocate memory\n");
goto cleanup_unload_mididev;
}
if (owner)
midi_devs[devc->my_dev]->owner = owner;
midi_devs[devc->my_dev]->devc = devc;
midi_devs[devc->my_dev]->converter = kmemdup(&std_midi_synth,
sizeof(struct synth_operations),
GFP_KERNEL);
if (!midi_devs[devc->my_dev]->converter) {
printk(KERN_WARNING "uart401: Failed to allocate memory\n");
goto cleanup_midi_devs;
}
strcpy(midi_devs[devc->my_dev]->info.name, name);
midi_devs[devc->my_dev]->converter->id = "UART401";
midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev;
if (owner)
midi_devs[devc->my_dev]->converter->owner = owner;
hw_config->slots[4] = devc->my_dev;
sequencer_init();
devc->opened = 0;
return 1;
cleanup_midi_devs:
kfree(midi_devs[devc->my_dev]);
cleanup_unload_mididev:
sound_unload_mididev(devc->my_dev);
cleanup_irq:
if (!devc->share_irq)
free_irq(devc->irq, devc);
cleanup_devc:
kfree(devc);
cleanup_region:
release_region(hw_config->io_base, 4);
return 0;
}
void unload_uart401(struct address_info *hw_config)
{
struct uart401_devc *devc;
int n=hw_config->slots[4];
/* Not set up */
if(n==-1 || midi_devs[n]==NULL)
return;
/* Not allocated (erm ??) */
devc = midi_devs[hw_config->slots[4]]->devc;
if (devc == NULL)
return;
reset_uart401(devc);
release_region(hw_config->io_base, 4);
if (!devc->share_irq)
free_irq(devc->irq, devc);
if (devc)
{
kfree(midi_devs[devc->my_dev]->converter);
kfree(midi_devs[devc->my_dev]);
kfree(devc);
devc = NULL;
}
/* This kills midi_devs[x] */
sound_unload_mididev(hw_config->slots[4]);
}
EXPORT_SYMBOL(probe_uart401);
EXPORT_SYMBOL(unload_uart401);
EXPORT_SYMBOL(uart401intr);
static struct address_info cfg_mpu;
static int io = -1;
static int irq = -1;
module_param(io, int, 0444);
module_param(irq, int, 0444);
static int __init init_uart401(void)
{
cfg_mpu.irq = irq;
cfg_mpu.io_base = io;
/* Can be loaded either for module use or to provide functions
to others */
if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) {
printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
if (!probe_uart401(&cfg_mpu, THIS_MODULE))
return -ENODEV;
}
return 0;
}
static void __exit cleanup_uart401(void)
{
if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1)
unload_uart401(&cfg_mpu);
}
module_init(init_uart401);
module_exit(cleanup_uart401);
#ifndef MODULE
static int __init setup_uart401(char *str)
{
/* io, irq */
int ints[3];
str = get_options(str, ARRAY_SIZE(ints), ints);
io = ints[1];
irq = ints[2];
return 1;
}
__setup("uart401=", setup_uart401);
#endif
MODULE_LICENSE("GPL");

361
sound/oss/uart6850.c Normal file
View file

@ -0,0 +1,361 @@
/*
* sound/oss/uart6850.c
*
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
* Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
* 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
*
* Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now
* uses native linux resources
* Christoph Hellwig: Adapted to module_init/module_exit
* Jeff Garzik: Made it work again, in theory
* FIXME: If the request_irq() succeeds, the probe succeeds. Ug.
*
* Status: Testing required (no shit -jgarzik)
*
*
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/spinlock.h>
/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
* added 6850 support, used with COVOX SoundMaster II and custom cards.
*/
#include "sound_config.h"
static int uart6850_base = 0x330;
static int *uart6850_osp;
#define DATAPORT (uart6850_base)
#define COMDPORT (uart6850_base+1)
#define STATPORT (uart6850_base+1)
static int uart6850_status(void)
{
return inb(STATPORT);
}
#define input_avail() (uart6850_status()&INPUT_AVAIL)
#define output_ready() (uart6850_status()&OUTPUT_READY)
static void uart6850_cmd(unsigned char cmd)
{
outb(cmd, COMDPORT);
}
static int uart6850_read(void)
{
return inb(DATAPORT);
}
static void uart6850_write(unsigned char byte)
{
outb(byte, DATAPORT);
}
#define OUTPUT_READY 0x02 /* Mask for data ready Bit */
#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */
#define UART_RESET 0x95
#define UART_MODE_ON 0x03
static int uart6850_opened;
static int uart6850_irq;
static int uart6850_detected;
static int my_dev;
static DEFINE_SPINLOCK(lock);
static void (*midi_input_intr) (int dev, unsigned char data);
static void poll_uart6850(unsigned long dummy);
static DEFINE_TIMER(uart6850_timer, poll_uart6850, 0, 0);
static void uart6850_input_loop(void)
{
int count = 10;
while (count)
{
/*
* Not timed out
*/
if (input_avail())
{
unsigned char c = uart6850_read();
count = 100;
if (uart6850_opened & OPEN_READ)
midi_input_intr(my_dev, c);
}
else
{
while (!input_avail() && count)
count--;
}
}
}
static irqreturn_t m6850intr(int irq, void *dev_id)
{
if (input_avail())
uart6850_input_loop();
return IRQ_HANDLED;
}
/*
* It looks like there is no input interrupts in the UART mode. Let's try
* polling.
*/
static void poll_uart6850(unsigned long dummy)
{
unsigned long flags;
if (!(uart6850_opened & OPEN_READ))
return; /* Device has been closed */
spin_lock_irqsave(&lock,flags);
if (input_avail())
uart6850_input_loop();
uart6850_timer.expires = 1 + jiffies;
add_timer(&uart6850_timer);
/*
* Come back later
*/
spin_unlock_irqrestore(&lock,flags);
}
static int uart6850_open(int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
if (uart6850_opened)
{
/* printk("Midi6850: Midi busy\n");*/
return -EBUSY;
}
uart6850_cmd(UART_RESET);
uart6850_input_loop();
midi_input_intr = input;
uart6850_opened = mode;
poll_uart6850(0); /*
* Enable input polling
*/
return 0;
}
static void uart6850_close(int dev)
{
uart6850_cmd(UART_MODE_ON);
del_timer(&uart6850_timer);
uart6850_opened = 0;
}
static int uart6850_out(int dev, unsigned char midi_byte)
{
int timeout;
unsigned long flags;
/*
* Test for input since pending input seems to block the output.
*/
spin_lock_irqsave(&lock,flags);
if (input_avail())
uart6850_input_loop();
spin_unlock_irqrestore(&lock,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(KERN_WARNING "Midi6850: Timeout\n");
return 0;
}
uart6850_write(midi_byte);
return 1;
}
static inline int uart6850_command(int dev, unsigned char *midi_byte)
{
return 1;
}
static inline int uart6850_start_read(int dev)
{
return 0;
}
static inline int uart6850_end_read(int dev)
{
return 0;
}
static inline void uart6850_kick(int dev)
{
}
static inline int uart6850_buffer_status(int dev)
{
return 0; /*
* No data in buffers
*/
}
#define MIDI_SYNTH_NAME "6850 UART Midi"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static struct midi_operations uart6850_operations =
{
.owner = THIS_MODULE,
.info = {"6850 UART", 0, 0, SNDCARD_UART6850},
.converter = &std_midi_synth,
.in_info = {0},
.open = uart6850_open,
.close = uart6850_close,
.outputc = uart6850_out,
.start_read = uart6850_start_read,
.end_read = uart6850_end_read,
.kick = uart6850_kick,
.command = uart6850_command,
.buffer_status = uart6850_buffer_status
};
static void __init attach_uart6850(struct address_info *hw_config)
{
int ok, timeout;
unsigned long flags;
if (!uart6850_detected)
return;
if ((my_dev = sound_alloc_mididev()) == -1)
{
printk(KERN_INFO "uart6850: Too many midi devices detected\n");
return;
}
uart6850_base = hw_config->io_base;
uart6850_osp = hw_config->osp;
uart6850_irq = hw_config->irq;
spin_lock_irqsave(&lock,flags);
for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
* Wait
*/
uart6850_cmd(UART_MODE_ON);
ok = 1;
spin_unlock_irqrestore(&lock,flags);
conf_printf("6850 Midi Interface", hw_config);
std_midi_synth.midi_dev = my_dev;
hw_config->slots[4] = my_dev;
midi_devs[my_dev] = &uart6850_operations;
sequencer_init();
}
static inline int reset_uart6850(void)
{
uart6850_read();
return 1; /*
* OK
*/
}
static int __init probe_uart6850(struct address_info *hw_config)
{
int ok;
uart6850_osp = hw_config->osp;
uart6850_base = hw_config->io_base;
uart6850_irq = hw_config->irq;
if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0)
return 0;
ok = reset_uart6850();
uart6850_detected = ok;
return ok;
}
static void __exit unload_uart6850(struct address_info *hw_config)
{
free_irq(hw_config->irq, NULL);
sound_unload_mididev(hw_config->slots[4]);
}
static struct address_info cfg_mpu;
static int __initdata io = -1;
static int __initdata irq = -1;
module_param(io, int, 0);
module_param(irq, int, 0);
static int __init init_uart6850(void)
{
cfg_mpu.io_base = io;
cfg_mpu.irq = irq;
if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) {
printk(KERN_INFO "uart6850: irq and io must be set.\n");
return -EINVAL;
}
if (probe_uart6850(&cfg_mpu))
return -ENODEV;
attach_uart6850(&cfg_mpu);
return 0;
}
static void __exit cleanup_uart6850(void)
{
unload_uart6850(&cfg_mpu);
}
module_init(init_uart6850);
module_exit(cleanup_uart6850);
#ifndef MODULE
static int __init setup_uart6850(char *str)
{
/* io, irq */
int ints[3];
str = get_options(str, ARRAY_SIZE(ints), ints);
io = ints[1];
irq = ints[2];
return 1;
}
__setup("uart6850=", setup_uart6850);
#endif
MODULE_LICENSE("GPL");

69
sound/oss/ulaw.h Normal file
View file

@ -0,0 +1,69 @@
static unsigned char ulaw_dsp[] = {
3, 7, 11, 15, 19, 23, 27, 31,
35, 39, 43, 47, 51, 55, 59, 63,
66, 68, 70, 72, 74, 76, 78, 80,
82, 84, 86, 88, 90, 92, 94, 96,
98, 99, 100, 101, 102, 103, 104, 105,
106, 107, 108, 109, 110, 111, 112, 113,
113, 114, 114, 115, 115, 116, 116, 117,
117, 118, 118, 119, 119, 120, 120, 121,
121, 121, 122, 122, 122, 122, 123, 123,
123, 123, 124, 124, 124, 124, 125, 125,
125, 125, 125, 125, 126, 126, 126, 126,
126, 126, 126, 126, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
253, 249, 245, 241, 237, 233, 229, 225,
221, 217, 213, 209, 205, 201, 197, 193,
190, 188, 186, 184, 182, 180, 178, 176,
174, 172, 170, 168, 166, 164, 162, 160,
158, 157, 156, 155, 154, 153, 152, 151,
150, 149, 148, 147, 146, 145, 144, 143,
143, 142, 142, 141, 141, 140, 140, 139,
139, 138, 138, 137, 137, 136, 136, 135,
135, 135, 134, 134, 134, 134, 133, 133,
133, 133, 132, 132, 132, 132, 131, 131,
131, 131, 131, 131, 130, 130, 130, 130,
130, 130, 130, 130, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
};
static unsigned char dsp_ulaw[] = {
0, 0, 0, 0, 0, 1, 1, 1,
1, 2, 2, 2, 2, 3, 3, 3,
3, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7,
7, 8, 8, 8, 8, 9, 9, 9,
9, 10, 10, 10, 10, 11, 11, 11,
11, 12, 12, 12, 12, 13, 13, 13,
13, 14, 14, 14, 14, 15, 15, 15,
15, 16, 16, 17, 17, 18, 18, 19,
19, 20, 20, 21, 21, 22, 22, 23,
23, 24, 24, 25, 25, 26, 26, 27,
27, 28, 28, 29, 29, 30, 30, 31,
31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46,
47, 49, 51, 53, 55, 57, 59, 61,
63, 66, 70, 74, 78, 84, 92, 104,
254, 231, 219, 211, 205, 201, 197, 193,
190, 188, 186, 184, 182, 180, 178, 176,
175, 174, 173, 172, 171, 170, 169, 168,
167, 166, 165, 164, 163, 162, 161, 160,
159, 159, 158, 158, 157, 157, 156, 156,
155, 155, 154, 154, 153, 153, 152, 152,
151, 151, 150, 150, 149, 149, 148, 148,
147, 147, 146, 146, 145, 145, 144, 144,
143, 143, 143, 143, 142, 142, 142, 142,
141, 141, 141, 141, 140, 140, 140, 140,
139, 139, 139, 139, 138, 138, 138, 138,
137, 137, 137, 137, 136, 136, 136, 136,
135, 135, 135, 135, 134, 134, 134, 134,
133, 133, 133, 133, 132, 132, 132, 132,
131, 131, 131, 131, 130, 130, 130, 130,
129, 129, 129, 129, 128, 128, 128, 128,
};

290
sound/oss/v_midi.c Normal file
View file

@ -0,0 +1,290 @@
/*
* sound/oss/v_midi.c
*
* The low level driver for the Sound Blaster DS chips.
*
*
* Copyright (C) by Hannu Savolainen 1993-1996
*
* USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
* ??
*
* Changes
* Alan Cox Modularisation, changed memory allocations
* Christoph Hellwig Adapted to module_init/module_exit
*
* Status
* Untested
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "sound_config.h"
#include "v_midi.h"
static vmidi_devc *v_devc[2] = { NULL, NULL};
static int midi1,midi2;
static void *midi_mem = NULL;
/*
* The DSP channel can be used either for input or output. Variable
* 'sb_irq_mode' will be set when the program calls read or write first time
* after open. Current version doesn't support mode changes without closing
* and reopening the device. Support for this feature may be implemented in a
* future version of this driver.
*/
static int v_midi_open (int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
vmidi_devc *devc = midi_devs[dev]->devc;
unsigned long flags;
if (devc == NULL)
return -(ENXIO);
spin_lock_irqsave(&devc->lock,flags);
if (devc->opened)
{
spin_unlock_irqrestore(&devc->lock,flags);
return -(EBUSY);
}
devc->opened = 1;
spin_unlock_irqrestore(&devc->lock,flags);
devc->intr_active = 1;
if (mode & OPEN_READ)
{
devc->input_opened = 1;
devc->midi_input_intr = input;
}
return 0;
}
static void v_midi_close (int dev)
{
vmidi_devc *devc = midi_devs[dev]->devc;
unsigned long flags;
if (devc == NULL)
return;
spin_lock_irqsave(&devc->lock,flags);
devc->intr_active = 0;
devc->input_opened = 0;
devc->opened = 0;
spin_unlock_irqrestore(&devc->lock,flags);
}
static int v_midi_out (int dev, unsigned char midi_byte)
{
vmidi_devc *devc = midi_devs[dev]->devc;
vmidi_devc *pdevc;
if (devc == NULL)
return -ENXIO;
pdevc = midi_devs[devc->pair_mididev]->devc;
if (pdevc->input_opened > 0){
if (MIDIbuf_avail(pdevc->my_mididev) > 500)
return 0;
pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
}
return 1;
}
static inline int v_midi_start_read (int dev)
{
return 0;
}
static int v_midi_end_read (int dev)
{
vmidi_devc *devc = midi_devs[dev]->devc;
if (devc == NULL)
return -ENXIO;
devc->intr_active = 0;
return 0;
}
/* why -EPERM and not -EINVAL?? */
static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg)
{
return -EPERM;
}
#define MIDI_SYNTH_NAME "Loopback MIDI"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static struct midi_operations v_midi_operations =
{
.owner = THIS_MODULE,
.info = {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
.converter = &std_midi_synth,
.in_info = {0},
.open = v_midi_open,
.close = v_midi_close,
.ioctl = v_midi_ioctl,
.outputc = v_midi_out,
.start_read = v_midi_start_read,
.end_read = v_midi_end_read,
};
static struct midi_operations v_midi_operations2 =
{
.owner = THIS_MODULE,
.info = {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
.converter = &std_midi_synth,
.in_info = {0},
.open = v_midi_open,
.close = v_midi_close,
.ioctl = v_midi_ioctl,
.outputc = v_midi_out,
.start_read = v_midi_start_read,
.end_read = v_midi_end_read,
};
/*
* We kmalloc just one of these - it makes life simpler and the code
* cleaner and the memory handling far more efficient
*/
struct vmidi_memory
{
/* Must be first */
struct midi_operations m_ops[2];
struct synth_operations s_ops[2];
struct vmidi_devc v_ops[2];
};
static void __init attach_v_midi (struct address_info *hw_config)
{
struct vmidi_memory *m;
/* printk("Attaching v_midi device.....\n"); */
midi1 = sound_alloc_mididev();
if (midi1 == -1)
{
printk(KERN_ERR "v_midi: Too many midi devices detected\n");
return;
}
m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
if (m == NULL)
{
printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
sound_unload_mididev(midi1);
return;
}
midi_mem = m;
midi_devs[midi1] = &m->m_ops[0];
midi2 = sound_alloc_mididev();
if (midi2 == -1)
{
printk (KERN_ERR "v_midi: Too many midi devices detected\n");
kfree(m);
sound_unload_mididev(midi1);
return;
}
midi_devs[midi2] = &m->m_ops[1];
/* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */
/* for MIDI-1 */
v_devc[0] = &m->v_ops[0];
memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
sizeof (struct midi_operations));
v_devc[0]->my_mididev = midi1;
v_devc[0]->pair_mididev = midi2;
v_devc[0]->opened = v_devc[0]->input_opened = 0;
v_devc[0]->intr_active = 0;
v_devc[0]->midi_input_intr = NULL;
spin_lock_init(&v_devc[0]->lock);
midi_devs[midi1]->devc = v_devc[0];
midi_devs[midi1]->converter = &m->s_ops[0];
std_midi_synth.midi_dev = midi1;
memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
sizeof (struct synth_operations));
midi_devs[midi1]->converter->id = "V_MIDI 1";
/* for MIDI-2 */
v_devc[1] = &m->v_ops[1];
memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
sizeof (struct midi_operations));
v_devc[1]->my_mididev = midi2;
v_devc[1]->pair_mididev = midi1;
v_devc[1]->opened = v_devc[1]->input_opened = 0;
v_devc[1]->intr_active = 0;
v_devc[1]->midi_input_intr = NULL;
spin_lock_init(&v_devc[1]->lock);
midi_devs[midi2]->devc = v_devc[1];
midi_devs[midi2]->converter = &m->s_ops[1];
std_midi_synth.midi_dev = midi2;
memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
sizeof (struct synth_operations));
midi_devs[midi2]->converter->id = "V_MIDI 2";
sequencer_init();
/* printk("Attached v_midi device\n"); */
}
static inline int __init probe_v_midi(struct address_info *hw_config)
{
return(1); /* always OK */
}
static void __exit unload_v_midi(struct address_info *hw_config)
{
sound_unload_mididev(midi1);
sound_unload_mididev(midi2);
kfree(midi_mem);
}
static struct address_info cfg; /* dummy */
static int __init init_vmidi(void)
{
printk("MIDI Loopback device driver\n");
if (!probe_v_midi(&cfg))
return -ENODEV;
attach_v_midi(&cfg);
return 0;
}
static void __exit cleanup_vmidi(void)
{
unload_v_midi(&cfg);
}
module_init(init_vmidi);
module_exit(cleanup_vmidi);
MODULE_LICENSE("GPL");

14
sound/oss/v_midi.h Normal file
View file

@ -0,0 +1,14 @@
typedef struct vmidi_devc {
int dev;
/* State variables */
int opened;
spinlock_t lock;
/* MIDI fields */
int my_mididev;
int pair_mididev;
int input_opened;
int intr_active;
void (*midi_input_intr) (int dev, unsigned char data);
} vmidi_devc;

557
sound/oss/vidc.c Normal file
View file

@ -0,0 +1,557 @@
/*
* linux/drivers/sound/vidc.c
*
* Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* VIDC20 audio driver.
*
* The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
* engine. The DMA transfers fixed-format (16-bit little-endian linear)
* samples to the VIDC20, which then transfers this data serially to the
* DACs. The samplerate is controlled by the VIDC.
*
* We currently support a mixer device, but it is currently non-functional.
*/
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/hardware/iomd.h>
#include <asm/irq.h>
#include "sound_config.h"
#include "vidc.h"
#ifndef _SIOC_TYPE
#define _SIOC_TYPE(x) _IOC_TYPE(x)
#endif
#ifndef _SIOC_NR
#define _SIOC_NR(x) _IOC_NR(x)
#endif
#define VIDC_SOUND_CLOCK (250000)
#define VIDC_SOUND_CLOCK_EXT (176400)
/*
* When using SERIAL SOUND mode (external DAC), the number of physical
* channels is fixed at 2.
*/
static int vidc_busy;
static int vidc_adev;
static int vidc_audio_rate;
static char vidc_audio_format;
static char vidc_audio_channels;
static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = {
85, /* master */
50, /* bass */
50, /* treble */
0, /* synth */
75, /* pcm */
0, /* speaker */
100, /* ext line */
0, /* mic */
100, /* CD */
0,
};
static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = {
85, /* master */
50, /* bass */
50, /* treble */
0, /* synth */
75, /* pcm */
0, /* speaker */
100, /* ext line */
0, /* mic */
100, /* CD */
0,
};
static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */
static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */
extern void vidc_update_filler(int bits, int channels);
extern int softoss_dev;
static void
vidc_mixer_set(int mdev, unsigned int level)
{
unsigned int lev_l = level & 0x007f;
unsigned int lev_r = (level & 0x7f00) >> 8;
unsigned int mlev_l, mlev_r;
if (lev_l > 100)
lev_l = 100;
if (lev_r > 100)
lev_r = 100;
#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000)
mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
switch (mdev) {
case SOUND_MIXER_VOLUME:
case SOUND_MIXER_PCM:
vidc_level_l[mdev] = lev_l;
vidc_level_r[mdev] = lev_r;
vidc_audio_volume_l = SCALE(lev_l, mlev_l);
vidc_audio_volume_r = SCALE(lev_r, mlev_r);
/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
break;
}
#undef SCALE
}
static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
{
unsigned int val;
unsigned int mdev;
if (_SIOC_TYPE(cmd) != 'M')
return -EINVAL;
mdev = _SIOC_NR(cmd);
if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
if (get_user(val, (unsigned int __user *)arg))
return -EFAULT;
if (mdev < SOUND_MIXER_NRDEVICES)
vidc_mixer_set(mdev, val);
else
return -EINVAL;
}
/*
* Return parameters
*/
switch (mdev) {
case SOUND_MIXER_RECSRC:
val = 0;
break;
case SOUND_MIXER_DEVMASK:
val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
break;
case SOUND_MIXER_STEREODEVS:
val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
break;
case SOUND_MIXER_RECMASK:
val = 0;
break;
case SOUND_MIXER_CAPS:
val = 0;
break;
default:
if (mdev < SOUND_MIXER_NRDEVICES)
val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
else
return -EINVAL;
}
return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0;
}
static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
{
switch (fmt) {
default:
fmt = AFMT_S16_LE;
case AFMT_U8:
case AFMT_S8:
case AFMT_S16_LE:
vidc_audio_format = fmt;
vidc_update_filler(vidc_audio_format, vidc_audio_channels);
case AFMT_QUERY:
break;
}
return vidc_audio_format;
}
#define my_abs(i) ((i)<0 ? -(i) : (i))
static int vidc_audio_set_speed(int dev, int rate)
{
if (rate) {
unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
unsigned int diff_int, diff_ext;
unsigned int newsize, new2size;
hwctrl = 0x00000003;
/* Using internal clock */
hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
if (hwrate < 3)
hwrate = 3;
if (hwrate > 255)
hwrate = 255;
/* Using exernal clock */
hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
if (hwrate_ext < 3)
hwrate_ext = 3;
if (hwrate_ext > 255)
hwrate_ext = 255;
rate_int = VIDC_SOUND_CLOCK / hwrate;
rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
/* Chose between external and internal clock */
diff_int = my_abs(rate_ext-rate);
diff_ext = my_abs(rate_int-rate);
if (diff_ext < diff_int) {
/*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
hwrate=hwrate_ext;
hwctrl=0x00000002;
/* Allow roughly 0.4% tolerance */
if (diff_ext > (rate/256))
rate=rate_ext;
} else {
/*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
hwctrl=0x00000003;
/* Allow roughly 0.4% tolerance */
if (diff_int > (rate/256))
rate=rate_int;
}
vidc_writel(0xb0000000 | (hwrate - 2));
vidc_writel(0xb1000000 | hwctrl);
newsize = (10000 / hwrate) & ~3;
if (newsize < 208)
newsize = 208;
if (newsize > 4096)
newsize = 4096;
for (new2size = 128; new2size < newsize; new2size <<= 1);
if (new2size - newsize > newsize - (new2size >> 1))
new2size >>= 1;
if (new2size > 4096) {
printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
newsize, new2size);
new2size = 4096;
}
/*printk("VIDC: dma size %d\n", new2size);*/
dma_bufsize = new2size;
vidc_audio_rate = rate;
}
return vidc_audio_rate;
}
static short vidc_audio_set_channels(int dev, short channels)
{
switch (channels) {
default:
channels = 2;
case 1:
case 2:
vidc_audio_channels = channels;
vidc_update_filler(vidc_audio_format, vidc_audio_channels);
case 0:
break;
}
return vidc_audio_channels;
}
/*
* Open the device
*/
static int vidc_audio_open(int dev, int mode)
{
/* This audio device does not have recording capability */
if (mode == OPEN_READ)
return -EPERM;
if (vidc_busy)
return -EBUSY;
vidc_busy = 1;
return 0;
}
/*
* Close the device
*/
static void vidc_audio_close(int dev)
{
vidc_busy = 0;
}
/*
* Output a block via DMA to sound device.
*
* We just set the DMA start and count; the DMA interrupt routine
* will take care of formatting the samples (via the appropriate
* vidc_filler routine), and flag via vidc_audio_dma_interrupt when
* more data is required.
*/
static void
vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
{
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
unsigned long flags;
local_irq_save(flags);
dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
dma_count = total_count;
local_irq_restore(flags);
}
static void
vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
{
}
static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
{
return -EINVAL;
}
static irqreturn_t vidc_audio_dma_interrupt(void)
{
DMAbuf_outputintr(vidc_adev, 1);
return IRQ_HANDLED;
}
/*
* Prepare for outputting samples.
*
* Each buffer that will be passed will be `bsize' bytes long,
* with a total of `bcount' buffers.
*/
static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
{
struct audio_operations *adev = audio_devs[dev];
dma_interrupt = NULL;
adev->dmap_out->flags |= DMA_NODMA;
return 0;
}
/*
* Stop our current operation.
*/
static void vidc_audio_reset(int dev)
{
dma_interrupt = NULL;
}
static int vidc_audio_local_qlen(int dev)
{
return /*dma_count !=*/ 0;
}
static void vidc_audio_trigger(int dev, int enable_bits)
{
struct audio_operations *adev = audio_devs[dev];
if (enable_bits & PCM_ENABLE_OUTPUT) {
if (!(adev->dmap_out->flags & DMA_ACTIVE)) {
unsigned long flags;
local_irq_save(flags);
/* prevent recusion */
adev->dmap_out->flags |= DMA_ACTIVE;
dma_interrupt = vidc_audio_dma_interrupt;
vidc_sound_dma_irq(0, NULL);
iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
local_irq_restore(flags);
}
}
}
static struct audio_driver vidc_audio_driver =
{
.owner = THIS_MODULE,
.open = vidc_audio_open,
.close = vidc_audio_close,
.output_block = vidc_audio_output_block,
.start_input = vidc_audio_start_input,
.prepare_for_input = vidc_audio_prepare_for_input,
.prepare_for_output = vidc_audio_prepare_for_output,
.halt_io = vidc_audio_reset,
.local_qlen = vidc_audio_local_qlen,
.trigger = vidc_audio_trigger,
.set_speed = vidc_audio_set_speed,
.set_bits = vidc_audio_set_format,
.set_channels = vidc_audio_set_channels
};
static struct mixer_operations vidc_mixer_operations = {
.owner = THIS_MODULE,
.id = "VIDC",
.name = "VIDCsound",
.ioctl = vidc_mixer_ioctl
};
void vidc_update_filler(int format, int channels)
{
#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
switch (TYPE(format, channels)) {
default:
case TYPE(AFMT_U8, 1):
vidc_filler = vidc_fill_1x8_u;
break;
case TYPE(AFMT_U8, 2):
vidc_filler = vidc_fill_2x8_u;
break;
case TYPE(AFMT_S8, 1):
vidc_filler = vidc_fill_1x8_s;
break;
case TYPE(AFMT_S8, 2):
vidc_filler = vidc_fill_2x8_s;
break;
case TYPE(AFMT_S16_LE, 1):
vidc_filler = vidc_fill_1x16_s;
break;
case TYPE(AFMT_S16_LE, 2):
vidc_filler = vidc_fill_2x16_s;
break;
}
}
static void __init attach_vidc(struct address_info *hw_config)
{
char name[32];
int i, adev;
sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
conf_printf(name, hw_config);
memset(dma_buf, 0, sizeof(dma_buf));
adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
&vidc_audio_driver, sizeof(vidc_audio_driver),
DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
NULL, hw_config->dma, hw_config->dma2);
if (adev < 0)
goto audio_failed;
/*
* 1024 bytes => 64 buffers
*/
audio_devs[adev]->min_fragment = 10;
audio_devs[adev]->mixer_dev = num_mixers;
audio_devs[adev]->mixer_dev =
sound_install_mixer(MIXER_DRIVER_VERSION,
name, &vidc_mixer_operations,
sizeof(vidc_mixer_operations), NULL);
if (audio_devs[adev]->mixer_dev < 0)
goto mixer_failed;
for (i = 0; i < 2; i++) {
dma_buf[i] = get_zeroed_page(GFP_KERNEL);
if (!dma_buf[i]) {
printk(KERN_ERR "%s: can't allocate required buffers\n",
name);
goto mem_failed;
}
dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
}
if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma);
goto dma_failed;
}
if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
hw_config->name, &dma_start)) {
printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
goto irq_failed;
}
vidc_adev = adev;
vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
return;
irq_failed:
sound_free_dma(hw_config->dma);
dma_failed:
mem_failed:
for (i = 0; i < 2; i++)
free_page(dma_buf[i]);
sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
mixer_failed:
sound_unload_audiodev(adev);
audio_failed:
return;
}
static int __init probe_vidc(struct address_info *hw_config)
{
hw_config->irq = IRQ_DMAS0;
hw_config->dma = DMA_VIRTUAL_SOUND;
hw_config->dma2 = -1;
hw_config->card_subtype = 16;
hw_config->name = "VIDC20";
return 1;
}
static void __exit unload_vidc(struct address_info *hw_config)
{
int i, adev = vidc_adev;
vidc_adev = -1;
free_irq(hw_config->irq, &dma_start);
sound_free_dma(hw_config->dma);
if (adev >= 0) {
sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
sound_unload_audiodev(adev);
for (i = 0; i < 2; i++)
free_page(dma_buf[i]);
}
}
static struct address_info cfg;
static int __init init_vidc(void)
{
if (probe_vidc(&cfg) == 0)
return -ENODEV;
attach_vidc(&cfg);
return 0;
}
static void __exit cleanup_vidc(void)
{
unload_vidc(&cfg);
}
module_init(init_vidc);
module_exit(cleanup_vidc);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("VIDC20 audio driver");
MODULE_LICENSE("GPL");

63
sound/oss/vidc.h Normal file
View file

@ -0,0 +1,63 @@
/*
* linux/drivers/sound/vidc.h
*
* Copyright (C) 1997 Russell King <rmk@arm.linux.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* VIDC sound function prototypes
*/
/* vidc_fill.S */
/*
* Filler routines for different channels and sample sizes
*/
extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
/*
* DMA Interrupt handler
*/
extern irqreturn_t vidc_sound_dma_irq(int irqnr, void *ref);
/*
* Filler routine pointer
*/
extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend,
unsigned long obuf, int mask);
/*
* Virtual DMA buffer exhausted
*/
extern irqreturn_t (*dma_interrupt) (void);
/*
* Virtual DMA buffer addresses
*/
extern unsigned long dma_start, dma_count, dma_bufsize;
extern unsigned long dma_buf[2], dma_pbuf[2];
/* vidc_synth.c */
extern void vidc_synth_init(struct address_info *hw_config);
extern void vidc_synth_exit(struct address_info *hw_config);
extern int vidc_synth_get_volume(void);
extern int vidc_synth_set_volume(int vol);

218
sound/oss/vidc_fill.S Normal file
View file

@ -0,0 +1,218 @@
/*
* linux/drivers/sound/vidc_fill.S
*
* Copyright (C) 1997 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Filler routines for DMA buffers
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <mach/hardware.h>
#include <asm/hardware/iomd.h>
.text
ENTRY(vidc_fill_1x8_u)
mov ip, #0xff00
1: cmp r0, r1
bge vidc_clear
ldrb r4, [r0], #1
eor r4, r4, #0x80
and r4, ip, r4, lsl #8
orr r4, r4, r4, lsl #16
str r4, [r2], #4
cmp r2, r3
blt 1b
mov pc, lr
ENTRY(vidc_fill_2x8_u)
mov ip, #0xff00
1: cmp r0, r1
bge vidc_clear
ldr r4, [r0], #2
and r5, r4, ip
and r4, ip, r4, lsl #8
orr r4, r4, r5, lsl #16
orr r4, r4, r4, lsr #8
str r4, [r2], #4
cmp r2, r3
blt 1b
mov pc, lr
ENTRY(vidc_fill_1x8_s)
mov ip, #0xff00
1: cmp r0, r1
bge vidc_clear
ldrb r4, [r0], #1
and r4, ip, r4, lsl #8
orr r4, r4, r4, lsl #16
str r4, [r2], #4
cmp r2, r3
blt 1b
mov pc, lr
ENTRY(vidc_fill_2x8_s)
mov ip, #0xff00
1: cmp r0, r1
bge vidc_clear
ldr r4, [r0], #2
and r5, r4, ip
and r4, ip, r4, lsl #8
orr r4, r4, r5, lsl #16
orr r4, r4, r4, lsr #8
str r4, [r2], #4
cmp r2, r3
blt 1b
mov pc, lr
ENTRY(vidc_fill_1x16_s)
mov ip, #0xff00
orr ip, ip, ip, lsr #8
1: cmp r0, r1
bge vidc_clear
ldr r5, [r0], #2
and r4, r5, ip
orr r4, r4, r4, lsl #16
str r4, [r2], #4
cmp r0, r1
addlt r0, r0, #2
andlt r4, r5, ip, lsl #16
orrlt r4, r4, r4, lsr #16
strlt r4, [r2], #4
cmp r2, r3
blt 1b
mov pc, lr
ENTRY(vidc_fill_2x16_s)
mov ip, #0xff00
orr ip, ip, ip, lsr #8
1: cmp r0, r1
bge vidc_clear
ldr r4, [r0], #4
str r4, [r2], #4
cmp r0, r1
ldrlt r4, [r0], #4
strlt r4, [r2], #4
cmp r2, r3
blt 1b
mov pc, lr
ENTRY(vidc_fill_noaudio)
mov r0, #0
mov r1, #0
2: mov r4, #0
mov r5, #0
1: cmp r2, r3
stmltia r2!, {r0, r1, r4, r5}
blt 1b
mov pc, lr
ENTRY(vidc_clear)
mov r0, #0
mov r1, #0
tst r2, #4
str r0, [r2], #4
tst r2, #8
stmia r2!, {r0, r1}
b 2b
/*
* Call filler routines with:
* r0 = phys address
* r1 = phys end
* r2 = buffer
* Returns:
* r0 = new buffer address
* r2 = new buffer finish
* r4 = corrupted
* r5 = corrupted
* ip = corrupted
*/
ENTRY(vidc_sound_dma_irq)
stmfd sp!, {r4 - r8, lr}
ldr r8, =dma_start
ldmia r8, {r0, r1, r2, r3, r4, r5}
teq r1, #0
adreq r4, vidc_fill_noaudio
moveq r7, #1 << 31
movne r7, #0
mov ip, #IOMD_BASE & 0xff000000
orr ip, ip, #IOMD_BASE & 0x00ff0000
ldrb r6, [ip, #IOMD_SD0ST]
tst r6, #DMA_ST_OFL @ Check for overrun
eorne r6, r6, #DMA_ST_AB
tst r6, #DMA_ST_AB
moveq r2, r3 @ DMAing A, update B
add r3, r2, r5 @ End of DMA buffer
add r1, r1, r0 @ End of virtual DMA buffer
mov lr, pc
mov pc, r4 @ Call fill routine (uses r4, ip)
sub r1, r1, r0 @ Remaining length
stmia r8, {r0, r1}
mov r0, #0
tst r2, #4 @ Round buffer up to 4 words
strne r0, [r2], #4
tst r2, #8
strne r0, [r2], #4
strne r0, [r2], #4
sub r2, r2, #16
mov r2, r2, lsl #20
movs r2, r2, lsr #20
orreq r2, r2, #1 << 30 @ Set L bit
orr r2, r2, r7
ldmdb r8, {r3, r4, r5}
tst r6, #DMA_ST_AB
mov ip, #IOMD_BASE & 0xff000000
orr ip, ip, #IOMD_BASE & 0x00ff0000
streq r4, [ip, #IOMD_SD0CURB]
strne r5, [ip, #IOMD_SD0CURA]
streq r2, [ip, #IOMD_SD0ENDB]
strne r2, [ip, #IOMD_SD0ENDA]
ldr lr, [ip, #IOMD_SD0ST]
tst lr, #DMA_ST_OFL
bne 1f
tst r6, #DMA_ST_AB
strne r4, [ip, #IOMD_SD0CURB]
streq r5, [ip, #IOMD_SD0CURA]
strne r2, [ip, #IOMD_SD0ENDB]
streq r2, [ip, #IOMD_SD0ENDA]
1: teq r7, #0
mov r0, #0x10
strneb r0, [ip, #IOMD_SD0CR]
ldmfd sp!, {r4 - r8, lr}
mov r0, #1 @ IRQ_HANDLED
teq r1, #0 @ If we have no more
movne pc, lr
teq r3, #0
movne pc, r3 @ Call interrupt routine
mov pc, lr
.data
.globl dma_interrupt
dma_interrupt:
.long 0 @ r3
.globl dma_pbuf
dma_pbuf:
.long 0 @ r4
.long 0 @ r5
.globl dma_start
dma_start:
.long 0 @ r0
.globl dma_count
dma_count:
.long 0 @ r1
.globl dma_buf
dma_buf:
.long 0 @ r2
.long 0 @ r3
.globl vidc_filler
vidc_filler:
.long vidc_fill_noaudio @ r4
.globl dma_bufsize
dma_bufsize:
.long 0x1000 @ r5

2045
sound/oss/waveartist.c Normal file

File diff suppressed because it is too large Load diff

92
sound/oss/waveartist.h Normal file
View file

@ -0,0 +1,92 @@
/*
* linux/sound/oss/waveartist.h
*
* def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
*/
//registers
#define CMDR 0
#define DATR 2
#define CTLR 4
#define STATR 5
#define IRQSTAT 12
//bit defs
//reg STATR
#define CMD_WE 0x80
#define CMD_RF 0x40
#define DAT_WE 0x20
#define DAT_RF 0x10
#define IRQ_REQ 0x08
#define DMA1 0x04
#define DMA0 0x02
//bit defs
//reg CTLR
#define CMD_WEIE 0x80
#define CMD_RFIE 0x40
#define DAT_WEIE 0x20
#define DAT_RFIE 0x10
#define RESET 0x08
#define DMA1_IE 0x04
#define DMA0_IE 0x02
#define IRQ_ACK 0x01
//commands
#define WACMD_SYSTEMID 0x00
#define WACMD_GETREV 0x00
#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U
#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo
#define WACMD_INPUTSPEED 0x12 //sampling rate
#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO
#define WACMD_INPUTSIZE 0x14 //samples to interrupt
#define WACMD_INPUTSTART 0x15 //start ADC
#define WACMD_INPUTPAUSE 0x16 //pause ADC
#define WACMD_INPUTSTOP 0x17 //stop ADC
#define WACMD_INPUTRESUME 0x18 //resume ADC
#define WACMD_INPUTPIO 0x19 //PIO ADC
#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U
#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo
#define WACMD_OUTPUTSPEED 0x22 //sampling rate
#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO
#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt
#define WACMD_OUTPUTSTART 0x25 //start ADC
#define WACMD_OUTPUTPAUSE 0x26 //pause ADC
#define WACMD_OUTPUTSTOP 0x27 //stop ADC
#define WACMD_OUTPUTRESUME 0x28 //resume ADC
#define WACMD_OUTPUTPIO 0x29 //PIO ADC
#define WACMD_GET_LEVEL 0x30
#define WACMD_SET_LEVEL 0x31
#define WACMD_SET_MIXER 0x32
#define WACMD_RST_MIXER 0x33
#define WACMD_SET_MONO 0x34
/*
* Definitions for left/right recording input mux
*/
#define ADC_MUX_NONE 0
#define ADC_MUX_MIXER 1
#define ADC_MUX_LINE 2
#define ADC_MUX_AUX2 3
#define ADC_MUX_AUX1 4
#define ADC_MUX_MIC 5
/*
* Definitions for mixer gain settings
*/
#define MIX_GAIN_LINE 0 /* line in */
#define MIX_GAIN_AUX1 1 /* aux1 */
#define MIX_GAIN_AUX2 2 /* aux2 */
#define MIX_GAIN_XMIC 3 /* crossover mic */
#define MIX_GAIN_MIC 4 /* normal mic */
#define MIX_GAIN_PREMIC 5 /* preamp mic */
#define MIX_GAIN_OUT 6 /* output */
#define MIX_GAIN_MONO 7 /* mono in */
int wa_sendcmd(unsigned int cmd);
int wa_writecmd(unsigned int cmd, unsigned int arg);