mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 07:18:51 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
369
sound/oss/CHANGELOG
Normal file
369
sound/oss/CHANGELOG
Normal 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
533
sound/oss/Kconfig
Normal 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
107
sound/oss/Makefile
Normal 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
6
sound/oss/README.FIRST
Normal 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
3065
sound/oss/ad1848.c
Normal file
File diff suppressed because it is too large
Load diff
24
sound/oss/ad1848.h
Normal file
24
sound/oss/ad1848.h
Normal 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
253
sound/oss/ad1848_mixer.h
Normal 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
1373
sound/oss/aedsp16.c
Normal file
File diff suppressed because it is too large
Load diff
985
sound/oss/audio.c
Normal file
985
sound/oss/audio.c
Normal 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
39
sound/oss/bin2hex.c
Normal 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
12
sound/oss/coproc.h
Normal 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
256
sound/oss/dev_table.c
Normal 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
390
sound/oss/dev_table.h
Normal 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
1266
sound/oss/dmabuf.c
Normal file
File diff suppressed because it is too large
Load diff
45
sound/oss/dmasound/Kconfig
Normal file
45
sound/oss/dmasound/Kconfig
Normal 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
|
||||
7
sound/oss/dmasound/Makefile
Normal file
7
sound/oss/dmasound/Makefile
Normal 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
|
||||
261
sound/oss/dmasound/dmasound.h
Normal file
261
sound/oss/dmasound/dmasound.h
Normal 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_ */
|
||||
1620
sound/oss/dmasound/dmasound_atari.c
Normal file
1620
sound/oss/dmasound/dmasound_atari.c
Normal file
File diff suppressed because it is too large
Load diff
1599
sound/oss/dmasound/dmasound_core.c
Normal file
1599
sound/oss/dmasound/dmasound_core.c
Normal file
File diff suppressed because it is too large
Load diff
739
sound/oss/dmasound/dmasound_paula.c
Normal file
739
sound/oss/dmasound/dmasound_paula.c
Normal 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");
|
||||
638
sound/oss/dmasound/dmasound_q40.c
Normal file
638
sound/oss/dmasound/dmasound_q40.c
Normal 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
101
sound/oss/hex2hex.c
Normal 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
229
sound/oss/kahlua.c
Normal 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
22
sound/oss/midi_ctrl.h
Normal 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
712
sound/oss/midi_synth.c
Normal 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
47
sound/oss/midi_synth.h
Normal 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
425
sound/oss/midibuf.c
Normal 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
1804
sound/oss/mpu401.c
Normal file
File diff suppressed because it is too large
Load diff
11
sound/oss/mpu401.h
Normal file
11
sound/oss/mpu401.h
Normal 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
413
sound/oss/msnd.c
Normal 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
278
sound/oss/msnd.h
Normal 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
3
sound/oss/msnd_classic.c
Normal 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
185
sound/oss/msnd_classic.h
Normal 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
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
246
sound/oss/msnd_pinnacle.h
Normal 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
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
246
sound/oss/opl3_hw.h
Normal 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
45
sound/oss/os.h
Normal 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
20
sound/oss/pas2.h
Normal 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
458
sound/oss/pas2_card.c
Normal 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
262
sound/oss/pas2_midi.c
Normal 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
327
sound/oss/pas2_mixer.c
Normal 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
419
sound/oss/pas2_pcm.c
Normal 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
1270
sound/oss/pss.c
Normal file
File diff suppressed because it is too large
Load diff
185
sound/oss/sb.h
Normal file
185
sound/oss/sb.h
Normal 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
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
354
sound/oss/sb_card.c
Normal 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
149
sound/oss/sb_card.h
Normal 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
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
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
34
sound/oss/sb_ess.h
Normal 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
206
sound/oss/sb_midi.c
Normal 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
770
sound/oss/sb_mixer.c
Normal 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
105
sound/oss/sb_mixer.h
Normal 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
1669
sound/oss/sequencer.c
Normal file
File diff suppressed because it is too large
Load diff
18
sound/oss/sleep.h
Normal file
18
sound/oss/sleep.h
Normal 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
87
sound/oss/sound_calls.h
Normal 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
143
sound/oss/sound_config.h
Normal 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
|
||||
2
sound/oss/sound_firmware.h
Normal file
2
sound/oss/sound_firmware.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
extern int mod_firmware_load(const char *fn, char **fp);
|
||||
|
||||
327
sound/oss/sound_timer.c
Normal file
327
sound/oss/sound_timer.c
Normal 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
733
sound/oss/soundcard.c
Normal 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
2
sound/oss/soundvers.h
Normal 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
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
285
sound/oss/sys_timer.c
Normal 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
525
sound/oss/trix.c
Normal 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
23
sound/oss/tuning.h
Normal 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
480
sound/oss/uart401.c
Normal 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
361
sound/oss/uart6850.c
Normal 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
69
sound/oss/ulaw.h
Normal 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
290
sound/oss/v_midi.c
Normal 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
14
sound/oss/v_midi.h
Normal 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
557
sound/oss/vidc.c
Normal 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
63
sound/oss/vidc.h
Normal 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
218
sound/oss/vidc_fill.S
Normal 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
2045
sound/oss/waveartist.c
Normal file
File diff suppressed because it is too large
Load diff
92
sound/oss/waveartist.h
Normal file
92
sound/oss/waveartist.h
Normal 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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue